(一百三十三)Android O WiFi重启机制学习——重启原因

2023-12-19 07:38

本文主要是介绍(一百三十三)Android O WiFi重启机制学习——重启原因,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:在之前一篇博客中(一百三十二)Android O WiFi重启机制学习——重启流程 提及

“api中简要说明了这个类是用来将WiFi从异常状态恢复的。恢复机制是通过WifiController触发一个重启,基本模拟了一次飞行模式切换。

当前触发条件

看门狗
正常操作过程中的HAL/wificond crashes
TBD:正常操作过程中的supplicant crashes(暂未有对应处理)”

继续看下重启原因

 

 

1.重启原因

从截图来看触发重启主要的3个地方也已经很明显的搜索到了,分别为watchdog、wificond_crash和hal_crash

 

2.重启原因流程探究

分别看下watchdog、wificond_crash和hal_crash

2.1 watchdog

    /*** Increments the failure reason count for the given bssid. Performs a check to see if we have* exceeded a failure threshold for all available networks, and executes the last resort restart* @param bssid of the network that has failed connection, can be "any"* @param reason Message id from WifiStateMachine for this failure* @return true if watchdog triggers, returned for test visibility*/public boolean noteConnectionFailureAndTriggerIfNeeded(String ssid, String bssid, int reason) {if (mVerboseLoggingEnabled) {Log.v(TAG, "noteConnectionFailureAndTriggerIfNeeded: [" + ssid + ", " + bssid + ", "+ reason + "]");}// Update failure count for the failing networkupdateFailureCountForNetwork(ssid, bssid, reason);// Have we met conditions to trigger the Watchdog Wifi restart?boolean isRestartNeeded = checkTriggerCondition();if (mVerboseLoggingEnabled) {Log.v(TAG, "isRestartNeeded = " + isRestartNeeded);}if (isRestartNeeded) {// Stop the watchdog from triggering until re-enabledsetWatchdogTriggerEnabled(false);Log.e(TAG, "Watchdog triggering recovery");mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);// increment various watchdog trigger count statsincrementWifiMetricsTriggerCounts();clearAllFailureCounts();}return isRestartNeeded;}

看了下这个方法只有WifiStateMachine会调用,分别是FAILURE_CODE_AUTHENTICATION、FAILURE_CODE_ASSOCIATION和FAILURE_CODE_DHCP

 

2.1.1 FAILURE_CODE_AUTHENTICATION

                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_AUTH_FAILURE);mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);int disableReason = WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE;// Check if this is a permanent wrong password failure.if (isPermanentWrongPasswordFailure(mTargetNetworkId, message.arg2)) {disableReason = WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD;WifiConfiguration targetedNetwork =mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);if (targetedNetwork != null) {mWrongPasswordNotifier.onWrongPasswordError(targetedNetwork.SSID);}}mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId, disableReason);mWifiConfigManager.clearRecentFailureReason(mTargetNetworkId);//If failure occurred while Metrics is tracking a ConnnectionEvent, end it.reportConnectionAttemptEnd(WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,WifiMetricsProto.ConnectionEvent.HLF_NONE);mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), mTargetRoamBSSID,WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);break;

2.1.2 FAILURE_CODE_ASSOCIATION

                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_ASSOC_FAILURE);didBlackListBSSID = false;bssid = (String) message.obj;timedOut = message.arg1 > 0;reasonCode = message.arg2;Log.d(TAG, "Assocation Rejection event: bssid=" + bssid + " reason code="+ reasonCode + " timedOut=" + Boolean.toString(timedOut));if (bssid == null || TextUtils.isEmpty(bssid)) {// If BSSID is null, use the target roam BSSIDbssid = mTargetRoamBSSID;}if (bssid != null) {// If we have a BSSID, tell configStore to black list itdidBlackListBSSID = mWifiConnectivityManager.trackBssid(bssid, false,reasonCode);}mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId,WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION);mWifiConfigManager.setRecentFailureAssociationStatus(mTargetNetworkId,reasonCode);mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);// If rejection occurred while Metrics is tracking a ConnnectionEvent, end it.reportConnectionAttemptEnd(WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,WifiMetricsProto.ConnectionEvent.HLF_NONE);mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), bssid,WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);break;

2.1.3 FAILURE_CODE_DHCP

        @Overridepublic void onNewDhcpResults(DhcpResults dhcpResults) {if (dhcpResults != null) {sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);} else {sendMessage(CMD_IPV4_PROVISIONING_FAILURE);mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), mTargetRoamBSSID,WifiLastResortWatchdog.FAILURE_CODE_DHCP);}

2.1.4 流程探究

再回头看下WifiLastResortWatchdog的逻辑

    /*** Increments the failure reason count for the given network, in 'mSsidFailureCount'* Failures are counted per SSID, either; by using the ssid string when the bssid is "any"* or by looking up the ssid attached to a specific bssid* An unused set of counts is also kept which is bssid specific, in 'mRecentAvailableNetworks'* @param ssid of the network that has failed connection* @param bssid of the network that has failed connection, can be "any"* @param reason Message id from WifiStateMachine for this failure*/private void updateFailureCountForNetwork(String ssid, String bssid, int reason) {if (mVerboseLoggingEnabled) {Log.v(TAG, "updateFailureCountForNetwork: [" + ssid + ", " + bssid + ", "+ reason + "]");}if (BSSID_ANY.equals(bssid)) {incrementSsidFailureCount(ssid, reason);} else {// Bssid count is actually unused except for logging purposes// SSID count is incremented within the BSSID counting methodincrementBssidFailureCount(ssid, bssid, reason);}}/*** Map of SSID to <FailureCount, AP count>, used to count failures & number of access points* belonging to an SSID.*/private Map<String, Pair<AvailableNetworkFailureCount, Integer>> mSsidFailureCount =new HashMap<>();/*** Update the per-SSID failure count* @param ssid the ssid to increment failure count for* @param reason the failure type to increment count for*/private void incrementSsidFailureCount(String ssid, int reason) {Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid);if (ssidFails == null) {Log.d(TAG, "updateFailureCountForNetwork: No networks for ssid = " + ssid);return;}AvailableNetworkFailureCount failureCount = ssidFails.first;failureCount.incrementFailureCount(reason);}/*** Update the per-BSSID failure count* @param bssid the bssid to increment failure count for* @param reason the failure type to increment count for*/private void incrementBssidFailureCount(String ssid, String bssid, int reason) {AvailableNetworkFailureCount availableNetworkFailureCount =mRecentAvailableNetworks.get(bssid);if (availableNetworkFailureCount == null) {Log.d(TAG, "updateFailureCountForNetwork: Unable to find Network [" + ssid+ ", " + bssid + "]");return;}if (!availableNetworkFailureCount.ssid.equals(ssid)) {Log.d(TAG, "updateFailureCountForNetwork: Failed connection attempt has"+ " wrong ssid. Failed [" + ssid + ", " + bssid + "], buffered ["+ availableNetworkFailureCount.ssid + ", " + bssid + "]");return;}if (availableNetworkFailureCount.config == null) {if (mVerboseLoggingEnabled) {Log.v(TAG, "updateFailureCountForNetwork: network has no config ["+ ssid + ", " + bssid + "]");}}availableNetworkFailureCount.incrementFailureCount(reason);incrementSsidFailureCount(ssid, reason);}

根据bssid是否是any进行两种失败次数的统计,分别是incrementSsidFailureCount和incrementBssidFailureCount

分别看下这两个方法

1. incrementSsidFailureCount

先看下初始化和添加

    /*** Map of SSID to <FailureCount, AP count>, used to count failures & number of access points* belonging to an SSID.*/private Map<String, Pair<AvailableNetworkFailureCount, Integer>> mSsidFailureCount =new HashMap<>();/*** Refreshes recentAvailableNetworks with the latest available networks* Adds new networks, removes old ones that have timed out. Should be called after Wifi* framework decides what networks it is potentially connecting to.* @param availableNetworks ScanDetail & Config list of potential connection* candidates*/public void updateAvailableNetworks(List<Pair<ScanDetail, WifiConfiguration>> availableNetworks) {if (mVerboseLoggingEnabled) {Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size());}// Add new networks to mRecentAvailableNetworksif (availableNetworks != null) {for (Pair<ScanDetail, WifiConfiguration> pair : availableNetworks) {final ScanDetail scanDetail = pair.first;final WifiConfiguration config = pair.second;ScanResult scanResult = scanDetail.getScanResult();if (scanResult == null) continue;String bssid = scanResult.BSSID;String ssid = "\"" + scanDetail.getSSID() + "\"";if (mVerboseLoggingEnabled) {Log.v(TAG, " " + bssid + ": " + scanDetail.getSSID());}// Cache the scanResult & WifiConfigAvailableNetworkFailureCount availableNetworkFailureCount =mRecentAvailableNetworks.get(bssid);if (availableNetworkFailureCount == null) {// New network is availableavailableNetworkFailureCount = new AvailableNetworkFailureCount(config);availableNetworkFailureCount.ssid = ssid;// Count AP for this SSIDPair<AvailableNetworkFailureCount, Integer> ssidFailsAndApCount =mSsidFailureCount.get(ssid);if (ssidFailsAndApCount == null) {// This is a new SSID, create new FailureCount for it and set AP count to 1ssidFailsAndApCount = Pair.create(new AvailableNetworkFailureCount(config),1);setWatchdogTriggerEnabled(true);} else {final Integer numberOfAps = ssidFailsAndApCount.second;// This is not a new SSID, increment the AP count for itssidFailsAndApCount = Pair.create(ssidFailsAndApCount.first,numberOfAps + 1);}mSsidFailureCount.put(ssid, ssidFailsAndApCount);}// refresh config if it is not nullif (config != null) {availableNetworkFailureCount.config = config;}// If we saw a network, set its Age to -1 here, aging iteration will set it to 0availableNetworkFailureCount.age = -1;mRecentAvailableNetworks.put(bssid, availableNetworkFailureCount);}}// Iterate through available networks updating timeout counts & removing networks.Iterator<Map.Entry<String, AvailableNetworkFailureCount>> it =mRecentAvailableNetworks.entrySet().iterator();while (it.hasNext()) {Map.Entry<String, AvailableNetworkFailureCount> entry = it.next();if (entry.getValue().age < MAX_BSSID_AGE - 1) {entry.getValue().age++;} else {// Decrement this SSID : AP countString ssid = entry.getValue().ssid;Pair<AvailableNetworkFailureCount, Integer> ssidFails =mSsidFailureCount.get(ssid);if (ssidFails != null) {Integer apCount = ssidFails.second - 1;if (apCount > 0) {ssidFails = Pair.create(ssidFails.first, apCount);mSsidFailureCount.put(ssid, ssidFails);} else {mSsidFailureCount.remove(ssid);}} else {Log.d(TAG, "updateAvailableNetworks: SSID to AP count mismatch for " + ssid);}it.remove();}}if (mVerboseLoggingEnabled) Log.v(TAG, toString());}

之前在梳理WiFi自动连接的时候有梳理到WifiConnectivityManager$handleScanResults,这个方法也会调用到WifiLastResortWatchdog$updateAvailableNetworks

http://androidxref.com/8.0.0_r4/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java

    /*** Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.* Executes selection of potential network candidates, initiation of connection attempt to that* network.** @return true - if a candidate is selected by WifiNetworkSelector*         false - if no candidate is selected by WifiNetworkSelector*/private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {// Check if any blacklisted BSSIDs can be freed.refreshBssidBlacklist();if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) {localLog(listenerName + " onResults: No network selection because linkDebouncing is "+ mStateMachine.isLinkDebouncing() + " and supplicantTransient 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());}return false;}}

继续看下WifiNetworkSelector

    /*** @return the list of ScanDetails scored as potential candidates by the last run of* selectNetwork, this will be empty if Network selector determined no selection was* needed on last run. This includes scan details of sufficient signal strength, and* had an associated WifiConfiguration.*/public List<Pair<ScanDetail, WifiConfiguration>> getConnectableScanDetails() {return mConnectableNetworks;}

这边还要看下mConnectableNetworks的处理

    // Buffer of filtered scan results (Scan results considered by network selection) & associated// WifiConfiguration (if any).private volatile List<Pair<ScanDetail, WifiConfiguration>> mConnectableNetworks =new ArrayList<>();/*** Select the best network from the ones in range.** @param scanDetails    List of ScanDetail for all the APs in range* @param bssidBlacklist Blacklisted BSSIDs* @param wifiInfo       Currently connected network* @param connected      True if the device is connected* @param disconnected   True if the device is disconnected* @param untrustedNetworkAllowed True if untrusted networks are allowed for connection* @return Configuration of the selected network, or Null if nothing*/@Nullablepublic 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;}

有三个Evalutor,这边只看下最熟悉的SavedNetworkEvaluator

    /*** 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();int highestScoreOfScanResult = Integer.MIN_VALUE;int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID;// 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.List<WifiConfiguration> associatedConfigurations = null;WifiConfiguration associatedConfiguration =mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);if (associatedConfiguration == null) {continue;} else {associatedConfigurations =new ArrayList<>(Arrays.asList(associatedConfiguration));}for (WifiConfiguration network : associatedConfigurations) {/*** 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 (score > highestScoreOfScanResult) {highestScoreOfScanResult = score;candidateIdOfScanResult = network.networkId;}}if (connectableNetworks != null) {connectableNetworks.add(Pair.create(scanDetail,mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult)));}if (highestScoreOfScanResult > highestScore|| (highestScoreOfScanResult == highestScore&& scanResultCandidate != null&& scanResult.level > scanResultCandidate.level)) {highestScore = highestScoreOfScanResult;scanResultCandidate = scanResult;mWifiConfigManager.setNetworkCandidateScanResult(candidateIdOfScanResult, scanResultCandidate, highestScore);// Reload the network config with the updated info.candidate = mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult);}}if (scoreHistory.length() > 0) {localLog("\n" + scoreHistory.toString());}if (scanResultCandidate == null) {localLog("did not see any good candidates.");}return candidate;}

这个方法会把搜索到的有资格连接(不一定要是最高分)的都加入到集合connectableNetworks中,最后更新到mWifiLastResortWatchdog,比如被enable的bssid相符的普通网络,比如插卡情况下的eap-sim网络

再回头看下WifiLastResortWatchdog的updateAvailableNetworks方法

    /*** Map of SSID to <FailureCount, AP count>, used to count failures & number of access points* belonging to an SSID.*/private Map<String, Pair<AvailableNetworkFailureCount, Integer>> mSsidFailureCount =new HashMap<>();/*** Refreshes recentAvailableNetworks with the latest available networks* Adds new networks, removes old ones that have timed out. Should be called after Wifi* framework decides what networks it is potentially connecting to.* @param availableNetworks ScanDetail & Config list of potential connection* candidates*/public void updateAvailableNetworks(List<Pair<ScanDetail, WifiConfiguration>> availableNetworks) {if (mVerboseLoggingEnabled) {Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size());}// Add new networks to mRecentAvailableNetworksif (availableNetworks != null) {for (Pair<ScanDetail, WifiConfiguration> pair : availableNetworks) {final ScanDetail scanDetail = pair.first;final WifiConfiguration config = pair.second;ScanResult scanResult = scanDetail.getScanResult();if (scanResult == null) continue;String bssid = scanResult.BSSID;String ssid = "\"" + scanDetail.getSSID() + "\"";if (mVerboseLoggingEnabled) {Log.v(TAG, " " + bssid + ": " + scanDetail.getSSID());}// Cache the scanResult & WifiConfigAvailableNetworkFailureCount availableNetworkFailureCount =mRecentAvailableNetworks.get(bssid);if (availableNetworkFailureCount == null) {// New network is availableavailableNetworkFailureCount = new AvailableNetworkFailureCount(config);availableNetworkFailureCount.ssid = ssid;// Count AP for this SSIDPair<AvailableNetworkFailureCount, Integer> ssidFailsAndApCount =mSsidFailureCount.get(ssid);if (ssidFailsAndApCount == null) {// This is a new SSID, create new FailureCount for it and set AP count to 1ssidFailsAndApCount = Pair.create(new AvailableNetworkFailureCount(config),1);setWatchdogTriggerEnabled(true);} else {final Integer numberOfAps = ssidFailsAndApCount.second;// This is not a new SSID, increment the AP count for itssidFailsAndApCount = Pair.create(ssidFailsAndApCount.first,numberOfAps + 1);}mSsidFailureCount.put(ssid, ssidFailsAndApCount);}// refresh config if it is not nullif (config != null) {availableNetworkFailureCount.config = config;}// If we saw a network, set its Age to -1 here, aging iteration will set it to 0availableNetworkFailureCount.age = -1;mRecentAvailableNetworks.put(bssid, availableNetworkFailureCount);}}// Iterate through available networks updating timeout counts & removing networks.Iterator<Map.Entry<String, AvailableNetworkFailureCount>> it =mRecentAvailableNetworks.entrySet().iterator();while (it.hasNext()) {Map.Entry<String, AvailableNetworkFailureCount> entry = it.next();if (entry.getValue().age < MAX_BSSID_AGE - 1) {entry.getValue().age++;} else {// Decrement this SSID : AP countString ssid = entry.getValue().ssid;Pair<AvailableNetworkFailureCount, Integer> ssidFails =mSsidFailureCount.get(ssid);if (ssidFails != null) {Integer apCount = ssidFails.second - 1;if (apCount > 0) {ssidFails = Pair.create(ssidFails.first, apCount);mSsidFailureCount.put(ssid, ssidFails);} else {mSsidFailureCount.remove(ssid);}} else {Log.d(TAG, "updateAvailableNetworks: SSID to AP count mismatch for " + ssid);}it.remove();}}if (mVerboseLoggingEnabled) Log.v(TAG, toString());}

看下

    /*** Maximum number of scan results received since we last saw a BSSID.* If it is not seen before this limit is reached, the network is culled*/public static final int MAX_BSSID_AGE = 10;

大致意思是从第一次缓存availableNetworks,即可连接网络,会最多缓存10次,若后续有次update包含该网络则会重新将age变为-1,否则一直增加,到10次后就会被移除掉,说明10次扫描都没有被认为是可连接的网络,那缓存没有意义了,移除即可。

这边完成了mSsidFailureCount和mRecentAvailableNetworks集合的增删

看了半天,这两个集合区别还是在于bssid是不是any,如果bssid不是any,那么ap数目肯定是1,可以用bssid作为key;若bssid为any,那么同名ssid的ap有可能有不止一个,所以需要apCount来计数。

下面就是最早看到的incrementSsidFailureCount对失败次数进行计数

    /*** Update the per-SSID failure count* @param ssid the ssid to increment failure count for* @param reason the failure type to increment count for*/private void incrementSsidFailureCount(String ssid, int reason) {Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid);if (ssidFails == null) {Log.d(TAG, "updateFailureCountForNetwork: No networks for ssid = " + ssid);return;}AvailableNetworkFailureCount failureCount = ssidFails.first;failureCount.incrementFailureCount(reason);}

然后下面是根据bssid对失败次数进行计数

    /*** Update the per-BSSID failure count* @param bssid the bssid to increment failure count for* @param reason the failure type to increment count for*/private void incrementBssidFailureCount(String ssid, String bssid, int reason) {AvailableNetworkFailureCount availableNetworkFailureCount =mRecentAvailableNetworks.get(bssid);if (availableNetworkFailureCount == null) {Log.d(TAG, "updateFailureCountForNetwork: Unable to find Network [" + ssid+ ", " + bssid + "]");return;}if (!availableNetworkFailureCount.ssid.equals(ssid)) {Log.d(TAG, "updateFailureCountForNetwork: Failed connection attempt has"+ " wrong ssid. Failed [" + ssid + ", " + bssid + "], buffered ["+ availableNetworkFailureCount.ssid + ", " + bssid + "]");return;}if (availableNetworkFailureCount.config == null) {if (mVerboseLoggingEnabled) {Log.v(TAG, "updateFailureCountForNetwork: network has no config ["+ ssid + ", " + bssid + "]");}}availableNetworkFailureCount.incrementFailureCount(reason);incrementSsidFailureCount(ssid, reason);}

可以看到incrementSsidFailureCount肯定会被调用的

更新完失败次数就是对次数进行校验看下次数是否达到重启的标准了

    /*** Increments the failure reason count for the given bssid. Performs a check to see if we have* exceeded a failure threshold for all available networks, and executes the last resort restart* @param bssid of the network that has failed connection, can be "any"* @param reason Message id from WifiStateMachine for this failure* @return true if watchdog triggers, returned for test visibility*/public boolean noteConnectionFailureAndTriggerIfNeeded(String ssid, String bssid, int reason) {if (mVerboseLoggingEnabled) {Log.v(TAG, "noteConnectionFailureAndTriggerIfNeeded: [" + ssid + ", " + bssid + ", "+ reason + "]");}// Update failure count for the failing networkupdateFailureCountForNetwork(ssid, bssid, reason);// Have we met conditions to trigger the Watchdog Wifi restart?boolean isRestartNeeded = checkTriggerCondition();if (mVerboseLoggingEnabled) {Log.v(TAG, "isRestartNeeded = " + isRestartNeeded);}if (isRestartNeeded) {// Stop the watchdog from triggering until re-enabledsetWatchdogTriggerEnabled(false);Log.e(TAG, "Watchdog triggering recovery");mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);// increment various watchdog trigger count statsincrementWifiMetricsTriggerCounts();clearAllFailureCounts();}return isRestartNeeded;}/*** Check trigger condition: For all available networks, have we met a failure threshold for each* of them, and have previously connected to at-least one of the available networks* @return is the trigger condition true*/private boolean checkTriggerCondition() {if (mVerboseLoggingEnabled) Log.v(TAG, "checkTriggerCondition.");// Don't check Watchdog trigger if wifi is in a connected state// (This should not occur, but we want to protect against any race conditions)if (mWifiIsConnected) return false;// Don't check Watchdog trigger if trigger is not enabledif (!mWatchdogAllowedToTrigger) return false;boolean atleastOneNetworkHasEverConnected = false;for (Map.Entry<String, AvailableNetworkFailureCount> entry: mRecentAvailableNetworks.entrySet()) {if (entry.getValue().config != null&& entry.getValue().config.getNetworkSelectionStatus().getHasEverConnected()) {atleastOneNetworkHasEverConnected = true;}if (!isOverFailureThreshold(entry.getKey())) {// This available network is not over failure threshold, meaning we still have a// network to try connecting toreturn false;}}// We have met the failure count for every available network & there is at-least one network// we have previously connected to present.if (mVerboseLoggingEnabled) {Log.v(TAG, "checkTriggerCondition: return = " + atleastOneNetworkHasEverConnected);}return atleastOneNetworkHasEverConnected;}

重启有两个条件

1.是否可选网络之前有连接过

2.是否可选网络失败次数已达到阈值

只有之前有连接过并且失败次数达到阈值才会认为需要重启,重启之后清空失败次数统计

    /*** @param bssid bssid to check the failures for* @return true if any failure count is over FAILURE_THRESHOLD*/public boolean isOverFailureThreshold(String bssid) {if ((getFailureCount(bssid, FAILURE_CODE_ASSOCIATION) >= FAILURE_THRESHOLD)|| (getFailureCount(bssid, FAILURE_CODE_AUTHENTICATION) >= FAILURE_THRESHOLD)|| (getFailureCount(bssid, FAILURE_CODE_DHCP) >= FAILURE_THRESHOLD)) {return true;}return false;}

失败次数统计呢主要是看下FAILURE_CODE_ASSOCIATION、FAILURE_CODE_AUTHENTICATION和FAILURE_CODE_DHCP失败次数是否超过FAILURE_THRESHOLD即7次

    /*** Failure count that each available networks must meet to possibly trigger the Watchdog*/public static final int FAILURE_THRESHOLD = 7;

 

2.2 REASON_WIFICOND_CRASH

                case CMD_CLIENT_INTERFACE_BINDER_DEATH:Log.e(TAG, "wificond died unexpectedly. Triggering recovery");mWifiMetrics.incrementNumWificondCrashes();mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_WIFICOND_CRASH);mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFICOND_CRASH);break;

 

2.3 REASON_HAL_CRASH

                case CMD_VENDOR_HAL_HWBINDER_DEATH:Log.e(TAG, "Vendor HAL died unexpectedly. Triggering recovery");mWifiMetrics.incrementNumHalCrashes();mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_HAL_CRASH);mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_HAL_CRASH);break;

 

3.总结

触发重启主要有3个原因,分别为watchdog、wificond_crash和hal_crash

其中watchdog触发条件是

1)可连接网络之前进行过连接

2)可连接网络ASSOCIATION/AUTHENTICATION/DHCP失败次数不小于7次

这篇关于(一百三十三)Android O WiFi重启机制学习——重启原因的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511389

相关文章

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

Redis的持久化之RDB和AOF机制详解

《Redis的持久化之RDB和AOF机制详解》:本文主要介绍Redis的持久化之RDB和AOF机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述RDB(Redis Database)核心原理触发方式手动触发自动触发AOF(Append-Only File)核

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文

SpringSecurity显示用户账号已被锁定的原因及解决方案

《SpringSecurity显示用户账号已被锁定的原因及解决方案》SpringSecurity中用户账号被锁定问题源于UserDetails接口方法返回值错误,解决方案是修正isAccountNon... 目录SpringSecurity显示用户账号已被锁定的解决方案1.问题出现前的工作2.问题出现原因各

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示