summaryrefslogtreecommitdiff
path: root/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
diff options
context:
space:
mode:
Diffstat (limited to 'service/java/com/android/server/wifi/WifiLastResortWatchdog.java')
-rw-r--r--service/java/com/android/server/wifi/WifiLastResortWatchdog.java309
1 files changed, 278 insertions, 31 deletions
diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
index 36f0656a8..6c069ade9 100644
--- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
+++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
@@ -27,55 +27,100 @@ 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<>();
/**
* 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);
+ } 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 +132,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 +157,112 @@ 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);
+ return false;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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() {
@@ -110,10 +279,67 @@ public class WifiLastResortWatchdog {
: mRecentAvailableNetworks.entrySet()) {
sb.append("\n " + entry.getKey() + ": " + 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" + entry.getKey() + ": " + apCount + ", "
+ + 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 +347,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 +369,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;