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