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