/*
|
* Copyright (C) 2016 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.wifi;
|
|
import android.annotation.NonNull;
|
import android.content.Context;
|
import android.net.wifi.ScanResult;
|
import android.net.wifi.WifiConfiguration;
|
import android.telephony.SubscriptionManager;
|
import android.util.LocalLog;
|
|
import com.android.internal.R;
|
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.server.wifi.util.TelephonyUtil;
|
|
import java.util.List;
|
|
/**
|
* This class is the WifiNetworkSelector.NetworkEvaluator implementation for
|
* saved networks.
|
*/
|
public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
|
private static final String NAME = "SavedNetworkEvaluator";
|
private final WifiConfigManager mWifiConfigManager;
|
private final Clock mClock;
|
private final LocalLog mLocalLog;
|
private final WifiConnectivityHelper mConnectivityHelper;
|
private final SubscriptionManager mSubscriptionManager;
|
private final int mRssiScoreSlope;
|
private final int mRssiScoreOffset;
|
private final int mSameBssidAward;
|
private final int mSameNetworkAward;
|
private final int mBand5GHzAward;
|
private final int mLastSelectionAward;
|
private final int mSecurityAward;
|
private final ScoringParams mScoringParams;
|
|
/**
|
* Time it takes for the mLastSelectionAward to decay by one point, in milliseconds
|
*/
|
@VisibleForTesting
|
public static final int LAST_SELECTION_AWARD_DECAY_MSEC = 60 * 1000;
|
|
|
SavedNetworkEvaluator(final Context context, ScoringParams scoringParams,
|
WifiConfigManager configManager, Clock clock,
|
LocalLog localLog, WifiConnectivityHelper connectivityHelper,
|
SubscriptionManager subscriptionManager) {
|
mScoringParams = scoringParams;
|
mWifiConfigManager = configManager;
|
mClock = clock;
|
mLocalLog = localLog;
|
mConnectivityHelper = connectivityHelper;
|
mSubscriptionManager = subscriptionManager;
|
|
mRssiScoreSlope = context.getResources().getInteger(
|
R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
|
mRssiScoreOffset = context.getResources().getInteger(
|
R.integer.config_wifi_framework_RSSI_SCORE_OFFSET);
|
mSameBssidAward = context.getResources().getInteger(
|
R.integer.config_wifi_framework_SAME_BSSID_AWARD);
|
mSameNetworkAward = context.getResources().getInteger(
|
R.integer.config_wifi_framework_current_network_boost);
|
mLastSelectionAward = context.getResources().getInteger(
|
R.integer.config_wifi_framework_LAST_SELECTION_AWARD);
|
mSecurityAward = context.getResources().getInteger(
|
R.integer.config_wifi_framework_SECURITY_AWARD);
|
mBand5GHzAward = context.getResources().getInteger(
|
R.integer.config_wifi_framework_5GHz_preference_boost_factor);
|
}
|
|
private void localLog(String log) {
|
mLocalLog.log(log);
|
}
|
|
/**
|
* Get the evaluator type.
|
*/
|
@Override
|
public @EvaluatorId int getId() {
|
return EVALUATOR_ID_SAVED;
|
}
|
|
/**
|
* Get the evaluator name.
|
*/
|
@Override
|
public String getName() {
|
return NAME;
|
}
|
|
/**
|
* Update the evaluator.
|
*/
|
@Override
|
public void update(List<ScanDetail> scanDetails) { }
|
|
private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network,
|
WifiConfiguration currentNetwork, String currentBssid,
|
StringBuffer sbuf) {
|
int score = 0;
|
boolean is5GHz = scanResult.is5GHz();
|
|
sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID)
|
.append(" RSSI:").append(scanResult.level).append(" ] ");
|
// Calculate the RSSI score.
|
int rssiSaturationThreshold = mScoringParams.getGoodRssi(scanResult.frequency);
|
int rssi = Math.min(scanResult.level, rssiSaturationThreshold);
|
score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
|
sbuf.append(" RSSI score: ").append(score).append(",");
|
|
// 5GHz band bonus.
|
if (is5GHz) {
|
score += mBand5GHzAward;
|
sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
|
}
|
|
// Last user selection award.
|
int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork();
|
if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID
|
&& lastUserSelectedNetworkId == network.networkId) {
|
long timeDifference = mClock.getElapsedSinceBootMillis()
|
- mWifiConfigManager.getLastSelectedTimeStamp();
|
if (timeDifference > 0) {
|
int decay = (int) (timeDifference / LAST_SELECTION_AWARD_DECAY_MSEC);
|
int bonus = Math.max(mLastSelectionAward - decay, 0);
|
score += bonus;
|
sbuf.append(" User selection ").append(timeDifference)
|
.append(" ms ago, bonus: ").append(bonus).append(",");
|
}
|
}
|
|
// Same network award.
|
if (currentNetwork != null && network.networkId == currentNetwork.networkId) {
|
score += mSameNetworkAward;
|
sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(",");
|
|
// When firmware roaming is supported, equivalent BSSIDs (the ones under the
|
// same network as the currently connected one) get the same BSSID award.
|
if (mConnectivityHelper.isFirmwareRoamingSupported()
|
&& currentBssid != null && !currentBssid.equals(scanResult.BSSID)) {
|
score += mSameBssidAward;
|
sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(",");
|
}
|
}
|
|
// Same BSSID award.
|
if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
|
score += mSameBssidAward;
|
sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
|
}
|
|
// Security award.
|
if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
|
score += mSecurityAward;
|
sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
|
}
|
|
sbuf.append(" ## Total score: ").append(score).append("\n");
|
|
return score;
|
}
|
|
/**
|
* Evaluate all the networks from the scan results and return
|
* the WifiConfiguration of the network chosen for connection.
|
*
|
* @return configuration of the chosen network;
|
* null if no network in this category is available.
|
*/
|
@Override
|
public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
|
WifiConfiguration currentNetwork, String currentBssid, boolean connected,
|
boolean untrustedNetworkAllowed,
|
@NonNull OnConnectableListener onConnectableListener) {
|
int highestScore = Integer.MIN_VALUE;
|
ScanResult scanResultCandidate = null;
|
WifiConfiguration candidate = null;
|
StringBuffer scoreHistory = new StringBuffer();
|
|
for (ScanDetail scanDetail : scanDetails) {
|
ScanResult scanResult = scanDetail.getScanResult();
|
|
// One ScanResult can be associated with more than one network, hence we calculate all
|
// the scores and use the highest one as the ScanResult's score.
|
// TODO(b/112196799): this has side effects, rather not do that in an evaluator
|
WifiConfiguration network =
|
mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
|
|
if (network == null) {
|
continue;
|
}
|
|
/**
|
* Ignore Passpoint and Ephemeral networks. They are configured networks,
|
* but without being persisted to the storage. They are evaluated by
|
* {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
|
* respectively.
|
*/
|
if (network.isPasspoint() || network.isEphemeral()) {
|
continue;
|
}
|
|
WifiConfiguration.NetworkSelectionStatus status =
|
network.getNetworkSelectionStatus();
|
// TODO (b/112196799): another side effect
|
status.setSeenInLastQualifiedNetworkSelection(true);
|
|
if (!status.isNetworkEnabled()) {
|
continue;
|
} else if (network.BSSID != null && !network.BSSID.equals("any")
|
&& !network.BSSID.equals(scanResult.BSSID)) {
|
// App has specified the only BSSID to connect for this
|
// configuration. So only the matching ScanResult can be a candidate.
|
localLog("Network " + WifiNetworkSelector.toNetworkString(network)
|
+ " has specified BSSID " + network.BSSID + ". Skip "
|
+ scanResult.BSSID);
|
continue;
|
} else if (TelephonyUtil.isSimConfig(network)
|
&& !TelephonyUtil.isSimPresent(mSubscriptionManager)) {
|
// Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
|
continue;
|
}
|
|
int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
|
scoreHistory);
|
|
// Set candidate ScanResult for all saved networks to ensure that users can
|
// override network selection. See WifiNetworkSelector#setUserConnectChoice.
|
if (score > status.getCandidateScore()
|
|| (score == status.getCandidateScore()
|
&& status.getCandidate() != null
|
&& scanResult.level > status.getCandidate().level)) {
|
mWifiConfigManager.setNetworkCandidateScanResult(
|
network.networkId, scanResult, score);
|
}
|
|
// If the network is marked to use external scores, or is an open network with
|
// curate saved open networks enabled, do not consider it for network selection.
|
if (network.useExternalScores) {
|
localLog("Network " + WifiNetworkSelector.toNetworkString(network)
|
+ " has external score.");
|
continue;
|
}
|
|
onConnectableListener.onConnectable(scanDetail,
|
mWifiConfigManager.getConfiguredNetwork(network.networkId), score);
|
|
// TODO(b/112196799) - pull into common code
|
if (score > highestScore
|
|| (score == highestScore
|
&& scanResultCandidate != null
|
&& scanResult.level > scanResultCandidate.level)) {
|
highestScore = score;
|
scanResultCandidate = scanResult;
|
mWifiConfigManager.setNetworkCandidateScanResult(
|
network.networkId, scanResultCandidate, highestScore);
|
// Reload the network config with the updated info.
|
candidate = mWifiConfigManager.getConfiguredNetwork(network.networkId);
|
}
|
}
|
|
if (scoreHistory.length() > 0) {
|
localLog("\n" + scoreHistory.toString());
|
}
|
|
if (scanResultCandidate == null) {
|
localLog("did not see any good candidates.");
|
}
|
return candidate;
|
}
|
}
|