1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import android.net.wifi.ScanResult; 20 import android.net.wifi.WifiConfiguration; 21 import android.os.Handler; 22 import android.os.Looper; 23 import android.util.Log; 24 import android.util.Pair; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.util.HashMap; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.Map; 32 33 /** 34 * This Class is a Work-In-Progress, intended behavior is as follows: 35 * Essentially this class automates a user toggling 'Airplane Mode' when WiFi "won't work". 36 * IF each available saved network has failed connecting more times than the FAILURE_THRESHOLD 37 * THEN Watchdog will restart Supplicant, wifi driver and return WifiStateMachine to InitialState. 38 */ 39 public class WifiLastResortWatchdog { 40 private static final String TAG = "WifiLastResortWatchdog"; 41 private boolean mVerboseLoggingEnabled = false; 42 /** 43 * Association Failure code 44 */ 45 public static final int FAILURE_CODE_ASSOCIATION = 1; 46 /** 47 * Authentication Failure code 48 */ 49 public static final int FAILURE_CODE_AUTHENTICATION = 2; 50 /** 51 * Dhcp Failure code 52 */ 53 public static final int FAILURE_CODE_DHCP = 3; 54 /** 55 * Maximum number of scan results received since we last saw a BSSID. 56 * If it is not seen before this limit is reached, the network is culled 57 */ 58 public static final int MAX_BSSID_AGE = 10; 59 /** 60 * BSSID used to increment failure counts against ALL bssids associated with a particular SSID 61 */ 62 public static final String BSSID_ANY = "any"; 63 /** 64 * Failure count that each available networks must meet to possibly trigger the Watchdog 65 */ 66 public static final int FAILURE_THRESHOLD = 7; 67 public static final String BUGREPORT_TITLE = "Wifi watchdog triggered"; 68 public static final double PROB_TAKE_BUGREPORT_DEFAULT = 0.08; 69 70 /** 71 * Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results 72 * Key:BSSID, Value:Counters of failure types 73 */ 74 private Map<String, AvailableNetworkFailureCount> mRecentAvailableNetworks = new HashMap<>(); 75 76 /** 77 * Map of SSID to <FailureCount, AP count>, used to count failures & number of access points 78 * belonging to an SSID. 79 */ 80 private Map<String, Pair<AvailableNetworkFailureCount, Integer>> mSsidFailureCount = 81 new HashMap<>(); 82 83 // Tracks: if WifiStateMachine is in ConnectedState 84 private boolean mWifiIsConnected = false; 85 // Is Watchdog allowed to trigger now? Set to false after triggering. Set to true after 86 // successfully connecting or a new network (SSID) becomes available to connect to. 87 private boolean mWatchdogAllowedToTrigger = true; 88 private long mTimeLastTrigger; 89 90 private SelfRecovery mSelfRecovery; 91 private WifiMetrics mWifiMetrics; 92 private WifiStateMachine mWifiStateMachine; 93 private Looper mWifiStateMachineLooper; 94 private double mBugReportProbability = PROB_TAKE_BUGREPORT_DEFAULT; 95 private Clock mClock; 96 // If any connection failure happened after watchdog triggering restart then assume watchdog 97 // did not fix the problem 98 private boolean mWatchdogFixedWifi = true; 99 100 WifiLastResortWatchdog(SelfRecovery selfRecovery, Clock clock, WifiMetrics wifiMetrics, 101 WifiStateMachine wsm, Looper wifiStateMachineLooper) { 102 mSelfRecovery = selfRecovery; 103 mClock = clock; 104 mWifiMetrics = wifiMetrics; 105 mWifiStateMachine = wsm; 106 mWifiStateMachineLooper = wifiStateMachineLooper; 107 } 108 109 /** 110 * Refreshes recentAvailableNetworks with the latest available networks 111 * Adds new networks, removes old ones that have timed out. Should be called after Wifi 112 * framework decides what networks it is potentially connecting to. 113 * @param availableNetworks ScanDetail & Config list of potential connection 114 * candidates 115 */ 116 public void updateAvailableNetworks( 117 List<Pair<ScanDetail, WifiConfiguration>> availableNetworks) { 118 if (mVerboseLoggingEnabled) { 119 Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size()); 120 } 121 // Add new networks to mRecentAvailableNetworks 122 if (availableNetworks != null) { 123 for (Pair<ScanDetail, WifiConfiguration> pair : availableNetworks) { 124 final ScanDetail scanDetail = pair.first; 125 final WifiConfiguration config = pair.second; 126 ScanResult scanResult = scanDetail.getScanResult(); 127 if (scanResult == null) continue; 128 String bssid = scanResult.BSSID; 129 String ssid = "\"" + scanDetail.getSSID() + "\""; 130 if (mVerboseLoggingEnabled) { 131 Log.v(TAG, " " + bssid + ": " + scanDetail.getSSID()); 132 } 133 // Cache the scanResult & WifiConfig 134 AvailableNetworkFailureCount availableNetworkFailureCount = 135 mRecentAvailableNetworks.get(bssid); 136 if (availableNetworkFailureCount == null) { 137 // New network is available 138 availableNetworkFailureCount = new AvailableNetworkFailureCount(config); 139 availableNetworkFailureCount.ssid = ssid; 140 141 // Count AP for this SSID 142 Pair<AvailableNetworkFailureCount, Integer> ssidFailsAndApCount = 143 mSsidFailureCount.get(ssid); 144 if (ssidFailsAndApCount == null) { 145 // This is a new SSID, create new FailureCount for it and set AP count to 1 146 ssidFailsAndApCount = Pair.create(new AvailableNetworkFailureCount(config), 147 1); 148 setWatchdogTriggerEnabled(true); 149 } else { 150 final Integer numberOfAps = ssidFailsAndApCount.second; 151 // This is not a new SSID, increment the AP count for it 152 ssidFailsAndApCount = Pair.create(ssidFailsAndApCount.first, 153 numberOfAps + 1); 154 } 155 mSsidFailureCount.put(ssid, ssidFailsAndApCount); 156 } 157 // refresh config if it is not null 158 if (config != null) { 159 availableNetworkFailureCount.config = config; 160 } 161 // If we saw a network, set its Age to -1 here, aging iteration will set it to 0 162 availableNetworkFailureCount.age = -1; 163 mRecentAvailableNetworks.put(bssid, availableNetworkFailureCount); 164 } 165 } 166 167 // Iterate through available networks updating timeout counts & removing networks. 168 Iterator<Map.Entry<String, AvailableNetworkFailureCount>> it = 169 mRecentAvailableNetworks.entrySet().iterator(); 170 while (it.hasNext()) { 171 Map.Entry<String, AvailableNetworkFailureCount> entry = it.next(); 172 if (entry.getValue().age < MAX_BSSID_AGE - 1) { 173 entry.getValue().age++; 174 } else { 175 // Decrement this SSID : AP count 176 String ssid = entry.getValue().ssid; 177 Pair<AvailableNetworkFailureCount, Integer> ssidFails = 178 mSsidFailureCount.get(ssid); 179 if (ssidFails != null) { 180 Integer apCount = ssidFails.second - 1; 181 if (apCount > 0) { 182 ssidFails = Pair.create(ssidFails.first, apCount); 183 mSsidFailureCount.put(ssid, ssidFails); 184 } else { 185 mSsidFailureCount.remove(ssid); 186 } 187 } else { 188 Log.d(TAG, "updateAvailableNetworks: SSID to AP count mismatch for " + ssid); 189 } 190 it.remove(); 191 } 192 } 193 if (mVerboseLoggingEnabled) Log.v(TAG, toString()); 194 } 195 196 /** 197 * Increments the failure reason count for the given bssid. Performs a check to see if we have 198 * exceeded a failure threshold for all available networks, and executes the last resort restart 199 * @param bssid of the network that has failed connection, can be "any" 200 * @param reason Message id from WifiStateMachine for this failure 201 * @return true if watchdog triggers, returned for test visibility 202 */ 203 public boolean noteConnectionFailureAndTriggerIfNeeded(String ssid, String bssid, int reason) { 204 if (mVerboseLoggingEnabled) { 205 Log.v(TAG, "noteConnectionFailureAndTriggerIfNeeded: [" + ssid + ", " + bssid + ", " 206 + reason + "]"); 207 } 208 209 // Update failure count for the failing network 210 updateFailureCountForNetwork(ssid, bssid, reason); 211 212 // If watchdog is not allowed to trigger it means a wifi restart is already triggered 213 if (!mWatchdogAllowedToTrigger) { 214 mWifiMetrics.incrementWatchdogTotalConnectionFailureCountAfterTrigger(); 215 mWatchdogFixedWifi = false; 216 } 217 // Have we met conditions to trigger the Watchdog Wifi restart? 218 boolean isRestartNeeded = checkTriggerCondition(); 219 if (mVerboseLoggingEnabled) { 220 Log.v(TAG, "isRestartNeeded = " + isRestartNeeded); 221 } 222 if (isRestartNeeded) { 223 // Stop the watchdog from triggering until re-enabled 224 setWatchdogTriggerEnabled(false); 225 mWatchdogFixedWifi = true; 226 Log.e(TAG, "Watchdog triggering recovery"); 227 mTimeLastTrigger = mClock.getElapsedSinceBootMillis(); 228 mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); 229 // increment various watchdog trigger count stats 230 incrementWifiMetricsTriggerCounts(); 231 clearAllFailureCounts(); 232 } 233 return isRestartNeeded; 234 } 235 236 /** 237 * Handles transitions entering and exiting WifiStateMachine ConnectedState 238 * Used to track wifistate, and perform watchdog count resetting 239 * @param isEntering true if called from ConnectedState.enter(), false for exit() 240 */ 241 public void connectedStateTransition(boolean isEntering) { 242 if (mVerboseLoggingEnabled) { 243 Log.v(TAG, "connectedStateTransition: isEntering = " + isEntering); 244 } 245 mWifiIsConnected = isEntering; 246 if (!isEntering) { 247 return; 248 } 249 if (!mWatchdogAllowedToTrigger && mWatchdogFixedWifi 250 && checkIfAtleastOneNetworkHasEverConnected()) { 251 takeBugReportWithCurrentProbability("Wifi fixed after restart"); 252 // WiFi has connected after a Watchdog trigger, without any new networks becoming 253 // available, log a Watchdog success in wifi metrics 254 mWifiMetrics.incrementNumLastResortWatchdogSuccesses(); 255 long durationMs = mClock.getElapsedSinceBootMillis() - mTimeLastTrigger; 256 mWifiMetrics.setWatchdogSuccessTimeDurationMs(durationMs); 257 } 258 // We connected to something! Reset failure counts for everything 259 clearAllFailureCounts(); 260 // If the watchdog trigger was disabled (it triggered), connecting means we did 261 // something right, re-enable it so it can fire again. 262 setWatchdogTriggerEnabled(true); 263 } 264 265 /** 266 * Triggers a wifi specific bugreport with a based on the current trigger probability. 267 * @param bugDetail description of the bug 268 */ 269 private void takeBugReportWithCurrentProbability(String bugDetail) { 270 if (mBugReportProbability <= Math.random()) { 271 return; 272 } 273 (new Handler(mWifiStateMachineLooper)).post(() -> { 274 mWifiStateMachine.takeBugReport(BUGREPORT_TITLE, bugDetail); 275 }); 276 } 277 278 /** 279 * Increments the failure reason count for the given network, in 'mSsidFailureCount' 280 * Failures are counted per SSID, either; by using the ssid string when the bssid is "any" 281 * or by looking up the ssid attached to a specific bssid 282 * An unused set of counts is also kept which is bssid specific, in 'mRecentAvailableNetworks' 283 * @param ssid of the network that has failed connection 284 * @param bssid of the network that has failed connection, can be "any" 285 * @param reason Message id from WifiStateMachine for this failure 286 */ 287 private void updateFailureCountForNetwork(String ssid, String bssid, int reason) { 288 if (mVerboseLoggingEnabled) { 289 Log.v(TAG, "updateFailureCountForNetwork: [" + ssid + ", " + bssid + ", " 290 + reason + "]"); 291 } 292 if (BSSID_ANY.equals(bssid)) { 293 incrementSsidFailureCount(ssid, reason); 294 } else { 295 // Bssid count is actually unused except for logging purposes 296 // SSID count is incremented within the BSSID counting method 297 incrementBssidFailureCount(ssid, bssid, reason); 298 } 299 } 300 301 /** 302 * Update the per-SSID failure count 303 * @param ssid the ssid to increment failure count for 304 * @param reason the failure type to increment count for 305 */ 306 private void incrementSsidFailureCount(String ssid, int reason) { 307 Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid); 308 if (ssidFails == null) { 309 Log.d(TAG, "updateFailureCountForNetwork: No networks for ssid = " + ssid); 310 return; 311 } 312 AvailableNetworkFailureCount failureCount = ssidFails.first; 313 failureCount.incrementFailureCount(reason); 314 } 315 316 /** 317 * Update the per-BSSID failure count 318 * @param bssid the bssid to increment failure count for 319 * @param reason the failure type to increment count for 320 */ 321 private void incrementBssidFailureCount(String ssid, String bssid, int reason) { 322 AvailableNetworkFailureCount availableNetworkFailureCount = 323 mRecentAvailableNetworks.get(bssid); 324 if (availableNetworkFailureCount == null) { 325 Log.d(TAG, "updateFailureCountForNetwork: Unable to find Network [" + ssid 326 + ", " + bssid + "]"); 327 return; 328 } 329 if (!availableNetworkFailureCount.ssid.equals(ssid)) { 330 Log.d(TAG, "updateFailureCountForNetwork: Failed connection attempt has" 331 + " wrong ssid. Failed [" + ssid + ", " + bssid + "], buffered [" 332 + availableNetworkFailureCount.ssid + ", " + bssid + "]"); 333 return; 334 } 335 if (availableNetworkFailureCount.config == null) { 336 if (mVerboseLoggingEnabled) { 337 Log.v(TAG, "updateFailureCountForNetwork: network has no config [" 338 + ssid + ", " + bssid + "]"); 339 } 340 } 341 availableNetworkFailureCount.incrementFailureCount(reason); 342 incrementSsidFailureCount(ssid, reason); 343 } 344 345 /** 346 * Check trigger condition: For all available networks, have we met a failure threshold for each 347 * of them, and have previously connected to at-least one of the available networks 348 * @return is the trigger condition true 349 */ 350 private boolean checkTriggerCondition() { 351 if (mVerboseLoggingEnabled) Log.v(TAG, "checkTriggerCondition."); 352 // Don't check Watchdog trigger if wifi is in a connected state 353 // (This should not occur, but we want to protect against any race conditions) 354 if (mWifiIsConnected) return false; 355 // Don't check Watchdog trigger if trigger is not enabled 356 if (!mWatchdogAllowedToTrigger) return false; 357 358 for (Map.Entry<String, AvailableNetworkFailureCount> entry 359 : mRecentAvailableNetworks.entrySet()) { 360 if (!isOverFailureThreshold(entry.getKey())) { 361 // This available network is not over failure threshold, meaning we still have a 362 // network to try connecting to 363 return false; 364 } 365 } 366 // We have met the failure count for every available network. 367 // Trigger restart if there exists at-least one network that we have previously connected. 368 boolean atleastOneNetworkHasEverConnected = checkIfAtleastOneNetworkHasEverConnected(); 369 if (mVerboseLoggingEnabled) { 370 Log.v(TAG, "checkTriggerCondition: return = " + atleastOneNetworkHasEverConnected); 371 } 372 return checkIfAtleastOneNetworkHasEverConnected(); 373 } 374 375 private boolean checkIfAtleastOneNetworkHasEverConnected() { 376 for (Map.Entry<String, AvailableNetworkFailureCount> entry 377 : mRecentAvailableNetworks.entrySet()) { 378 if (entry.getValue().config != null 379 && entry.getValue().config.getNetworkSelectionStatus().getHasEverConnected()) { 380 return true; 381 } 382 } 383 return false; 384 } 385 386 /** 387 * Update WifiMetrics with various Watchdog stats (trigger counts, failed network counts) 388 */ 389 private void incrementWifiMetricsTriggerCounts() { 390 if (mVerboseLoggingEnabled) Log.v(TAG, "incrementWifiMetricsTriggerCounts."); 391 mWifiMetrics.incrementNumLastResortWatchdogTriggers(); 392 mWifiMetrics.addCountToNumLastResortWatchdogAvailableNetworksTotal( 393 mSsidFailureCount.size()); 394 // Number of networks over each failure type threshold, present at trigger time 395 int badAuth = 0; 396 int badAssoc = 0; 397 int badDhcp = 0; 398 for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry 399 : mSsidFailureCount.entrySet()) { 400 badAuth += (entry.getValue().first.authenticationFailure >= FAILURE_THRESHOLD) ? 1 : 0; 401 badAssoc += (entry.getValue().first.associationRejection >= FAILURE_THRESHOLD) ? 1 : 0; 402 badDhcp += (entry.getValue().first.dhcpFailure >= FAILURE_THRESHOLD) ? 1 : 0; 403 } 404 if (badAuth > 0) { 405 mWifiMetrics.addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(badAuth); 406 mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadAuthentication(); 407 } 408 if (badAssoc > 0) { 409 mWifiMetrics.addCountToNumLastResortWatchdogBadAssociationNetworksTotal(badAssoc); 410 mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadAssociation(); 411 } 412 if (badDhcp > 0) { 413 mWifiMetrics.addCountToNumLastResortWatchdogBadDhcpNetworksTotal(badDhcp); 414 mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadDhcp(); 415 } 416 } 417 418 /** 419 * Clear failure counts for each network in recentAvailableNetworks 420 */ 421 public void clearAllFailureCounts() { 422 if (mVerboseLoggingEnabled) Log.v(TAG, "clearAllFailureCounts."); 423 for (Map.Entry<String, AvailableNetworkFailureCount> entry 424 : mRecentAvailableNetworks.entrySet()) { 425 final AvailableNetworkFailureCount failureCount = entry.getValue(); 426 failureCount.resetCounts(); 427 } 428 for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry 429 : mSsidFailureCount.entrySet()) { 430 final AvailableNetworkFailureCount failureCount = entry.getValue().first; 431 failureCount.resetCounts(); 432 } 433 } 434 /** 435 * Gets the buffer of recently available networks 436 */ 437 Map<String, AvailableNetworkFailureCount> getRecentAvailableNetworks() { 438 return mRecentAvailableNetworks; 439 } 440 441 /** 442 * Activates or deactivates the Watchdog trigger. Counting and network buffering still occurs 443 * @param enable true to enable the Watchdog trigger, false to disable it 444 */ 445 private void setWatchdogTriggerEnabled(boolean enable) { 446 if (mVerboseLoggingEnabled) Log.v(TAG, "setWatchdogTriggerEnabled: enable = " + enable); 447 mWatchdogAllowedToTrigger = enable; 448 } 449 450 /** 451 * Prints all networks & counts within mRecentAvailableNetworks to string 452 */ 453 public String toString() { 454 StringBuilder sb = new StringBuilder(); 455 sb.append("mWatchdogAllowedToTrigger: ").append(mWatchdogAllowedToTrigger); 456 sb.append("\nmWifiIsConnected: ").append(mWifiIsConnected); 457 sb.append("\nmRecentAvailableNetworks: ").append(mRecentAvailableNetworks.size()); 458 for (Map.Entry<String, AvailableNetworkFailureCount> entry 459 : mRecentAvailableNetworks.entrySet()) { 460 sb.append("\n ").append(entry.getKey()).append(": ").append(entry.getValue()) 461 .append(", Age: ").append(entry.getValue().age); 462 } 463 sb.append("\nmSsidFailureCount:"); 464 for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry : 465 mSsidFailureCount.entrySet()) { 466 final AvailableNetworkFailureCount failureCount = entry.getValue().first; 467 final Integer apCount = entry.getValue().second; 468 sb.append("\n").append(entry.getKey()).append(": ").append(apCount).append(",") 469 .append(failureCount.toString()); 470 } 471 return sb.toString(); 472 } 473 474 /** 475 * @param bssid bssid to check the failures for 476 * @return true if any failure count is over FAILURE_THRESHOLD 477 */ 478 public boolean isOverFailureThreshold(String bssid) { 479 if ((getFailureCount(bssid, FAILURE_CODE_ASSOCIATION) >= FAILURE_THRESHOLD) 480 || (getFailureCount(bssid, FAILURE_CODE_AUTHENTICATION) >= FAILURE_THRESHOLD) 481 || (getFailureCount(bssid, FAILURE_CODE_DHCP) >= FAILURE_THRESHOLD)) { 482 return true; 483 } 484 return false; 485 } 486 487 /** 488 * Get the failure count for a specific bssid. This actually checks the ssid attached to the 489 * BSSID and returns the SSID count 490 * @param reason failure reason to get count for 491 */ 492 public int getFailureCount(String bssid, int reason) { 493 AvailableNetworkFailureCount availableNetworkFailureCount = 494 mRecentAvailableNetworks.get(bssid); 495 if (availableNetworkFailureCount == null) { 496 return 0; 497 } 498 String ssid = availableNetworkFailureCount.ssid; 499 Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid); 500 if (ssidFails == null) { 501 Log.d(TAG, "getFailureCount: Could not find SSID count for " + ssid); 502 return 0; 503 } 504 final AvailableNetworkFailureCount failCount = ssidFails.first; 505 switch (reason) { 506 case FAILURE_CODE_ASSOCIATION: 507 return failCount.associationRejection; 508 case FAILURE_CODE_AUTHENTICATION: 509 return failCount.authenticationFailure; 510 case FAILURE_CODE_DHCP: 511 return failCount.dhcpFailure; 512 default: 513 return 0; 514 } 515 } 516 517 protected void enableVerboseLogging(int verbose) { 518 if (verbose > 0) { 519 mVerboseLoggingEnabled = true; 520 } else { 521 mVerboseLoggingEnabled = false; 522 } 523 } 524 525 @VisibleForTesting 526 protected void setBugReportProbability(double newProbability) { 527 mBugReportProbability = newProbability; 528 } 529 530 /** 531 * This class holds the failure counts for an 'available network' (one of the potential 532 * candidates for connection, as determined by framework). 533 */ 534 public static class AvailableNetworkFailureCount { 535 /** 536 * WifiConfiguration associated with this network. Can be null for Ephemeral networks 537 */ 538 public WifiConfiguration config; 539 /** 540 * SSID of the network (from ScanDetail) 541 */ 542 public String ssid = ""; 543 /** 544 * Number of times network has failed due to Association Rejection 545 */ 546 public int associationRejection = 0; 547 /** 548 * Number of times network has failed due to Authentication Failure or SSID_TEMP_DISABLED 549 */ 550 public int authenticationFailure = 0; 551 /** 552 * Number of times network has failed due to DHCP failure 553 */ 554 public int dhcpFailure = 0; 555 /** 556 * Number of scanResults since this network was last seen 557 */ 558 public int age = 0; 559 560 AvailableNetworkFailureCount(WifiConfiguration configParam) { 561 this.config = configParam; 562 } 563 564 /** 565 * @param reason failure reason to increment count for 566 */ 567 public void incrementFailureCount(int reason) { 568 switch (reason) { 569 case FAILURE_CODE_ASSOCIATION: 570 associationRejection++; 571 break; 572 case FAILURE_CODE_AUTHENTICATION: 573 authenticationFailure++; 574 break; 575 case FAILURE_CODE_DHCP: 576 dhcpFailure++; 577 break; 578 default: //do nothing 579 } 580 } 581 582 /** 583 * Set all failure counts for this network to 0 584 */ 585 void resetCounts() { 586 associationRejection = 0; 587 authenticationFailure = 0; 588 dhcpFailure = 0; 589 } 590 591 public String toString() { 592 return ssid + " HasEverConnected: " + ((config != null) 593 ? config.getNetworkSelectionStatus().getHasEverConnected() : "null_config") 594 + ", Failures: {" 595 + "Assoc: " + associationRejection 596 + ", Auth: " + authenticationFailure 597 + ", Dhcp: " + dhcpFailure 598 + "}"; 599 } 600 } 601 } 602