1、WifiConnectivityManager的初始化
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java
private void setupClientMode() {
Log.d(TAG, "setupClientMode() ifacename = " + mInterfaceName);
mWifiStateTracker.updateState(WifiStateTracker.INVALID);
if (mWifiConnectivityManager == null) {
synchronized (mWifiReqCountLock) {
mWifiConnectivityManager =
mWifiInjector.makeWifiConnectivityManager(mWifiInfo,
hasConnectionRequests());
mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
}
}
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java
public WifiConnectivityManager makeWifiConnectivityManager(WifiInfo wifiInfo,
boolean hasConnectionRequests) {
return new WifiConnectivityManager(mContext, getScoringParams(),
mWifiStateMachine, getWifiScanner(),
mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
mWifiLastResortWatchdog, mOpenNetworkNotifier, mCarrierNetworkNotifier,
mCarrierNetworkConfig, mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(),
mClock, mConnectivityLocalLog, hasConnectionRequests, mFrameworkFacade,
mSavedNetworkEvaluator, mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
}
构造方法里注册了3个NetworkEvaluator
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
WifiConnectivityManager(Context context, ScoringParams scoringParams,
WifiStateMachine stateMachine,
WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
// Register the network evaluators
mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator,
SAVED_NETWORK_EVALUATOR_PRIORITY);
if (hs2Enabled) {
mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator,
PASSPOINT_NETWORK_EVALUATOR_PRIORITY);
}
mNetworkSelector.registerNetworkEvaluator(scoredNetworkEvaluator,
SCORED_NETWORK_EVALUATOR_PRIORITY);
}
注册方法就是初始化一个NetworkEvaluator数组,大小为6,即优先级从高到低0-5。
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
public boolean registerNetworkEvaluator(NetworkEvaluator evaluator, int priority) {
if (priority < 0 || priority >= EVALUATOR_MIN_PRIORITY) {
localLog("Invalid network evaluator priority: " + priority);
return false;
}
if (mEvaluators[priority] != null) {
localLog("Priority " + priority + " is already registered by "
+ mEvaluators[priority].getName());
return false;
}
mEvaluators[priority] = evaluator;
return true;
}
private final NetworkEvaluator[] mEvaluators = new NetworkEvaluator[MAX_NUM_EVALUATORS];
public static final int MAX_NUM_EVALUATORS = EVALUATOR_MIN_PRIORITY;
/**
* WiFi Network Selector supports various types of networks. Each type can
* have its evaluator to choose the best WiFi network for the device to connect
* to. When registering a WiFi network evaluator with the WiFi Network Selector,
* the priority of the network must be specified, and it must be a value between
* 0 and (EVALUATOR_MIN_PIRORITY - 1) with 0 being the highest priority. Wifi
* Network Selector iterates through the registered scorers from the highest priority
* to the lowest till a network is selected.
*/
public static final int EVALUATOR_MIN_PRIORITY = 6;
2、WifiConnectivityManager的网络评估
处理扫描结果
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
// Check if any blacklisted BSSIDs can be freed.
refreshBssidBlacklist();
if (mStateMachine.isSupplicantTransientState()) {
localLog(listenerName
+ " onResults: No network selection because supplicantTransientState is "
+ mStateMachine.isSupplicantTransientState());
return false;
}
localLog(listenerName + " onResults: start network selection");
WifiConfiguration candidate =
mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo,
mStateMachine.isConnected(), mStateMachine.isDisconnected(),
mUntrustedConnectionAllowed);
mWifiLastResortWatchdog.updateAvailableNetworks(
mNetworkSelector.getConnectableScanDetails());
mWifiMetrics.countScanResults(scanDetails);
if (candidate != null) {
localLog(listenerName + ": WNS candidate-" + candidate.SSID);
connectToNetwork(candidate);
return true;
} else {
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mOpenNetworkNotifier.handleScanResults(
mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
if (mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) {
mCarrierNetworkNotifier.handleScanResults(
mNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks(
mCarrierNetworkConfig));
}
}
return false;
}
}
如注释所说:处理周期性,单次和Pno ScanListener的“ onResult”回调。 执行潜在网络候选者的选择,启动与该网络的连接尝试。
然后看candidate是如何生成的:
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java
public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails,
HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
boolean connected, boolean disconnected, boolean untrustedNetworkAllowed) {
mFilteredNetworks.clear();
mConnectableNetworks.clear();
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan result");
return null;
}
WifiConfiguration currentNetwork =
mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
// Always get the current BSSID from WifiInfo in case that firmware initiated
// roaming happened.
String currentBssid = wifiInfo.getBSSID();
// Shall we start network selection at all?
if (!isNetworkSelectionNeeded(scanDetails, wifiInfo, connected, disconnected)) {
return null;
}
// Update the registered network evaluators.
for (NetworkEvaluator registeredEvaluator : mEvaluators) {
if (registeredEvaluator != null) {
registeredEvaluator.update(scanDetails);
}
}
// Filter out unwanted networks.
mFilteredNetworks = filterScanResults(scanDetails, bssidBlacklist,
connected, currentBssid);
if (mFilteredNetworks.size() == 0) {
return null;
}
// Go through the registered network evaluators from the highest priority
// one to the lowest till a network is selected.
WifiConfiguration selectedNetwork = null;
for (NetworkEvaluator registeredEvaluator : mEvaluators) {
if (registeredEvaluator != null) {
localLog("About to run " + registeredEvaluator.getName() + " :");
selectedNetwork = registeredEvaluator.evaluateNetworks(
new ArrayList<>(mFilteredNetworks), currentNetwork, currentBssid, connected,
untrustedNetworkAllowed, mConnectableNetworks);
if (selectedNetwork != null) {
localLog(registeredEvaluator.getName() + " selects "
+ WifiNetworkSelector.toNetworkString(selectedNetwork) + " : "
+ selectedNetwork.getNetworkSelectionStatus().getCandidate().BSSID);
break;
}
}
}
if (selectedNetwork != null) {
selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
}
return selectedNetwork;
}
2.1、判断是否需要网络选择
private boolean isNetworkSelectionNeeded(List<ScanDetail> scanDetails, WifiInfo wifiInfo,
boolean connected, boolean disconnected) {
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan results. Skip network selection.");
return false;
}
if (connected) {
// Is roaming allowed?
if (!mEnableAutoJoinWhenAssociated) {
localLog("Switching networks in connected state is not allowed."
+ " Skip network selection.");
return false;
}
// Has it been at least the minimum interval since last network selection?
if (mLastNetworkSelectionTimeStamp != INVALID_TIME_STAMP) {
long gap = mClock.getElapsedSinceBootMillis()
- mLastNetworkSelectionTimeStamp;
if (gap < MINIMUM_NETWORK_SELECTION_INTERVAL_MS) {
localLog("Too short since last network selection: " + gap + " ms."
+ " Skip network selection.");
return false;
}
}
if (isCurrentNetworkSufficient(wifiInfo, scanDetails)) {
localLog("Current connected network already sufficient. Skip network selection.");
return false;
} else {
localLog("Current connected network is not sufficient.");
return true;
}
} else if (disconnected) {
return true;
} else {
// No network selection if WifiStateMachine is in a state other than
// CONNECTED or DISCONNECTED.
localLog("WifiStateMachine is in neither CONNECTED nor DISCONNECTED state."
+ " Skip network selection.");
return false;
}
}
2.2、SavedNetworkEvaluator的筛选
根据扫描结果评估所有网络,并返回选择用于连接的网络的WifiConfiguration
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
/**
* 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.
*/
public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
WifiConfiguration currentNetwork, String currentBssid, boolean connected,
boolean untrustedNetworkAllowed,
List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
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 networks, hence we calculate all
// the scores and use the highest one as the ScanResult's score.
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();
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)
&& !mWifiConfigManager.isSimPresent()) {
// 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.
// TODO(b/36067705): consider alternative designs to push filtering/selecting of
// user connect choice networks to RecommendedNetworkEvaluator.
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;
}
if (connectableNetworks != null) {
connectableNetworks.add(Pair.create(scanDetail,
mWifiConfigManager.getConfiguredNetwork(network.networkId)));
}
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;
}
忽略Passpoint和临时网络。
/**
* 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;
}
判断当前网络是否允许加入网络选择,如果不允许,则过滤掉
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;
如果该网络被标记为使用外部得分,或者该网络是启用了保存已保存的开放网络的开放网络,则不要将其视为网络选择
if (network.useExternalScores) {
localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+ " has external score.");
continue;
}
2.3、评分机制
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
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 = scanResult.level < rssiSaturationThreshold ? 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 bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60);
score += bonus > 0 ? bonus : 0;
sbuf.append(" User selection ").append(timeDifference / 1000 / 60)
.append(" minutes ago, bonus: ").append(bonus).append(",");
}
}
// Same network award.
if (currentNetwork != null
&& (network.networkId == currentNetwork.networkId
//TODO(b/36788683): re-enable linked configuration check
/* || network.isLinked(currentNetwork) */)) {
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;
}
2.3.1、RSSI
int rssiSaturationThreshold = mScoringParams.getGoodRssi(scanResult.frequency);
int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level
: rssiSaturationThreshold;
score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
2.3.2、5G
如果频率是5G,会加分
if (is5GHz) {
score += mBand5GHzAward;
sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
}
2.3.3、Last user selection award
最后一个使用的网络会加分
// 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 bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60);
score += bonus > 0 ? bonus : 0;
sbuf.append(" User selection ").append(timeDifference / 1000 / 60)
.append(" minutes ago, bonus: ").append(bonus).append(",");
}
}
2.3.4、Same network award
相同的networkId会加分,相同的networkId且支持漫游也会加分
// Same network award.
if (currentNetwork != null
&& (network.networkId == currentNetwork.networkId
//TODO(b/36788683): re-enable linked configuration check
/* || network.isLinked(currentNetwork) */)) {
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(",");
}
}
2.3.5、Same BSSID award
相同的BSSID会加分
// Same BSSID award.
if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
score += mSameBssidAward;
sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
}
2.3.6、Security award
不是开放的网络会加分
// Security award.
if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
score += mSecurityAward;
sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
}
3、总结
SavedNetworkEvaluator评分几大要素:
rssi
5G
Last user selection award
Same network award 支持漫游
Same BSSID award
Security award
关注公众号,获取更多开发必备知识