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