前言:在之前一篇博客中(一百三十二)Android O WiFi重启机制学习——重启流程 提及
“api中简要说明了这个类是用来将WiFi从异常状态恢复的。恢复机制是通过WifiController触发一个重启,基本模拟了一次飞行模式切换。
当前触发条件
看门狗
正常操作过程中的HAL/wificond crashes
TBD:正常操作过程中的supplicant crashes(暂未有对应处理)”
继续看下重启原因
从截图来看触发重启主要的3个地方也已经很明显的搜索到了,分别为watchdog、wificond_crash和hal_crash
分别看下watchdog、wificond_crash和hal_crash
/**
* 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 network
updateFailureCountForNetwork(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-enabled
setWatchdogTriggerEnabled(false);
Log.e(TAG, "Watchdog triggering recovery");
mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
// increment various watchdog trigger count stats
incrementWifiMetricsTriggerCounts();
clearAllFailureCounts();
}
return isRestartNeeded;
}
看了下这个方法只有WifiStateMachine会调用,分别是FAILURE_CODE_AUTHENTICATION、FAILURE_CODE_ASSOCIATION和FAILURE_CODE_DHCP
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;
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 BSSID
bssid = mTargetRoamBSSID;
}
if (bssid != null) {
// If we have a BSSID, tell configStore to black list it
didBlackListBSSID = 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;
@Override
public 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);
}
再回头看下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 method
incrementBssidFailureCount(ssid, bssid, reason);
}
}
/**
* Map of SSID to , used to count failures & number of access points
* belonging to an SSID.
*/
private Map> 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 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 , used to count failures & number of access points
* belonging to an SSID.
*/
private Map> 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> availableNetworks) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size());
}
// Add new networks to mRecentAvailableNetworks
if (availableNetworks != null) {
for (Pair 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 & WifiConfig
AvailableNetworkFailureCount availableNetworkFailureCount =
mRecentAvailableNetworks.get(bssid);
if (availableNetworkFailureCount == null) {
// New network is available
availableNetworkFailureCount = new AvailableNetworkFailureCount(config);
availableNetworkFailureCount.ssid = ssid;
// Count AP for this SSID
Pair ssidFailsAndApCount =
mSsidFailureCount.get(ssid);
if (ssidFailsAndApCount == null) {
// This is a new SSID, create new FailureCount for it and set AP count to 1
ssidFailsAndApCount = 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 it
ssidFailsAndApCount = Pair.create(ssidFailsAndApCount.first,
numberOfAps + 1);
}
mSsidFailureCount.put(ssid, ssidFailsAndApCount);
}
// refresh config if it is not null
if (config != null) {
availableNetworkFailureCount.config = config;
}
// If we saw a network, set its Age to -1 here, aging iteration will set it to 0
availableNetworkFailureCount.age = -1;
mRecentAvailableNetworks.put(bssid, availableNetworkFailureCount);
}
}
// Iterate through available networks updating timeout counts & removing networks.
Iterator> it =
mRecentAvailableNetworks.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
if (entry.getValue().age < MAX_BSSID_AGE - 1) {
entry.getValue().age++;
} else {
// Decrement this SSID : AP count
String ssid = entry.getValue().ssid;
Pair 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 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> getConnectableScanDetails() {
return mConnectableNetworks;
}
这边还要看下mConnectableNetworks的处理
// Buffer of filtered scan results (Scan results considered by network selection) & associated
// WifiConfiguration (if any).
private volatile List> 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
*/
@Nullable
public WifiConfiguration selectNetwork(List scanDetails,
HashSet 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 scanDetails,
WifiConfiguration currentNetwork, String currentBssid, boolean connected,
boolean untrustedNetworkAllowed,
List> 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 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 , used to count failures & number of access points
* belonging to an SSID.
*/
private Map> 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> availableNetworks) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size());
}
// Add new networks to mRecentAvailableNetworks
if (availableNetworks != null) {
for (Pair 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 & WifiConfig
AvailableNetworkFailureCount availableNetworkFailureCount =
mRecentAvailableNetworks.get(bssid);
if (availableNetworkFailureCount == null) {
// New network is available
availableNetworkFailureCount = new AvailableNetworkFailureCount(config);
availableNetworkFailureCount.ssid = ssid;
// Count AP for this SSID
Pair ssidFailsAndApCount =
mSsidFailureCount.get(ssid);
if (ssidFailsAndApCount == null) {
// This is a new SSID, create new FailureCount for it and set AP count to 1
ssidFailsAndApCount = 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 it
ssidFailsAndApCount = Pair.create(ssidFailsAndApCount.first,
numberOfAps + 1);
}
mSsidFailureCount.put(ssid, ssidFailsAndApCount);
}
// refresh config if it is not null
if (config != null) {
availableNetworkFailureCount.config = config;
}
// If we saw a network, set its Age to -1 here, aging iteration will set it to 0
availableNetworkFailureCount.age = -1;
mRecentAvailableNetworks.put(bssid, availableNetworkFailureCount);
}
}
// Iterate through available networks updating timeout counts & removing networks.
Iterator> it =
mRecentAvailableNetworks.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
if (entry.getValue().age < MAX_BSSID_AGE - 1) {
entry.getValue().age++;
} else {
// Decrement this SSID : AP count
String ssid = entry.getValue().ssid;
Pair 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 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 network
updateFailureCountForNetwork(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-enabled
setWatchdogTriggerEnabled(false);
Log.e(TAG, "Watchdog triggering recovery");
mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
// increment various watchdog trigger count stats
incrementWifiMetricsTriggerCounts();
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 enabled
if (!mWatchdogAllowedToTrigger) return false;
boolean atleastOneNetworkHasEverConnected = false;
for (Map.Entry 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 to
return 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;
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;
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个原因,分别为watchdog、wificond_crash和hal_crash
其中watchdog触发条件是
1)可连接网络之前进行过连接
2)可连接网络ASSOCIATION/AUTHENTICATION/DHCP失败次数不小于7次