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