/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import android.content.Context; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 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 ClientModeImpl to InitialState. */ public class WifiLastResortWatchdog { private static final String TAG = "WifiLastResortWatchdog"; private boolean mVerboseLoggingEnabled = false; /** * 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; public static final String BUGREPORT_TITLE = "Wifi watchdog triggered"; public static final double PROB_TAKE_BUGREPORT_DEFAULT = 1; // Number of milliseconds to wait before re-enable Watchdog triger @VisibleForTesting public static final long LAST_TRIGGER_TIMEOUT_MILLIS = 2 * 3600 * 1000; // 2 hours private int mAbnormalConnectionDurationMs; private boolean mAbnormalConnectionBugreportEnabled; /** * Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results * Key:BSSID, Value:Counters of failure types */ private Map mRecentAvailableNetworks = new HashMap<>(); /** * Map of SSID to , used to count failures & number of access points * belonging to an SSID. */ private Map> mSsidFailureCount = new HashMap<>(); // Tracks: if ClientModeImpl 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; private long mTimeLastTrigger = 0; private String mSsidLastTrigger = null; private WifiInjector mWifiInjector; private WifiMetrics mWifiMetrics; private ClientModeImpl mClientModeImpl; private Looper mClientModeImplLooper; private double mBugReportProbability = PROB_TAKE_BUGREPORT_DEFAULT; private Clock mClock; private Context mContext; private DeviceConfigFacade mDeviceConfigFacade; // If any connection failure happened after watchdog triggering restart then assume watchdog // did not fix the problem private boolean mWatchdogFixedWifi = true; private long mLastStartConnectTime = 0; private Handler mHandler; /** * Local log used for debugging any WifiLastResortWatchdog issues. */ private final LocalLog mLocalLog = new LocalLog(100); WifiLastResortWatchdog(WifiInjector wifiInjector, Context context, Clock clock, WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, DeviceConfigFacade deviceConfigFacade) { mWifiInjector = wifiInjector; mClock = clock; mWifiMetrics = wifiMetrics; mClientModeImpl = clientModeImpl; mClientModeImplLooper = clientModeImplLooper; mContext = context; mDeviceConfigFacade = deviceConfigFacade; updateDeviceConfigFlags(); mHandler = new Handler(clientModeImplLooper) { public void handleMessage(Message msg) { processMessage(msg); } }; mDeviceConfigFacade.addOnPropertiesChangedListener( command -> mHandler.post(command), properties -> { updateDeviceConfigFlags(); }); } private void updateDeviceConfigFlags() { mAbnormalConnectionBugreportEnabled = mDeviceConfigFacade.isAbnormalConnectionBugreportEnabled(); mAbnormalConnectionDurationMs = mDeviceConfigFacade.getAbnormalConnectionDurationMs(); logv("updateDeviceConfigFlags: mAbnormalConnectionDurationMs = " + mAbnormalConnectionDurationMs + ", mAbnormalConnectionBugreportEnabled = " + mAbnormalConnectionBugreportEnabled); } /** * Returns handler for L2 events from supplicant. * @return Handler */ public Handler getHandler() { return mHandler; } /** * Refreshes when the last CMD_START_CONNECT is triggered. */ public void noteStartConnectTime() { mHandler.post(() -> { mLastStartConnectTime = mClock.getElapsedSinceBootMillis(); }); } private void processMessage(Message msg) { switch (msg.what) { case WifiMonitor.NETWORK_CONNECTION_EVENT: // Trigger bugreport for successful connections that take abnormally long if (mAbnormalConnectionBugreportEnabled && mLastStartConnectTime > 0) { long durationMs = mClock.getElapsedSinceBootMillis() - mLastStartConnectTime; if (durationMs > mAbnormalConnectionDurationMs) { final String bugTitle = "Wi-Fi Bugreport: Abnormal connection time"; final String bugDetail = "Expected connection to take less than " + mAbnormalConnectionDurationMs + " milliseconds. " + "Actually took " + durationMs + " milliseconds."; logv("Triggering bug report for abnormal connection time."); mWifiInjector.getClientModeImplHandler().post(() -> { mClientModeImpl.takeBugReport(bugTitle, bugDetail); }); } } // Should reset last connection time after each connection regardless if bugreport // is enabled or not. mLastStartConnectTime = 0; break; default: return; } } /** * 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); // Do not re-enable Watchdog in LAST_TRIGGER_TIMEOUT_MILLIS // after last time Watchdog be triggered if (mTimeLastTrigger == 0 || (mClock.getElapsedSinceBootMillis() - mTimeLastTrigger) >= LAST_TRIGGER_TIMEOUT_MILLIS) { localLog("updateAvailableNetworks: setWatchdogTriggerEnabled to true"); 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()); } /** * 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 ClientModeImpl 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); // If watchdog is not allowed to trigger it means a wifi restart is already triggered if (!mWatchdogAllowedToTrigger) { mWifiMetrics.incrementWatchdogTotalConnectionFailureCountAfterTrigger(); mWatchdogFixedWifi = false; } // 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 localLog("noteConnectionFailureAndTriggerIfNeeded: setWatchdogTriggerEnabled to false"); setWatchdogTriggerEnabled(false); mWatchdogFixedWifi = true; loge("Watchdog triggering recovery"); mSsidLastTrigger = ssid; mTimeLastTrigger = mClock.getElapsedSinceBootMillis(); localLog(toString()); mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); incrementWifiMetricsTriggerCounts(); clearAllFailureCounts(); } return isRestartNeeded; } /** * Handles transitions entering and exiting ClientModeImpl ConnectedState * Used to track wifistate, and perform watchdog count resetting * @param isEntering true if called from ConnectedState.enter(), false for exit() */ public void connectedStateTransition(boolean isEntering) { logv("connectedStateTransition: isEntering = " + isEntering); mWifiIsConnected = isEntering; if (!isEntering) { return; } if (!mWatchdogAllowedToTrigger && mWatchdogFixedWifi && checkIfAtleastOneNetworkHasEverConnected() && checkIfConnectedBackToSameSsid()) { takeBugReportWithCurrentProbability("Wifi fixed after restart"); // WiFi has connected after a Watchdog trigger, without any new networks becoming // available, log a Watchdog success in wifi metrics mWifiMetrics.incrementNumLastResortWatchdogSuccesses(); long durationMs = mClock.getElapsedSinceBootMillis() - mTimeLastTrigger; mWifiMetrics.setWatchdogSuccessTimeDurationMs(durationMs); } // 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. localLog("connectedStateTransition: setWatchdogTriggerEnabled to true"); setWatchdogTriggerEnabled(true); } /** * Helper function to check if device connect back to same * SSID after watchdog trigger */ private boolean checkIfConnectedBackToSameSsid() { if (TextUtils.equals(mSsidLastTrigger, mClientModeImpl.getWifiInfo().getSSID())) { return true; } localLog("checkIfConnectedBackToSameSsid: different SSID be connected"); return false; } /** * Triggers a wifi specific bugreport with a based on the current trigger probability. * @param bugDetail description of the bug */ private void takeBugReportWithCurrentProbability(String bugDetail) { if (mBugReportProbability <= Math.random()) { return; } (new Handler(mClientModeImplLooper)).post(() -> { mClientModeImpl.takeBugReport(BUGREPORT_TITLE, bugDetail); }); } /** * 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 ClientModeImpl for this failure */ private void updateFailureCountForNetwork(String ssid, String bssid, int reason) { logv("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 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); } /** * Helper function to check if we should ignore BSSID update. * @param bssid BSSID of the access point * @return true if we should ignore BSSID update */ public boolean shouldIgnoreBssidUpdate(String bssid) { return mWatchdogAllowedToTrigger && isBssidOnlyApOfSsid(bssid) && isSingleSsidRecorded() && checkIfAtleastOneNetworkHasEverConnected(); } /** * Helper function to check if we should ignore SSID update. * @return true if should ignore SSID update */ public boolean shouldIgnoreSsidUpdate() { return mWatchdogAllowedToTrigger && isSingleSsidRecorded() && checkIfAtleastOneNetworkHasEverConnected(); } /** * Check the specified BSSID is the only BSSID for its corresponding SSID. * @param bssid BSSID of the access point * @return true if only BSSID for its corresponding SSID be observed */ private boolean isBssidOnlyApOfSsid(String bssid) { AvailableNetworkFailureCount availableNetworkFailureCount = mRecentAvailableNetworks.get(bssid); if (availableNetworkFailureCount == null) { return false; } String ssid = availableNetworkFailureCount.ssid; Pair ssidFails = mSsidFailureCount.get(ssid); if (ssidFails == null) { Log.d(TAG, "isOnlyBssidAvailable: Could not find SSID count for " + ssid); return false; } if (ssidFails.second != 1) { return false; } return true; } /** * Check there is only single SSID be observed. * @return true if only single SSID be observed. */ private boolean isSingleSsidRecorded() { return (mSsidFailureCount.size() == 1); } /** * 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; for (Map.Entry entry : mRecentAvailableNetworks.entrySet()) { 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. // Trigger restart if there exists at-least one network that we have previously connected. boolean atleastOneNetworkHasEverConnected = checkIfAtleastOneNetworkHasEverConnected(); logv("checkTriggerCondition: return = " + atleastOneNetworkHasEverConnected); return checkIfAtleastOneNetworkHasEverConnected(); } private boolean checkIfAtleastOneNetworkHasEverConnected() { for (Map.Entry entry : mRecentAvailableNetworks.entrySet()) { if (entry.getValue().config != null && entry.getValue().config.getNetworkSelectionStatus().getHasEverConnected()) { return true; } } return false; } /** * Update WifiMetrics with various Watchdog stats (trigger counts, failed network counts) */ private void incrementWifiMetricsTriggerCounts() { if (mVerboseLoggingEnabled) Log.v(TAG, "incrementWifiMetricsTriggerCounts."); mWifiMetrics.incrementNumLastResortWatchdogTriggers(); mWifiMetrics.addCountToNumLastResortWatchdogAvailableNetworksTotal( mSsidFailureCount.size()); // Number of networks over each failure type threshold, present at trigger time int badAuth = 0; int badAssoc = 0; int badDhcp = 0; int badSum = 0; for (Map.Entry> entry : mSsidFailureCount.entrySet()) { badSum = entry.getValue().first.associationRejection + entry.getValue().first.authenticationFailure + entry.getValue().first.dhcpFailure; // count as contributor if over half of badSum. if (badSum >= FAILURE_THRESHOLD) { badAssoc += (entry.getValue().first.associationRejection >= badSum / 2) ? 1 : 0; badAuth += (entry.getValue().first.authenticationFailure >= badSum / 2) ? 1 : 0; badDhcp += (entry.getValue().first.dhcpFailure >= badSum / 2) ? 1 : 0; } } if (badAuth > 0) { mWifiMetrics.addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(badAuth); mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadAuthentication(); } if (badAssoc > 0) { mWifiMetrics.addCountToNumLastResortWatchdogBadAssociationNetworksTotal(badAssoc); mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadAssociation(); } if (badDhcp > 0) { mWifiMetrics.addCountToNumLastResortWatchdogBadDhcpNetworksTotal(badDhcp); mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadDhcp(); } } /** * Clear failure counts for each network in recentAvailableNetworks */ public void clearAllFailureCounts() { if (mVerboseLoggingEnabled) Log.v(TAG, "clearAllFailureCounts."); for (Map.Entry entry : mRecentAvailableNetworks.entrySet()) { final AvailableNetworkFailureCount failureCount = entry.getValue(); failureCount.resetCounts(); } for (Map.Entry> entry : mSsidFailureCount.entrySet()) { final AvailableNetworkFailureCount failureCount = entry.getValue().first; failureCount.resetCounts(); } } /** * Gets the buffer of recently available networks */ Map getRecentAvailableNetworks() { return mRecentAvailableNetworks; } /** * 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 (mVerboseLoggingEnabled) 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("mWatchdogAllowedToTrigger: ").append(mWatchdogAllowedToTrigger); sb.append("\nmWifiIsConnected: ").append(mWifiIsConnected); sb.append("\nmRecentAvailableNetworks: ").append(mRecentAvailableNetworks.size()); for (Map.Entry entry : mRecentAvailableNetworks.entrySet()) { sb.append("\n ").append(entry.getKey()).append(": ").append(entry.getValue()) .append(", Age: ").append(entry.getValue().age); } sb.append("\nmSsidFailureCount:"); for (Map.Entry> 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(); } /** * @param bssid bssid to check the failures for * @return true if sum of failure count is over FAILURE_THRESHOLD */ public boolean isOverFailureThreshold(String bssid) { return (getFailureCount(bssid, FAILURE_CODE_ASSOCIATION) + getFailureCount(bssid, FAILURE_CODE_AUTHENTICATION) + getFailureCount(bssid, FAILURE_CODE_DHCP)) >= FAILURE_THRESHOLD; } /** * 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 ssidFails = mSsidFailureCount.get(ssid); if (ssidFails == null) { 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; } } protected void enableVerboseLogging(int verbose) { if (verbose > 0) { mVerboseLoggingEnabled = true; } else { mVerboseLoggingEnabled = false; } } @VisibleForTesting protected void setBugReportProbability(double newProbability) { mBugReportProbability = newProbability; } /** * 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 */ public WifiConfiguration config; /** * SSID of the network (from ScanDetail) */ public String ssid = ""; /** * Number of times network has failed due to Association Rejection */ public int associationRejection = 0; /** * Number of times network has failed due to Authentication Failure or SSID_TEMP_DISABLED */ public int authenticationFailure = 0; /** * Number of times network has failed due to DHCP failure */ public int dhcpFailure = 0; /** * Number of scanResults since this network was last seen */ public int age = 0; AvailableNetworkFailureCount(WifiConfiguration configParam) { this.config = configParam; } /** * @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; authenticationFailure = 0; dhcpFailure = 0; } public String toString() { return ssid + " HasEverConnected: " + ((config != null) ? config.getNetworkSelectionStatus().getHasEverConnected() : "null_config") + ", Failures: {" + "Assoc: " + associationRejection + ", Auth: " + authenticationFailure + ", Dhcp: " + dhcpFailure + "}"; } } /** * Helper function for logging into local log buffer. */ private void localLog(String s) { mLocalLog.log(s); } private void logv(String s) { mLocalLog.log(s); if (mVerboseLoggingEnabled) { Log.v(TAG, s); } } private void loge(String s) { mLocalLog.log(s); Log.e(TAG, s); } /** * Dump the local log buffer and other internal state of WifiLastResortWatchdog. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Dump of WifiLastResortWatchdog"); pw.println("WifiLastResortWatchdog - Log Begin ----"); mLocalLog.dump(fd, pw, args); pw.println("WifiLastResortWatchdog - Log End ----"); } }