diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2016-04-27 19:46:37 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-04-27 19:46:37 +0000 |
commit | 7883cf595742b2c9b1ccba223b45a5fcb6978ec9 (patch) | |
tree | 91744ffd2e863f97f4de7e06d61053e8f52a2548 /service | |
parent | 1f4fa946d229906500f381fa5561821fba164ba8 (diff) | |
parent | 83f2b8087178705445e4d1618eaac832f9c633f4 (diff) |
Merge changes I91ad6127,I416bfd0e into nyc-dev
* changes:
WifiLastResortWatchdog trigger logic
WifiLastResortWatchdog failure counting
Diffstat (limited to 'service')
-rw-r--r-- | service/java/com/android/server/wifi/WifiLastResortWatchdog.java | 410 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiStateMachine.java | 26 |
2 files changed, 403 insertions, 33 deletions
diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java index 36f0656a8..adb8771ea 100644 --- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java +++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java @@ -27,55 +27,106 @@ import java.util.List; import java.util.Map; /** - * <TBD> Intended Purpose/Behavior of the class upon completion: + * This Class is a Work-In-Progress, intended behavior is as follows: * Essentially this class automates a user toggling 'Airplane Mode' when WiFi "won't work". * IF each available saved network has failed connecting more times than the FAILURE_THRESHOLD * THEN Watchdog will restart Supplicant, wifi driver and return WifiStateMachine to InitialState. - * </TBD> */ public class WifiLastResortWatchdog { private static final String TAG = "WifiLastResortWatchdog"; private static final boolean VDBG = false; + private static final boolean DBG = true; + /** + * Association Failure code + */ + public static final int FAILURE_CODE_ASSOCIATION = 1; + /** + * Authentication Failure code + */ + public static final int FAILURE_CODE_AUTHENTICATION = 2; + /** + * Dhcp Failure code + */ + public static final int FAILURE_CODE_DHCP = 3; + /** + * 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; + /** + * BSSID used to increment failure counts against ALL bssids associated with a particular SSID + */ + public static final String BSSID_ANY = "any"; + /** + * Failure count that each available networks must meet to possibly trigger the Watchdog + */ + public static final int FAILURE_THRESHOLD = 7; /** * Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results * Key:BSSID, Value:Counters of failure types */ private Map<String, AvailableNetworkFailureCount> mRecentAvailableNetworks = new HashMap<>(); - - // 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; + /** + * 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<>(); + // Tracks: if WifiStateMachine is in ConnectedState + private boolean mWifiIsConnected = false; + // Is Watchdog allowed to trigger now? Set to false after triggering. Set to true after + // successfully connecting or a new network (SSID) becomes available to connect to. + private boolean mWatchdogAllowedToTrigger = true; /** * 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 availableNetworkFailureCounts ScanDetail & Config list of potential connection + * @param availableNetworks ScanDetail & Config list of potential connection * candidates */ public void updateAvailableNetworks( - List<Pair<ScanDetail, WifiConfiguration>> availableNetworkFailureCounts) { + List<Pair<ScanDetail, WifiConfiguration>> availableNetworks) { + if (VDBG) Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size()); // Add new networks to mRecentAvailableNetworks - if (availableNetworkFailureCounts != null) { - for (Pair<ScanDetail, WifiConfiguration> pair : availableNetworkFailureCounts) { - ScanResult scanResult = pair.first.getScanResult(); + if (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 key = scanResult.BSSID; - + String bssid = scanResult.BSSID; + String ssid = "\"" + scanDetail.getSSID() + "\""; + if (VDBG) Log.v(TAG, " " + bssid + ": " + scanDetail.getSSID()); // Cache the scanResult & WifiConfig AvailableNetworkFailureCount availableNetworkFailureCount = - mRecentAvailableNetworks.get(key); - if (availableNetworkFailureCount != null) { - // We've already cached this, refresh timeout count & config - availableNetworkFailureCount.config = pair.second; - } else { + mRecentAvailableNetworks.get(bssid); + if (availableNetworkFailureCount == null) { // New network is available - availableNetworkFailureCount = new AvailableNetworkFailureCount(pair.second); - availableNetworkFailureCount.Ssid = pair.first.getSSID(); + availableNetworkFailureCount = new AvailableNetworkFailureCount(config); + availableNetworkFailureCount.ssid = ssid; + + // Count AP for this SSID + Pair<AvailableNetworkFailureCount, Integer> 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); } - // If we saw a network, set its Age to -1 here, next incrementation will set it to 0 + // refresh config + 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(key, availableNetworkFailureCount); + mRecentAvailableNetworks.put(bssid, availableNetworkFailureCount); } } @@ -87,6 +138,24 @@ public class WifiLastResortWatchdog { if (entry.getValue().age < MAX_BSSID_AGE - 1) { entry.getValue().age++; } else { + // Decrement this SSID : AP count + String 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 { + if (DBG) { + Log.d(TAG, "updateAvailableNetworks: SSID to AP count mismatch for " + + ssid); + } + } it.remove(); } } @@ -94,6 +163,192 @@ public class WifiLastResortWatchdog { } /** + * 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 (VDBG) { + 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 (isRestartNeeded) { + // Stop the watchdog from triggering until re-enabled + setWatchdogTriggerEnabled(false); + restartWifiStack(); + // increment various watchdog trigger count stats + incrementWifiMetricsTriggerCounts(); + clearAllFailureCounts(); + } + return isRestartNeeded; + } + + /** + * Handles transitions entering and exiting WifiStateMachine ConnectedState + * Used to track wifistate, and perform watchdog count reseting + * @param isEntering true if called from ConnectedState.enter(), false for exit() + */ + public void connectedStateTransition(boolean isEntering) { + if (VDBG) Log.v(TAG, "connectedStateTransition: isEntering = " + isEntering); + mWifiIsConnected = isEntering; + if (isEntering) { + // We connected to something! Reset failure counts for everything + clearAllFailureCounts(); + // If the watchdog trigger was disabled (it triggered), connecting means we did + // something right, re-enable it so it can fire again. + setWatchdogTriggerEnabled(true); + } + } + + /** + * 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 (VDBG) { + 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); + } + } + + /** + * 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) { + if (DBG) { + Log.v(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) { + if (DBG) { + Log.d(TAG, "updateFailureCountForNetwork: Unable to find Network [" + ssid + + ", " + bssid + "]"); + } + return; + } + if (!availableNetworkFailureCount.ssid.equals(ssid)) { + if (DBG) { + Log.d(TAG, "updateFailureCountForNetwork: Failed connection attempt has" + + " wrong ssid. Failed [" + ssid + ", " + bssid + "], buffered [" + + availableNetworkFailureCount.ssid + ", " + bssid + "]"); + } + return; + } + if (availableNetworkFailureCount.config == null) { + if (VDBG) { + Log.v(TAG, "updateFailureCountForNetwork: network has no config [" + + ssid + ", " + bssid + "]"); + } + } + availableNetworkFailureCount.incrementFailureCount(reason); + incrementSsidFailureCount(ssid, reason); + } + + /** + * 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 (VDBG) 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<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 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 (VDBG) { + Log.v(TAG, "checkTriggerCondition: return = " + atleastOneNetworkHasEverConnected); + } + return atleastOneNetworkHasEverConnected; + } + + /** + * Restart Supplicant, Driver & return WifiStateMachine to InitialState + */ + private void restartWifiStack() { + if (VDBG) Log.v(TAG, "restartWifiStack."); + Log.i(TAG, "Triggered."); + if (DBG) Log.d(TAG, toString()); + // <TODO> + } + + /** + * Update WifiMetrics with various Watchdog stats (trigger counts, tracked network count) + */ + private void incrementWifiMetricsTriggerCounts() { + if (VDBG) Log.v(TAG, "incrementWifiMetricsTriggerCounts."); + // <TODO> + } + + /** + * Clear failure counts for each network in recentAvailableNetworks + */ + private void clearAllFailureCounts() { + if (VDBG) Log.v(TAG, "clearAllFailureCounts."); + for (Map.Entry<String, AvailableNetworkFailureCount> entry + : mRecentAvailableNetworks.entrySet()) { + final AvailableNetworkFailureCount failureCount = entry.getValue(); + entry.getValue().resetCounts(); + } + for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry + : mSsidFailureCount.entrySet()) { + final AvailableNetworkFailureCount failureCount = entry.getValue().first; + failureCount.resetCounts(); + } + } + /** * Gets the buffer of recently available networks */ Map<String, AvailableNetworkFailureCount> getRecentAvailableNetworks() { @@ -101,19 +356,87 @@ public class WifiLastResortWatchdog { } /** + * Activates or deactivates the Watchdog trigger. Counting and network buffering still occurs + * @param enable true to enable the Watchdog trigger, false to disable it + */ + private void setWatchdogTriggerEnabled(boolean enable) { + if (VDBG) Log.v(TAG, "setWatchdogTriggerEnabled: enable = " + enable); + mWatchdogAllowedToTrigger = enable; + } + + /** * Prints all networks & counts within mRecentAvailableNetworks to string */ public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("WifiLastResortWatchdog: " + mRecentAvailableNetworks.size() + " networks..."); + sb.append("mWatchdogAllowedToTrigger: ").append(mWatchdogAllowedToTrigger); + sb.append("\nmWifiIsConnected: ").append(mWifiIsConnected); + sb.append("\nmRecentAvailableNetworks: ").append(mRecentAvailableNetworks.size()); for (Map.Entry<String, AvailableNetworkFailureCount> entry : mRecentAvailableNetworks.entrySet()) { - sb.append("\n " + entry.getKey() + ": " + entry.getValue()); + sb.append("\n ").append(entry.getKey()).append(": ").append(entry.getValue()); + } + sb.append("\nmSsidFailureCount:"); + for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry : + mSsidFailureCount.entrySet()) { + final AvailableNetworkFailureCount failureCount = entry.getValue().first; + final Integer apCount = entry.getValue().second; + sb.append("\n").append(entry.getKey()).append(": ").append(apCount).append(", ") + .append(failureCount.toString()); } return sb.toString(); } - static class AvailableNetworkFailureCount { + /** + * @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; + } + + /** + * Get the failure count for a specific bssid. This actually checks the ssid attached to the + * BSSID and returns the SSID count + * @param reason failure reason to get count for + */ + public int getFailureCount(String bssid, int reason) { + AvailableNetworkFailureCount availableNetworkFailureCount = + mRecentAvailableNetworks.get(bssid); + if (availableNetworkFailureCount == null) { + return 0; + } + String ssid = availableNetworkFailureCount.ssid; + Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid); + if (ssidFails == null) { + if (DBG) { + Log.d(TAG, "getFailureCount: Could not find SSID count for " + ssid); + } + return 0; + } + final AvailableNetworkFailureCount failCount = ssidFails.first; + switch (reason) { + case FAILURE_CODE_ASSOCIATION: + return failCount.associationRejection; + case FAILURE_CODE_AUTHENTICATION: + return failCount.authenticationFailure; + case FAILURE_CODE_DHCP: + return failCount.dhcpFailure; + default: + return 0; + } + } + + /** + * This class holds the failure counts for an 'available network' (one of the potential + * candidates for connection, as determined by framework). + */ + public static class AvailableNetworkFailureCount { /** * WifiConfiguration associated with this network. Can be null for Ephemeral networks */ @@ -121,17 +444,17 @@ public class WifiLastResortWatchdog { /** * SSID of the network (from ScanDetail) */ - public String Ssid = ""; + public String ssid = ""; /** - * Number of times network has failed for this reason + * Number of times network has failed due to Association Rejection */ public int associationRejection = 0; /** - * Number of times network has failed for this reason + * Number of times network has failed due to Authentication Failure or SSID_TEMP_DISABLED */ - public int authenticationRejection = 0; + public int authenticationFailure = 0; /** - * Number of times network has failed for this reason + * Number of times network has failed due to DHCP failure */ public int dhcpFailure = 0; /** @@ -143,18 +466,39 @@ public class WifiLastResortWatchdog { config = config; } + /** + * @param reason failure reason to increment count for + */ + public void incrementFailureCount(int reason) { + switch (reason) { + case FAILURE_CODE_ASSOCIATION: + associationRejection++; + break; + case FAILURE_CODE_AUTHENTICATION: + authenticationFailure++; + break; + case FAILURE_CODE_DHCP: + dhcpFailure++; + break; + default: //do nothing + } + } + + /** + * Set all failure counts for this network to 0 + */ void resetCounts() { associationRejection = 0; - authenticationRejection = 0; + authenticationFailure = 0; dhcpFailure = 0; } public String toString() { - return Ssid + ", HasEverConnected: " + ((config != null) + return ssid + ", HasEverConnected: " + ((config != null) ? config.getNetworkSelectionStatus().getHasEverConnected() : false) + ", Failures: {" + "Assoc: " + associationRejection - + ", Auth: " + authenticationRejection + + ", Auth: " + authenticationFailure + ", Dhcp: " + dhcpFailure + "}" + ", Age: " + age; diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index d464fdb55..3e429ac2d 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -1239,6 +1239,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults); } else { sendMessage(CMD_IPV4_PROVISIONING_FAILURE); + mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), + mTargetRoamBSSID, + WifiLastResortWatchdog.FAILURE_CODE_DHCP); } } @@ -5334,6 +5337,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE); + mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), + bssid, + WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION); break; case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_AUTH_FAILURE); @@ -5347,6 +5353,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE, WifiMetricsProto.ConnectionEvent.HLF_NONE); + mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), + mTargetRoamBSSID, + WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); break; case WifiMonitor.SSID_TEMP_DISABLED: Log.e(TAG, "Supplicant SSID temporary disabled:" @@ -5358,6 +5367,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED, WifiMetricsProto.ConnectionEvent.HLF_NONE); + mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(), + mTargetRoamBSSID, + WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); break; case WifiMonitor.SSID_REENABLED: Log.d(TAG, "Supplicant SSID reenable:" @@ -7166,6 +7178,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mLastDriverRoamAttempt = 0; mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID; + mWifiLastResortWatchdog.connectedStateTransition(true); } @Override public boolean processMessage(Message message) { @@ -7430,6 +7443,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mLastDriverRoamAttempt = 0; mWhiteListedSsids = null; + mWifiLastResortWatchdog.connectedStateTransition(false); } } @@ -8317,4 +8331,16 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } + + /** + * Gets the SSID from the WifiConfiguration pointed at by 'mTargetNetworkId' + * This should match the network config framework is attempting to connect to. + */ + private String getTargetSsid() { + WifiConfiguration currentConfig = mWifiConfigManager.getWifiConfiguration(mTargetNetworkId); + if (currentConfig != null) { + return currentConfig.SSID; + } + return null; + } } |