WifiConnectivityManager.java revision ab8c62a5c348702090aceee23de12945cc3fdb0d
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 17package com.android.server.wifi; 18 19import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; 20 21import android.app.AlarmManager; 22import android.content.Context; 23import android.net.wifi.ScanResult; 24import android.net.wifi.SupplicantState; 25import android.net.wifi.WifiConfiguration; 26import android.net.wifi.WifiInfo; 27import android.net.wifi.WifiScanner; 28import android.net.wifi.WifiScanner.PnoSettings; 29import android.net.wifi.WifiScanner.ScanSettings; 30import android.os.Handler; 31import android.os.Looper; 32import android.util.LocalLog; 33import android.util.Log; 34 35import com.android.internal.R; 36import com.android.internal.annotations.VisibleForTesting; 37import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator; 38import com.android.server.wifi.util.ScanResultUtil; 39 40import java.io.FileDescriptor; 41import java.io.PrintWriter; 42import java.util.ArrayList; 43import java.util.HashMap; 44import java.util.HashSet; 45import java.util.Iterator; 46import java.util.LinkedList; 47import java.util.List; 48import java.util.Map; 49import java.util.Set; 50 51/** 52 * This class manages all the connectivity related scanning activities. 53 * 54 * When the screen is turned on or off, WiFi is connected or disconnected, 55 * or on-demand, a scan is initiatiated and the scan results are passed 56 * to WifiNetworkSelector for it to make a recommendation on which network 57 * to connect to. 58 */ 59public class WifiConnectivityManager { 60 public static final String WATCHDOG_TIMER_TAG = 61 "WifiConnectivityManager Schedule Watchdog Timer"; 62 public static final String PERIODIC_SCAN_TIMER_TAG = 63 "WifiConnectivityManager Schedule Periodic Scan Timer"; 64 public static final String RESTART_SINGLE_SCAN_TIMER_TAG = 65 "WifiConnectivityManager Restart Single Scan"; 66 public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG = 67 "WifiConnectivityManager Restart Scan"; 68 69 private static final long RESET_TIME_STAMP = Long.MIN_VALUE; 70 // Constants to indicate whether a scan should start immediately or 71 // it should comply to the minimum scan interval rule. 72 private static final boolean SCAN_IMMEDIATELY = true; 73 private static final boolean SCAN_ON_SCHEDULE = false; 74 // Periodic scan interval in milli-seconds. This is the scan 75 // performed when screen is on. 76 @VisibleForTesting 77 public static final int PERIODIC_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds 78 // When screen is on and WiFi traffic is heavy, exponential backoff 79 // connectivity scans are scheduled. This constant defines the maximum 80 // scan interval in this scenario. 81 @VisibleForTesting 82 public static final int MAX_PERIODIC_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds 83 // PNO scan interval in milli-seconds. This is the scan 84 // performed when screen is off and disconnected. 85 private static final int DISCONNECTED_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds 86 // PNO scan interval in milli-seconds. This is the scan 87 // performed when screen is off and connected. 88 private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds 89 // When a network is found by PNO scan but gets rejected by Wifi Network Selector due 90 // to its low RSSI value, scan will be reschduled in an exponential back off manner. 91 private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds 92 private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds 93 // Maximum number of retries when starting a scan failed 94 @VisibleForTesting 95 public static final int MAX_SCAN_RESTART_ALLOWED = 5; 96 // Number of milli-seconds to delay before retry starting 97 // a previously failed scan 98 private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds 99 // When in disconnected mode, a watchdog timer will be fired 100 // every WATCHDOG_INTERVAL_MS to start a single scan. This is 101 // to prevent caveat from things like PNO scan. 102 private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes 103 // Restricted channel list age out value. 104 private static final int CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour 105 // This is the time interval for the connection attempt rate calculation. Connection attempt 106 // timestamps beyond this interval is evicted from the list. 107 public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins 108 // Max number of connection attempts in the above time interval. 109 public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6; 110 // Packet tx/rx rates to determine if we want to do partial vs full scans. 111 // TODO(b/31180330): Make these device configs. 112 public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8; 113 public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16; 114 115 // WifiStateMachine has a bunch of states. From the 116 // WifiConnectivityManager's perspective it only cares 117 // if it is in Connected state, Disconnected state or in 118 // transition between these two states. 119 public static final int WIFI_STATE_UNKNOWN = 0; 120 public static final int WIFI_STATE_CONNECTED = 1; 121 public static final int WIFI_STATE_DISCONNECTED = 2; 122 public static final int WIFI_STATE_TRANSITIONING = 3; 123 124 // Saved network evaluator priority 125 private static final int SAVED_NETWORK_EVALUATOR_PRIORITY = 1; 126 private static final int PASSPOINT_NETWORK_EVALUATOR_PRIORITY = 2; 127 private static final int RECOMMENDED_NETWORK_EVALUATOR_PRIORITY = 3; 128 129 // Log tag for this class 130 private static final String TAG = "WifiConnectivityManager"; 131 132 private final WifiStateMachine mStateMachine; 133 private final WifiScanner mScanner; 134 private final WifiConfigManager mConfigManager; 135 private final WifiInfo mWifiInfo; 136 private final WifiConnectivityHelper mConnectivityHelper; 137 private final WifiNetworkSelector mNetworkSelector; 138 private final WifiLastResortWatchdog mWifiLastResortWatchdog; 139 private final WifiMetrics mWifiMetrics; 140 private final AlarmManager mAlarmManager; 141 private final Handler mEventHandler; 142 private final Clock mClock; 143 private final LocalLog mLocalLog; 144 private final LinkedList<Long> mConnectionAttemptTimeStamps; 145 146 private boolean mDbg = false; 147 private boolean mWifiEnabled = false; 148 private boolean mWifiConnectivityManagerEnabled = true; 149 private boolean mScreenOn = false; 150 private int mWifiState = WIFI_STATE_UNKNOWN; 151 private boolean mUntrustedConnectionAllowed = false; 152 private int mScanRestartCount = 0; 153 private int mSingleScanRestartCount = 0; 154 private int mTotalConnectivityAttemptsRateLimited = 0; 155 private String mLastConnectionAttemptBssid = null; 156 private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; 157 private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 158 private boolean mPnoScanStarted = false; 159 private boolean mPeriodicScanTimerSet = false; 160 // Device configs 161 private boolean mEnableAutoJoinWhenAssociated; 162 private boolean mWaitForFullBandScanResults = false; 163 164 // PNO settings 165 private int mMin5GHzRssi; 166 private int mMin24GHzRssi; 167 private int mInitialScoreMax; 168 private int mCurrentConnectionBonus; 169 private int mSameNetworkBonus; 170 private int mSecureBonus; 171 private int mBand5GHzBonus; 172 173 // BSSID blacklist 174 @VisibleForTesting 175 public static final int BSSID_BLACKLIST_THRESHOLD = 3; 176 @VisibleForTesting 177 public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000; 178 private static class BssidBlacklistStatus { 179 // Number of times this BSSID has been rejected for association. 180 public int counter; 181 public boolean isBlacklisted; 182 public long blacklistedTimeStamp = RESET_TIME_STAMP; 183 } 184 private Map<String, BssidBlacklistStatus> mBssidBlacklist = 185 new HashMap<>(); 186 187 // Association failure reason codes 188 @VisibleForTesting 189 public static final int REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; 190 191 // A helper to log debugging information in the local log buffer, which can 192 // be retrieved in bugreport. 193 private void localLog(String log) { 194 if (mLocalLog != null) { 195 mLocalLog.log(log); 196 } 197 } 198 199 // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 200 // if the start scan command failed. An timer is used here to make it a deferred retry. 201 private final AlarmManager.OnAlarmListener mRestartScanListener = 202 new AlarmManager.OnAlarmListener() { 203 public void onAlarm() { 204 startConnectivityScan(SCAN_IMMEDIATELY); 205 } 206 }; 207 208 // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 209 // if the start scan command failed. An timer is used here to make it a deferred retry. 210 private class RestartSingleScanListener implements AlarmManager.OnAlarmListener { 211 private final boolean mIsFullBandScan; 212 213 RestartSingleScanListener(boolean isFullBandScan) { 214 mIsFullBandScan = isFullBandScan; 215 } 216 217 @Override 218 public void onAlarm() { 219 startSingleScan(mIsFullBandScan); 220 } 221 } 222 223 // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS 224 // if it is in the WIFI_STATE_DISCONNECTED state. 225 private final AlarmManager.OnAlarmListener mWatchdogListener = 226 new AlarmManager.OnAlarmListener() { 227 public void onAlarm() { 228 watchdogHandler(); 229 } 230 }; 231 232 // Due to b/28020168, timer based single scan will be scheduled 233 // to provide periodic scan in an exponential backoff fashion. 234 private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener = 235 new AlarmManager.OnAlarmListener() { 236 public void onAlarm() { 237 periodicScanTimerHandler(); 238 } 239 }; 240 241 /** 242 * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener. 243 * Executes selection of potential network candidates, initiation of connection attempt to that 244 * network. 245 * 246 * @return true - if a candidate is selected by WifiNetworkSelector 247 * false - if no candidate is selected by WifiNetworkSelector 248 */ 249 private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) { 250 // Check if any blacklisted BSSIDs can be freed. 251 refreshBssidBlacklist(); 252 253 if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) { 254 localLog(listenerName + " onResults: No network selection because linkDebouncing is " 255 + mStateMachine.isLinkDebouncing() + " and supplicantTransient is " 256 + mStateMachine.isSupplicantTransientState()); 257 return false; 258 } 259 260 localLog(listenerName + " onResults: start network selection"); 261 262 WifiConfiguration candidate = 263 mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo, 264 mStateMachine.isConnected(), mStateMachine.isDisconnected(), 265 mUntrustedConnectionAllowed); 266 mWifiLastResortWatchdog.updateAvailableNetworks( 267 mNetworkSelector.getFilteredScanDetails()); 268 mWifiMetrics.countScanResults(scanDetails); 269 if (candidate != null) { 270 localLog(listenerName + ": WNS candidate-" + candidate.SSID); 271 connectToNetwork(candidate); 272 return true; 273 } else { 274 return false; 275 } 276 } 277 278 // All single scan results listener. 279 // 280 // Note: This is the listener for all the available single scan results, 281 // including the ones initiated by WifiConnectivityManager and 282 // other modules. 283 private class AllSingleScanListener implements WifiScanner.ScanListener { 284 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 285 286 public void clearScanDetails() { 287 mScanDetails.clear(); 288 } 289 290 @Override 291 public void onSuccess() { 292 localLog("registerScanListener onSuccess"); 293 } 294 295 @Override 296 public void onFailure(int reason, String description) { 297 localLog("registerScanListener onFailure:" 298 + " reason: " + reason + " description: " + description); 299 } 300 301 @Override 302 public void onPeriodChanged(int periodInMs) { 303 } 304 305 @Override 306 public void onResults(WifiScanner.ScanData[] results) { 307 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 308 clearScanDetails(); 309 mWaitForFullBandScanResults = false; 310 return; 311 } 312 313 // Full band scan results only. 314 if (mWaitForFullBandScanResults) { 315 if (!results[0].isAllChannelsScanned()) { 316 localLog("AllSingleScanListener waiting for full band scan results."); 317 clearScanDetails(); 318 return; 319 } else { 320 mWaitForFullBandScanResults = false; 321 } 322 } 323 324 boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener"); 325 clearScanDetails(); 326 327 // Update metrics to see if a single scan detected a valid network 328 // while PNO scan didn't. 329 // Note: We don't update the background scan metrics any more as it is 330 // not in use. 331 if (mPnoScanStarted) { 332 if (wasConnectAttempted) { 333 mWifiMetrics.incrementNumConnectivityWatchdogPnoBad(); 334 } else { 335 mWifiMetrics.incrementNumConnectivityWatchdogPnoGood(); 336 } 337 } 338 } 339 340 @Override 341 public void onFullResult(ScanResult fullScanResult) { 342 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 343 return; 344 } 345 346 if (mDbg) { 347 localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID 348 + " capabilities " + fullScanResult.capabilities); 349 } 350 351 mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult)); 352 } 353 } 354 355 private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener(); 356 357 // Single scan results listener. A single scan is initiated when 358 // DisconnectedPNO scan found a valid network and woke up 359 // the system, or by the watchdog timer, or to form the timer based 360 // periodic scan. 361 // 362 // Note: This is the listener for the single scans initiated by the 363 // WifiConnectivityManager. 364 private class SingleScanListener implements WifiScanner.ScanListener { 365 private final boolean mIsFullBandScan; 366 367 SingleScanListener(boolean isFullBandScan) { 368 mIsFullBandScan = isFullBandScan; 369 } 370 371 @Override 372 public void onSuccess() { 373 localLog("SingleScanListener onSuccess"); 374 } 375 376 @Override 377 public void onFailure(int reason, String description) { 378 localLog("SingleScanListener onFailure:" 379 + " reason: " + reason + " description: " + description); 380 381 // reschedule the scan 382 if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 383 scheduleDelayedSingleScan(mIsFullBandScan); 384 } else { 385 mSingleScanRestartCount = 0; 386 localLog("Failed to successfully start single scan for " 387 + MAX_SCAN_RESTART_ALLOWED + " times"); 388 } 389 } 390 391 @Override 392 public void onPeriodChanged(int periodInMs) { 393 localLog("SingleScanListener onPeriodChanged: " 394 + "actual scan period " + periodInMs + "ms"); 395 } 396 397 @Override 398 public void onResults(WifiScanner.ScanData[] results) { 399 } 400 401 @Override 402 public void onFullResult(ScanResult fullScanResult) { 403 } 404 } 405 406 // PNO scan results listener for both disconected and connected PNO scanning. 407 // A PNO scan is initiated when screen is off. 408 private class PnoScanListener implements WifiScanner.PnoScanListener { 409 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 410 private int mLowRssiNetworkRetryDelay = 411 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 412 413 public void clearScanDetails() { 414 mScanDetails.clear(); 415 } 416 417 // Reset to the start value when either a non-PNO scan is started or 418 // WifiNetworkSelector selects a candidate from the PNO scan results. 419 public void resetLowRssiNetworkRetryDelay() { 420 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 421 } 422 423 @VisibleForTesting 424 public int getLowRssiNetworkRetryDelay() { 425 return mLowRssiNetworkRetryDelay; 426 } 427 428 @Override 429 public void onSuccess() { 430 localLog("PnoScanListener onSuccess"); 431 } 432 433 @Override 434 public void onFailure(int reason, String description) { 435 localLog("PnoScanListener onFailure:" 436 + " reason: " + reason + " description: " + description); 437 438 // reschedule the scan 439 if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 440 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS); 441 } else { 442 mScanRestartCount = 0; 443 localLog("Failed to successfully start PNO scan for " 444 + MAX_SCAN_RESTART_ALLOWED + " times"); 445 } 446 } 447 448 @Override 449 public void onPeriodChanged(int periodInMs) { 450 localLog("PnoScanListener onPeriodChanged: " 451 + "actual scan period " + periodInMs + "ms"); 452 } 453 454 // Currently the PNO scan results doesn't include IE, 455 // which contains information required by WifiNetworkSelector. Ignore them 456 // for now. 457 @Override 458 public void onResults(WifiScanner.ScanData[] results) { 459 } 460 461 @Override 462 public void onFullResult(ScanResult fullScanResult) { 463 } 464 465 @Override 466 public void onPnoNetworkFound(ScanResult[] results) { 467 localLog("PnoScanListener: onPnoNetworkFound: results len = " + results.length); 468 469 for (ScanResult result: results) { 470 mScanDetails.add(ScanResultUtil.toScanDetail(result)); 471 } 472 473 boolean wasConnectAttempted; 474 wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener"); 475 clearScanDetails(); 476 mScanRestartCount = 0; 477 478 if (!wasConnectAttempted) { 479 // The scan results were rejected by WifiNetworkSelector due to low RSSI values 480 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) { 481 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS; 482 } 483 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay); 484 485 // Set up the delay value for next retry. 486 mLowRssiNetworkRetryDelay *= 2; 487 } else { 488 resetLowRssiNetworkRetryDelay(); 489 } 490 } 491 } 492 493 private final PnoScanListener mPnoScanListener = new PnoScanListener(); 494 495 /** 496 * WifiConnectivityManager constructor 497 */ 498 WifiConnectivityManager(Context context, WifiStateMachine stateMachine, 499 WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo, 500 WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, 501 WifiLastResortWatchdog wifiLastResortWatchdog, WifiMetrics wifiMetrics, 502 Looper looper, Clock clock, boolean enable, FrameworkFacade frameworkFacade, 503 SavedNetworkEvaluator savedNetworkEvaluator, 504 RecommendedNetworkEvaluator recommendedNetworkEvaluator, 505 PasspointNetworkEvaluator passpointNetworkEvaluator) { 506 mStateMachine = stateMachine; 507 mScanner = scanner; 508 mConfigManager = configManager; 509 mWifiInfo = wifiInfo; 510 mNetworkSelector = networkSelector; 511 mConnectivityHelper = connectivityHelper; 512 mLocalLog = networkSelector.getLocalLog(); 513 mWifiLastResortWatchdog = wifiLastResortWatchdog; 514 mWifiMetrics = wifiMetrics; 515 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 516 mEventHandler = new Handler(looper); 517 mClock = clock; 518 mConnectionAttemptTimeStamps = new LinkedList<>(); 519 520 mMin5GHzRssi = context.getResources().getInteger( 521 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); 522 mMin24GHzRssi = context.getResources().getInteger( 523 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); 524 mBand5GHzBonus = context.getResources().getInteger( 525 R.integer.config_wifi_framework_5GHz_preference_boost_factor); 526 mCurrentConnectionBonus = context.getResources().getInteger( 527 R.integer.config_wifi_framework_current_network_boost); 528 mSameNetworkBonus = context.getResources().getInteger( 529 R.integer.config_wifi_framework_SAME_BSSID_AWARD); 530 mSecureBonus = context.getResources().getInteger( 531 R.integer.config_wifi_framework_SECURITY_AWARD); 532 int thresholdSaturatedRssi24 = context.getResources().getInteger( 533 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); 534 mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( 535 R.bool.config_wifi_framework_enable_associated_network_selection); 536 mInitialScoreMax = (context.getResources().getInteger( 537 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz) 538 + context.getResources().getInteger( 539 R.integer.config_wifi_framework_RSSI_SCORE_OFFSET)) 540 * context.getResources().getInteger( 541 R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); 542 543 localLog("PNO settings:" + " min5GHzRssi " + mMin5GHzRssi 544 + " min24GHzRssi " + mMin24GHzRssi 545 + " currentConnectionBonus " + mCurrentConnectionBonus 546 + " sameNetworkBonus " + mSameNetworkBonus 547 + " secureNetworkBonus " + mSecureBonus 548 + " initialScoreMax " + mInitialScoreMax); 549 550 boolean hs2Enabled = context.getResources().getBoolean( 551 R.bool.config_wifi_hotspot2_enabled); 552 localLog("Passpoint is: " + (hs2Enabled ? "enabled" : "disabled")); 553 554 // Register the network evaluators 555 mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator, 556 SAVED_NETWORK_EVALUATOR_PRIORITY); 557 if (hs2Enabled) { 558 mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator, 559 PASSPOINT_NETWORK_EVALUATOR_PRIORITY); 560 } 561 mNetworkSelector.registerNetworkEvaluator(recommendedNetworkEvaluator, 562 RECOMMENDED_NETWORK_EVALUATOR_PRIORITY); 563 564 // Register for all single scan results 565 mScanner.registerScanListener(mAllSingleScanListener); 566 567 mWifiConnectivityManagerEnabled = enable; 568 569 localLog("ConnectivityScanManager initialized and " 570 + (enable ? "enabled" : "disabled")); 571 } 572 573 /** 574 * This checks the connection attempt rate and recommends whether the connection attempt 575 * should be skipped or not. This attempts to rate limit the rate of connections to 576 * prevent us from flapping between networks and draining battery rapidly. 577 */ 578 private boolean shouldSkipConnectionAttempt(Long timeMillis) { 579 Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator(); 580 // First evict old entries from the queue. 581 while (attemptIter.hasNext()) { 582 Long connectionAttemptTimeMillis = attemptIter.next(); 583 if ((timeMillis - connectionAttemptTimeMillis) 584 > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) { 585 attemptIter.remove(); 586 } else { 587 // This list is sorted by timestamps, so we can skip any more checks 588 break; 589 } 590 } 591 // If we've reached the max connection attempt rate, skip this connection attempt 592 return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE); 593 } 594 595 /** 596 * Add the current connection attempt timestamp to our queue of connection attempts. 597 */ 598 private void noteConnectionAttempt(Long timeMillis) { 599 mConnectionAttemptTimeStamps.addLast(timeMillis); 600 } 601 602 /** 603 * This is used to clear the connection attempt rate limiter. This is done when the user 604 * explicitly tries to connect to a specified network. 605 */ 606 private void clearConnectionAttemptTimeStamps() { 607 mConnectionAttemptTimeStamps.clear(); 608 } 609 610 /** 611 * Attempt to connect to a network candidate. 612 * 613 * Based on the currently connected network, this menthod determines whether we should 614 * connect or roam to the network candidate recommended by WifiNetworkSelector. 615 */ 616 private void connectToNetwork(WifiConfiguration candidate) { 617 ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate(); 618 if (scanResultCandidate == null) { 619 localLog("connectToNetwork: bad candidate - " + candidate 620 + " scanResult: " + scanResultCandidate); 621 return; 622 } 623 624 String targetBssid = scanResultCandidate.BSSID; 625 String targetAssociationId = candidate.SSID + " : " + targetBssid; 626 627 // Check if we are already connected or in the process of connecting to the target 628 // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just 629 // in case the firmware automatically roamed to a BSSID different from what 630 // WifiNetworkSelector selected. 631 if (targetBssid != null 632 && (targetBssid.equals(mLastConnectionAttemptBssid) 633 || targetBssid.equals(mWifiInfo.getBSSID())) 634 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) { 635 localLog("connectToNetwork: Either already connected " 636 + "or is connecting to " + targetAssociationId); 637 return; 638 } 639 640 if (candidate.BSSID != null 641 && !candidate.BSSID.equals(WifiStateMachine.SUPPLICANT_BSSID_ANY) 642 && !candidate.BSSID.equals(targetBssid)) { 643 localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the " 644 + "config specified BSSID " + candidate.BSSID + ". Drop it!"); 645 return; 646 } 647 648 Long elapsedTimeMillis = mClock.getElapsedSinceBootMillis(); 649 if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) { 650 localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!"); 651 mTotalConnectivityAttemptsRateLimited++; 652 return; 653 } 654 noteConnectionAttempt(elapsedTimeMillis); 655 656 mLastConnectionAttemptBssid = targetBssid; 657 658 WifiConfiguration currentConnectedNetwork = mConfigManager 659 .getConfiguredNetwork(mWifiInfo.getNetworkId()); 660 String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" : 661 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID()); 662 663 if (currentConnectedNetwork != null 664 && (currentConnectedNetwork.networkId == candidate.networkId 665 || currentConnectedNetwork.isLinked(candidate))) { 666 // Framework initiates roaming only if firmware doesn't support 667 // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}. 668 if (mConnectivityHelper.isFirmwareRoamingSupported()) { 669 // Keep this logging here for now to validate the firmware roaming behavior. 670 localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "." 671 + " The actual roaming target is up to the firmware."); 672 } else { 673 localLog("connectToNetwork: Roaming to " + targetAssociationId + " from " 674 + currentAssociationId); 675 mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate); 676 } 677 } else { 678 // Framework specifies the connection target BSSID if firmware doesn't support 679 // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the 680 // candidate configuration contains a specified BSSID. 681 if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null 682 || candidate.BSSID.equals(WifiStateMachine.SUPPLICANT_BSSID_ANY))) { 683 targetBssid = WifiStateMachine.SUPPLICANT_BSSID_ANY; 684 localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid 685 + " from " + currentAssociationId); 686 } else { 687 localLog("connectToNetwork: Connect to " + targetAssociationId + " from " 688 + currentAssociationId); 689 } 690 mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid); 691 } 692 } 693 694 // Helper for selecting the band for connectivity scan 695 private int getScanBand() { 696 return getScanBand(true); 697 } 698 699 private int getScanBand(boolean isFullBandScan) { 700 if (isFullBandScan) { 701 return WifiScanner.WIFI_BAND_BOTH_WITH_DFS; 702 } else { 703 // Use channel list instead. 704 return WifiScanner.WIFI_BAND_UNSPECIFIED; 705 } 706 } 707 708 // Helper for setting the channels for connectivity scan when band is unspecified. Returns 709 // false if we can't retrieve the info. 710 private boolean setScanChannels(ScanSettings settings) { 711 WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration(); 712 713 if (config == null) { 714 return false; 715 } 716 717 Set<Integer> freqs = 718 mConfigManager.fetchChannelSetForNetworkForPartialScan( 719 config.networkId, CHANNEL_LIST_AGE_MS, mWifiInfo.getFrequency()); 720 721 if (freqs != null && freqs.size() != 0) { 722 int index = 0; 723 settings.channels = new WifiScanner.ChannelSpec[freqs.size()]; 724 for (Integer freq : freqs) { 725 settings.channels[index++] = new WifiScanner.ChannelSpec(freq); 726 } 727 return true; 728 } else { 729 localLog("No scan channels for " + config.configKey() + ". Perform full band scan"); 730 return false; 731 } 732 } 733 734 // Watchdog timer handler 735 private void watchdogHandler() { 736 localLog("watchdogHandler"); 737 738 // Schedule the next timer and start a single scan if we are in disconnected state. 739 // Otherwise, the watchdog timer will be scheduled when entering disconnected 740 // state. 741 if (mWifiState == WIFI_STATE_DISCONNECTED) { 742 localLog("start a single scan from watchdogHandler"); 743 744 scheduleWatchdogTimer(); 745 startSingleScan(true); 746 } 747 } 748 749 // Start a single scan and set up the interval for next single scan. 750 private void startPeriodicSingleScan() { 751 long currentTimeStamp = mClock.getElapsedSinceBootMillis(); 752 753 if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) { 754 long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp; 755 if (msSinceLastScan < PERIODIC_SCAN_INTERVAL_MS) { 756 localLog("Last periodic single scan started " + msSinceLastScan 757 + "ms ago, defer this new scan request."); 758 schedulePeriodicScanTimer(PERIODIC_SCAN_INTERVAL_MS - (int) msSinceLastScan); 759 return; 760 } 761 } 762 763 boolean isFullBandScan = true; 764 765 // If the WiFi traffic is heavy, only partial scan is initiated. 766 if (mWifiState == WIFI_STATE_CONNECTED 767 && (mWifiInfo.txSuccessRate > MAX_TX_PACKET_FOR_FULL_SCANS 768 || mWifiInfo.rxSuccessRate > MAX_RX_PACKET_FOR_FULL_SCANS)) { 769 localLog("No full band scan due to heavy traffic, txSuccessRate=" 770 + mWifiInfo.txSuccessRate + " rxSuccessRate=" 771 + mWifiInfo.rxSuccessRate); 772 isFullBandScan = false; 773 } 774 775 mLastPeriodicSingleScanTimeStamp = currentTimeStamp; 776 startSingleScan(isFullBandScan); 777 schedulePeriodicScanTimer(mPeriodicSingleScanInterval); 778 779 // Set up the next scan interval in an exponential backoff fashion. 780 mPeriodicSingleScanInterval *= 2; 781 if (mPeriodicSingleScanInterval > MAX_PERIODIC_SCAN_INTERVAL_MS) { 782 mPeriodicSingleScanInterval = MAX_PERIODIC_SCAN_INTERVAL_MS; 783 } 784 } 785 786 // Reset the last periodic single scan time stamp so that the next periodic single 787 // scan can start immediately. 788 private void resetLastPeriodicSingleScanTimeStamp() { 789 mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 790 } 791 792 // Periodic scan timer handler 793 private void periodicScanTimerHandler() { 794 localLog("periodicScanTimerHandler"); 795 796 // Schedule the next timer and start a single scan if screen is on. 797 if (mScreenOn) { 798 startPeriodicSingleScan(); 799 } 800 } 801 802 // Start a single scan 803 private void startSingleScan(boolean isFullBandScan) { 804 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 805 return; 806 } 807 808 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 809 810 ScanSettings settings = new ScanSettings(); 811 if (!isFullBandScan) { 812 if (!setScanChannels(settings)) { 813 isFullBandScan = true; 814 } 815 } 816 settings.band = getScanBand(isFullBandScan); 817 settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 818 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 819 settings.numBssidsPerScan = 0; 820 821 List<ScanSettings.HiddenNetwork> hiddenNetworkList = 822 mConfigManager.retrieveHiddenNetworkList(); 823 settings.hiddenNetworks = 824 hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]); 825 826 SingleScanListener singleScanListener = 827 new SingleScanListener(isFullBandScan); 828 mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE); 829 } 830 831 // Start a periodic scan when screen is on 832 private void startPeriodicScan(boolean scanImmediately) { 833 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 834 835 // No connectivity scan if auto roaming is disabled. 836 if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) { 837 return; 838 } 839 840 // Due to b/28020168, timer based single scan will be scheduled 841 // to provide periodic scan in an exponential backoff fashion. 842 if (scanImmediately) { 843 resetLastPeriodicSingleScanTimeStamp(); 844 } 845 mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; 846 startPeriodicSingleScan(); 847 } 848 849 // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected 850 private void startDisconnectedPnoScan() { 851 // TODO(b/29503772): Need to change this interface. 852 853 // Initialize PNO settings 854 PnoSettings pnoSettings = new PnoSettings(); 855 List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList(); 856 int listSize = pnoNetworkList.size(); 857 858 if (listSize == 0) { 859 // No saved network 860 localLog("No saved network for starting disconnected PNO."); 861 return; 862 } 863 864 pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize]; 865 pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList); 866 pnoSettings.min5GHzRssi = mMin5GHzRssi; 867 pnoSettings.min24GHzRssi = mMin24GHzRssi; 868 pnoSettings.initialScoreMax = mInitialScoreMax; 869 pnoSettings.currentConnectionBonus = mCurrentConnectionBonus; 870 pnoSettings.sameNetworkBonus = mSameNetworkBonus; 871 pnoSettings.secureBonus = mSecureBonus; 872 pnoSettings.band5GHzBonus = mBand5GHzBonus; 873 874 // Initialize scan settings 875 ScanSettings scanSettings = new ScanSettings(); 876 scanSettings.band = getScanBand(); 877 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; 878 scanSettings.numBssidsPerScan = 0; 879 scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS; 880 881 mPnoScanListener.clearScanDetails(); 882 883 mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener); 884 mPnoScanStarted = true; 885 } 886 887 // Stop PNO scan. 888 private void stopPnoScan() { 889 if (mPnoScanStarted) { 890 mScanner.stopPnoScan(mPnoScanListener); 891 } 892 893 mPnoScanStarted = false; 894 } 895 896 // Set up watchdog timer 897 private void scheduleWatchdogTimer() { 898 localLog("scheduleWatchdogTimer"); 899 900 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 901 mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS, 902 WATCHDOG_TIMER_TAG, 903 mWatchdogListener, mEventHandler); 904 } 905 906 // Set up periodic scan timer 907 private void schedulePeriodicScanTimer(int intervalMs) { 908 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 909 mClock.getElapsedSinceBootMillis() + intervalMs, 910 PERIODIC_SCAN_TIMER_TAG, 911 mPeriodicScanTimerListener, mEventHandler); 912 mPeriodicScanTimerSet = true; 913 } 914 915 // Cancel periodic scan timer 916 private void cancelPeriodicScanTimer() { 917 if (mPeriodicScanTimerSet) { 918 mAlarmManager.cancel(mPeriodicScanTimerListener); 919 mPeriodicScanTimerSet = false; 920 } 921 } 922 923 // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS 924 private void scheduleDelayedSingleScan(boolean isFullBandScan) { 925 localLog("scheduleDelayedSingleScan"); 926 927 RestartSingleScanListener restartSingleScanListener = 928 new RestartSingleScanListener(isFullBandScan); 929 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 930 mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS, 931 RESTART_SINGLE_SCAN_TIMER_TAG, 932 restartSingleScanListener, mEventHandler); 933 } 934 935 // Set up timer to start a delayed scan after msFromNow milli-seconds 936 private void scheduleDelayedConnectivityScan(int msFromNow) { 937 localLog("scheduleDelayedConnectivityScan"); 938 939 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 940 mClock.getElapsedSinceBootMillis() + msFromNow, 941 RESTART_CONNECTIVITY_SCAN_TIMER_TAG, 942 mRestartScanListener, mEventHandler); 943 944 } 945 946 // Start a connectivity scan. The scan method is chosen according to 947 // the current screen state and WiFi state. 948 private void startConnectivityScan(boolean scanImmediately) { 949 localLog("startConnectivityScan: screenOn=" + mScreenOn 950 + " wifiState=" + mWifiState 951 + " scanImmediately=" + scanImmediately 952 + " wifiEnabled=" + mWifiEnabled 953 + " wifiConnectivityManagerEnabled=" 954 + mWifiConnectivityManagerEnabled); 955 956 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 957 return; 958 } 959 960 // Always stop outstanding connecivity scan if there is any 961 stopConnectivityScan(); 962 963 // Don't start a connectivity scan while Wifi is in the transition 964 // between connected and disconnected states. 965 if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) { 966 return; 967 } 968 969 if (mScreenOn) { 970 startPeriodicScan(scanImmediately); 971 } else { 972 if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) { 973 startDisconnectedPnoScan(); 974 } 975 } 976 977 } 978 979 // Stop connectivity scan if there is any. 980 private void stopConnectivityScan() { 981 // Due to b/28020168, timer based single scan will be scheduled 982 // to provide periodic scan in an exponential backoff fashion. 983 cancelPeriodicScanTimer(); 984 stopPnoScan(); 985 mScanRestartCount = 0; 986 } 987 988 /** 989 * Handler for screen state (on/off) changes 990 */ 991 public void handleScreenStateChanged(boolean screenOn) { 992 localLog("handleScreenStateChanged: screenOn=" + screenOn); 993 994 mScreenOn = screenOn; 995 996 startConnectivityScan(SCAN_ON_SCHEDULE); 997 } 998 999 /** 1000 * Handler for WiFi state (connected/disconnected) changes 1001 */ 1002 public void handleConnectionStateChanged(int state) { 1003 localLog("handleConnectionStateChanged: state=" + state); 1004 1005 mWifiState = state; 1006 1007 // Reset BSSID of last connection attempt and kick off 1008 // the watchdog timer if entering disconnected state. 1009 if (mWifiState == WIFI_STATE_DISCONNECTED) { 1010 mLastConnectionAttemptBssid = null; 1011 scheduleWatchdogTimer(); 1012 startConnectivityScan(SCAN_IMMEDIATELY); 1013 } else { 1014 startConnectivityScan(SCAN_ON_SCHEDULE); 1015 } 1016 } 1017 1018 /** 1019 * Handler when user toggles whether untrusted connection is allowed 1020 */ 1021 public void setUntrustedConnectionAllowed(boolean allowed) { 1022 localLog("setUntrustedConnectionAllowed: allowed=" + allowed); 1023 1024 if (mUntrustedConnectionAllowed != allowed) { 1025 mUntrustedConnectionAllowed = allowed; 1026 startConnectivityScan(SCAN_IMMEDIATELY); 1027 } 1028 } 1029 1030 /** 1031 * Handler when user specifies a particular network to connect to 1032 */ 1033 public void setUserConnectChoice(int netId) { 1034 localLog("setUserConnectChoice: netId=" + netId); 1035 1036 mNetworkSelector.setUserConnectChoice(netId); 1037 } 1038 1039 /** 1040 * Handler to prepare for connection to a user or app specified network 1041 */ 1042 public void prepareForForcedConnection(int netId) { 1043 localLog("prepareForForcedConnection: netId=" + netId); 1044 1045 clearConnectionAttemptTimeStamps(); 1046 clearBssidBlacklist(); 1047 } 1048 1049 /** 1050 * Handler for on-demand connectivity scan 1051 */ 1052 public void forceConnectivityScan() { 1053 localLog("forceConnectivityScan"); 1054 1055 mWaitForFullBandScanResults = true; 1056 startSingleScan(true); 1057 } 1058 1059 /** 1060 * Update the BSSID blacklist when a BSSID is enabled or disabled 1061 * 1062 * @param bssid the bssid to be enabled/disabled 1063 * @param enable -- true enable the bssid 1064 * -- false disable the bssid 1065 * @param reasonCode enable/disable reason code 1066 * @return true if blacklist is updated; false otherwise 1067 */ 1068 private boolean updateBssidBlacklist(String bssid, boolean enable, int reasonCode) { 1069 // Remove the bssid from blacklist when it is enabled. 1070 if (enable) { 1071 return mBssidBlacklist.remove(bssid) != null; 1072 } 1073 1074 // Update the bssid's blacklist status when it is disabled because of 1075 // association rejection. 1076 BssidBlacklistStatus status = mBssidBlacklist.get(bssid); 1077 if (status == null) { 1078 // First time for this BSSID 1079 status = new BssidBlacklistStatus(); 1080 mBssidBlacklist.put(bssid, status); 1081 } 1082 1083 status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis(); 1084 status.counter++; 1085 if (!status.isBlacklisted) { 1086 if (status.counter >= BSSID_BLACKLIST_THRESHOLD 1087 || reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) { 1088 status.isBlacklisted = true; 1089 return true; 1090 } 1091 } 1092 return false; 1093 } 1094 1095 /** 1096 * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector 1097 * 1098 * @param bssid the bssid to be enabled/disabled 1099 * @param enable -- true enable the bssid 1100 * -- false disable the bssid 1101 * @param reasonCode enable/disable reason code 1102 * @return true if blacklist is updated; false otherwise 1103 */ 1104 public boolean trackBssid(String bssid, boolean enable, int reasonCode) { 1105 localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid + " reason code " 1106 + reasonCode); 1107 1108 if (bssid == null) { 1109 return false; 1110 } 1111 1112 if (!updateBssidBlacklist(bssid, enable, reasonCode)) { 1113 return false; 1114 } 1115 1116 // Blacklist was updated, so update firmware roaming configuration. 1117 updateFirmwareRoamingConfiguration(); 1118 1119 if (!enable) { 1120 // Disabling a BSSID can happen when connection to the AP was rejected. 1121 // We start another scan immediately so that WifiNetworkSelector can 1122 // give us another candidate to connect to. 1123 startConnectivityScan(SCAN_IMMEDIATELY); 1124 } 1125 1126 return true; 1127 } 1128 1129 /** 1130 * Check whether a bssid is disabled 1131 */ 1132 @VisibleForTesting 1133 public boolean isBssidDisabled(String bssid) { 1134 BssidBlacklistStatus status = mBssidBlacklist.get(bssid); 1135 return status == null ? false : status.isBlacklisted; 1136 } 1137 1138 /** 1139 * Compile and return a hashset of the blacklisted BSSIDs 1140 */ 1141 private HashSet<String> buildBssidBlacklist() { 1142 HashSet<String> blacklistedBssids = new HashSet<String>(); 1143 for (String bssid : mBssidBlacklist.keySet()) { 1144 if (isBssidDisabled(bssid)) { 1145 blacklistedBssids.add(bssid); 1146 } 1147 } 1148 1149 return blacklistedBssids; 1150 } 1151 1152 /** 1153 * Update firmware roaming configuration if the firmware roaming feature is supported. 1154 * Compile and write the BSSID blacklist only. TODO(b/36488259): SSID whitelist is always 1155 * empty for now. 1156 */ 1157 private void updateFirmwareRoamingConfiguration() { 1158 if (!mConnectivityHelper.isFirmwareRoamingSupported()) { 1159 return; 1160 } 1161 1162 int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid(); 1163 if (maxBlacklistSize <= 0) { 1164 Log.wtf(TAG, "Invalid max BSSID blacklist size: " + maxBlacklistSize); 1165 return; 1166 } 1167 1168 ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist()); 1169 int blacklistSize = blacklistedBssids.size(); 1170 1171 if (blacklistSize > maxBlacklistSize) { 1172 Log.wtf(TAG, "Attempt to write " + blacklistSize + " blacklisted BSSIDs, max size is " 1173 + maxBlacklistSize); 1174 1175 blacklistedBssids = new ArrayList<String>(blacklistedBssids.subList(0, 1176 maxBlacklistSize)); 1177 localLog("Trim down BSSID blacklist size from " + blacklistSize + " to " 1178 + blacklistedBssids.size()); 1179 } 1180 1181 if (!mConnectivityHelper.setFirmwareRoamingConfiguration(blacklistedBssids, 1182 new ArrayList<String>())) { // TODO(b/36488259): SSID whitelist management. 1183 localLog("Failed to set firmware roaming configuration."); 1184 } 1185 } 1186 1187 /** 1188 * Refresh the BSSID blacklist 1189 * 1190 * Go through the BSSID blacklist and check if a BSSID has been blacklisted for 1191 * BSSID_BLACKLIST_EXPIRE_TIME_MS. If yes, re-enable it. 1192 */ 1193 private void refreshBssidBlacklist() { 1194 if (mBssidBlacklist.isEmpty()) { 1195 return; 1196 } 1197 1198 boolean updated = false; 1199 Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator(); 1200 Long currentTimeStamp = mClock.getElapsedSinceBootMillis(); 1201 1202 while (iter.hasNext()) { 1203 BssidBlacklistStatus status = iter.next(); 1204 if (status.isBlacklisted && ((currentTimeStamp - status.blacklistedTimeStamp) 1205 >= BSSID_BLACKLIST_EXPIRE_TIME_MS)) { 1206 iter.remove(); 1207 updated = true; 1208 } 1209 } 1210 1211 if (updated) { 1212 updateFirmwareRoamingConfiguration(); 1213 } 1214 } 1215 1216 /** 1217 * Clear the BSSID blacklist 1218 */ 1219 private void clearBssidBlacklist() { 1220 mBssidBlacklist.clear(); 1221 updateFirmwareRoamingConfiguration(); 1222 } 1223 1224 /** 1225 * Start WifiConnectivityManager 1226 */ 1227 private void start() { 1228 mConnectivityHelper.getFirmwareRoamingInfo(); 1229 clearBssidBlacklist(); 1230 startConnectivityScan(SCAN_IMMEDIATELY); 1231 } 1232 1233 /** 1234 * Stop and reset WifiConnectivityManager 1235 */ 1236 private void stop() { 1237 stopConnectivityScan(); 1238 clearBssidBlacklist(); 1239 resetLastPeriodicSingleScanTimeStamp(); 1240 mLastConnectionAttemptBssid = null; 1241 mWaitForFullBandScanResults = false; 1242 } 1243 1244 /** 1245 * Update WifiConnectivityManager running state 1246 * 1247 * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager 1248 * are enabled, otherwise stop it. 1249 */ 1250 private void updateRunningState() { 1251 if (mWifiEnabled && mWifiConnectivityManagerEnabled) { 1252 localLog("Starting up WifiConnectivityManager"); 1253 start(); 1254 } else { 1255 localLog("Stopping WifiConnectivityManager"); 1256 stop(); 1257 } 1258 } 1259 1260 /** 1261 * Inform WiFi is enabled for connection or not 1262 */ 1263 public void setWifiEnabled(boolean enable) { 1264 localLog("Set WiFi " + (enable ? "enabled" : "disabled")); 1265 1266 mWifiEnabled = enable; 1267 updateRunningState(); 1268 1269 } 1270 1271 /** 1272 * Turn on/off the WifiConnectivityManager at runtime 1273 */ 1274 public void enable(boolean enable) { 1275 localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled")); 1276 1277 mWifiConnectivityManagerEnabled = enable; 1278 updateRunningState(); 1279 } 1280 1281 @VisibleForTesting 1282 int getLowRssiNetworkRetryDelay() { 1283 return mPnoScanListener.getLowRssiNetworkRetryDelay(); 1284 } 1285 1286 @VisibleForTesting 1287 long getLastPeriodicSingleScanTimeStamp() { 1288 return mLastPeriodicSingleScanTimeStamp; 1289 } 1290 1291 /** 1292 * Dump the local logs. 1293 * 1294 * Note: this call temporarily calls in to NetworkSelector to dump the LocalLog. This should be 1295 * refactored to dump from WifiConnectivityManager instead. 1296 */ 1297 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1298 mNetworkSelector.dump(fd, pw, args); 1299 } 1300} 1301