Android(安卓)P WiFi自动连接评分机制
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
关注公众号,获取更多开发必备知识
更多相关文章
- OpenWrt上运行Android(安卓)OS
- windows下android 开发环境建立
- Android搜索视媒体库视频 列表显示选择
- Android下载网络图片到本地
- Android(安卓)环境搭建
- android实现猜扑克牌小游戏(改进:每次只可以选择一张)
- android中获取网络图片
- android ping 网络延迟
- android设备信息获取