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