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 // retrieve the list of hidden network SSIDs from saved network to scan for 920 List<ScanSettings.HiddenNetwork> hiddenNetworkList = 921 new ArrayList<>(mConfigManager.retrieveHiddenNetworkList()); 922 // retrieve the list of hidden network SSIDs from Network suggestion to scan for 923 hiddenNetworkList.addAll( 924 mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList()); 925 settings.hiddenNetworks = 926 hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[0]); 927 928 SingleScanListener singleScanListener = 929 new SingleScanListener(isFullBandScan); 930 mScanner.startScan(settings, singleScanListener, workSource); 931 mWifiMetrics.incrementConnectivityOneshotScanCount(); 932 } 933 934 // Start a periodic scan when screen is on startPeriodicScan(boolean scanImmediately)935 private void startPeriodicScan(boolean scanImmediately) { 936 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 937 938 // No connectivity scan if auto roaming is disabled. 939 if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) { 940 return; 941 } 942 943 // Due to b/28020168, timer based single scan will be scheduled 944 // to provide periodic scan in an exponential backoff fashion. 945 if (scanImmediately) { 946 resetLastPeriodicSingleScanTimeStamp(); 947 } 948 mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; 949 startPeriodicSingleScan(); 950 } 951 deviceMobilityStateToPnoScanIntervalMs(@eviceMobilityState int state)952 private static int deviceMobilityStateToPnoScanIntervalMs(@DeviceMobilityState int state) { 953 switch (state) { 954 case WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN: 955 case WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT: 956 case WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT: 957 return MOVING_PNO_SCAN_INTERVAL_MS; 958 case WifiManager.DEVICE_MOBILITY_STATE_STATIONARY: 959 return STATIONARY_PNO_SCAN_INTERVAL_MS; 960 default: 961 return -1; 962 } 963 } 964 965 /** 966 * Alters the PNO scan interval based on the current device mobility state. 967 * If the device is stationary, it will likely not find many new Wifi networks. Thus, increase 968 * the interval between scans. Decrease the interval between scans if the device begins to move 969 * again. 970 * @param newState the new device mobility state 971 */ setDeviceMobilityState(@eviceMobilityState int newState)972 public void setDeviceMobilityState(@DeviceMobilityState int newState) { 973 int newPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(newState); 974 if (newPnoScanIntervalMs < 0) { 975 Log.e(TAG, "Invalid device mobility state: " + newState); 976 return; 977 } 978 979 if (newPnoScanIntervalMs == mPnoScanIntervalMs) { 980 if (mPnoScanStarted) { 981 mWifiMetrics.logPnoScanStop(); 982 mWifiMetrics.enterDeviceMobilityState(newState); 983 mWifiMetrics.logPnoScanStart(); 984 } else { 985 mWifiMetrics.enterDeviceMobilityState(newState); 986 } 987 } else { 988 mPnoScanIntervalMs = newPnoScanIntervalMs; 989 Log.d(TAG, "PNO Scan Interval changed to " + mPnoScanIntervalMs + " ms."); 990 991 if (mPnoScanStarted) { 992 Log.d(TAG, "Restarting PNO Scan with new scan interval"); 993 stopPnoScan(); 994 mWifiMetrics.enterDeviceMobilityState(newState); 995 startDisconnectedPnoScan(); 996 } else { 997 mWifiMetrics.enterDeviceMobilityState(newState); 998 } 999 } 1000 } 1001 1002 // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected startDisconnectedPnoScan()1003 private void startDisconnectedPnoScan() { 1004 // Initialize PNO settings 1005 PnoSettings pnoSettings = new PnoSettings(); 1006 List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList(); 1007 int listSize = pnoNetworkList.size(); 1008 1009 if (listSize == 0) { 1010 // No saved network 1011 localLog("No saved network for starting disconnected PNO."); 1012 return; 1013 } 1014 1015 pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize]; 1016 pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList); 1017 pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScoringParams.BAND5); 1018 pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi(ScoringParams.BAND2); 1019 pnoSettings.initialScoreMax = initialScoreMax(); 1020 pnoSettings.currentConnectionBonus = mCurrentConnectionBonus; 1021 pnoSettings.sameNetworkBonus = mSameNetworkBonus; 1022 pnoSettings.secureBonus = mSecureBonus; 1023 pnoSettings.band5GHzBonus = mBand5GHzBonus; 1024 1025 // Initialize scan settings 1026 ScanSettings scanSettings = new ScanSettings(); 1027 scanSettings.band = getScanBand(); 1028 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; 1029 scanSettings.numBssidsPerScan = 0; 1030 scanSettings.periodInMs = mPnoScanIntervalMs; 1031 1032 mPnoScanListener.clearScanDetails(); 1033 1034 mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener); 1035 mPnoScanStarted = true; 1036 mWifiMetrics.logPnoScanStart(); 1037 } 1038 1039 // Stop PNO scan. stopPnoScan()1040 private void stopPnoScan() { 1041 if (!mPnoScanStarted) return; 1042 1043 mScanner.stopPnoScan(mPnoScanListener); 1044 mPnoScanStarted = false; 1045 mWifiMetrics.logPnoScanStop(); 1046 } 1047 1048 // Set up watchdog timer scheduleWatchdogTimer()1049 private void scheduleWatchdogTimer() { 1050 localLog("scheduleWatchdogTimer"); 1051 1052 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1053 mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS, 1054 WATCHDOG_TIMER_TAG, 1055 mWatchdogListener, mEventHandler); 1056 } 1057 1058 // Set up periodic scan timer schedulePeriodicScanTimer(int intervalMs)1059 private void schedulePeriodicScanTimer(int intervalMs) { 1060 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1061 mClock.getElapsedSinceBootMillis() + intervalMs, 1062 PERIODIC_SCAN_TIMER_TAG, 1063 mPeriodicScanTimerListener, mEventHandler); 1064 mPeriodicScanTimerSet = true; 1065 } 1066 1067 // Cancel periodic scan timer cancelPeriodicScanTimer()1068 private void cancelPeriodicScanTimer() { 1069 if (mPeriodicScanTimerSet) { 1070 mAlarmManager.cancel(mPeriodicScanTimerListener); 1071 mPeriodicScanTimerSet = false; 1072 } 1073 } 1074 1075 // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS scheduleDelayedSingleScan(boolean isFullBandScan)1076 private void scheduleDelayedSingleScan(boolean isFullBandScan) { 1077 localLog("scheduleDelayedSingleScan"); 1078 1079 RestartSingleScanListener restartSingleScanListener = 1080 new RestartSingleScanListener(isFullBandScan); 1081 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1082 mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS, 1083 RESTART_SINGLE_SCAN_TIMER_TAG, 1084 restartSingleScanListener, mEventHandler); 1085 } 1086 1087 // Set up timer to start a delayed scan after msFromNow milli-seconds scheduleDelayedConnectivityScan(int msFromNow)1088 private void scheduleDelayedConnectivityScan(int msFromNow) { 1089 localLog("scheduleDelayedConnectivityScan"); 1090 1091 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1092 mClock.getElapsedSinceBootMillis() + msFromNow, 1093 RESTART_CONNECTIVITY_SCAN_TIMER_TAG, 1094 mRestartScanListener, mEventHandler); 1095 1096 } 1097 1098 // Start a connectivity scan. The scan method is chosen according to 1099 // the current screen state and WiFi state. startConnectivityScan(boolean scanImmediately)1100 private void startConnectivityScan(boolean scanImmediately) { 1101 localLog("startConnectivityScan: screenOn=" + mScreenOn 1102 + " wifiState=" + stateToString(mWifiState) 1103 + " scanImmediately=" + scanImmediately 1104 + " wifiEnabled=" + mWifiEnabled 1105 + " wifiConnectivityManagerEnabled=" 1106 + mWifiConnectivityManagerEnabled); 1107 1108 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 1109 return; 1110 } 1111 1112 // Always stop outstanding connecivity scan if there is any 1113 stopConnectivityScan(); 1114 1115 // Don't start a connectivity scan while Wifi is in the transition 1116 // between connected and disconnected states. 1117 if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) { 1118 return; 1119 } 1120 1121 if (mScreenOn) { 1122 startPeriodicScan(scanImmediately); 1123 } else { 1124 if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) { 1125 startDisconnectedPnoScan(); 1126 } 1127 } 1128 1129 } 1130 1131 // Stop connectivity scan if there is any. stopConnectivityScan()1132 private void stopConnectivityScan() { 1133 // Due to b/28020168, timer based single scan will be scheduled 1134 // to provide periodic scan in an exponential backoff fashion. 1135 cancelPeriodicScanTimer(); 1136 stopPnoScan(); 1137 mScanRestartCount = 0; 1138 } 1139 1140 /** 1141 * Handler for screen state (on/off) changes 1142 */ handleScreenStateChanged(boolean screenOn)1143 public void handleScreenStateChanged(boolean screenOn) { 1144 localLog("handleScreenStateChanged: screenOn=" + screenOn); 1145 1146 mScreenOn = screenOn; 1147 1148 mOpenNetworkNotifier.handleScreenStateChanged(screenOn); 1149 mCarrierNetworkNotifier.handleScreenStateChanged(screenOn); 1150 1151 startConnectivityScan(SCAN_ON_SCHEDULE); 1152 } 1153 1154 /** 1155 * Helper function that converts the WIFI_STATE_XXX constants to string 1156 */ stateToString(int state)1157 private static String stateToString(int state) { 1158 switch (state) { 1159 case WIFI_STATE_CONNECTED: 1160 return "connected"; 1161 case WIFI_STATE_DISCONNECTED: 1162 return "disconnected"; 1163 case WIFI_STATE_TRANSITIONING: 1164 return "transitioning"; 1165 default: 1166 return "unknown"; 1167 } 1168 } 1169 1170 /** 1171 * Handler for WiFi state (connected/disconnected) changes 1172 */ handleConnectionStateChanged(int state)1173 public void handleConnectionStateChanged(int state) { 1174 localLog("handleConnectionStateChanged: state=" + stateToString(state)); 1175 1176 mWifiState = state; 1177 1178 // Reset BSSID of last connection attempt and kick off 1179 // the watchdog timer if entering disconnected state. 1180 if (mWifiState == WIFI_STATE_DISCONNECTED) { 1181 mLastConnectionAttemptBssid = null; 1182 scheduleWatchdogTimer(); 1183 startConnectivityScan(SCAN_IMMEDIATELY); 1184 } else { 1185 startConnectivityScan(SCAN_ON_SCHEDULE); 1186 } 1187 } 1188 1189 /** 1190 * Handler when a WiFi connection attempt ended. 1191 * 1192 * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code. 1193 */ handleConnectionAttemptEnded(int failureCode)1194 public void handleConnectionAttemptEnded(int failureCode) { 1195 if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) { 1196 String ssid = (mWifiInfo.getWifiSsid() == null) 1197 ? null 1198 : mWifiInfo.getWifiSsid().toString(); 1199 mOpenNetworkNotifier.handleWifiConnected(ssid); 1200 mCarrierNetworkNotifier.handleWifiConnected(ssid); 1201 } else { 1202 mOpenNetworkNotifier.handleConnectionFailure(); 1203 mCarrierNetworkNotifier.handleConnectionFailure(); 1204 } 1205 } 1206 1207 // Enable auto-join if we have any pending network request (trusted or untrusted) and no 1208 // specific network request in progress. checkStateAndEnable()1209 private void checkStateAndEnable() { 1210 enable(!mSpecificNetworkRequestInProgress 1211 && (mUntrustedConnectionAllowed || mTrustedConnectionAllowed)); 1212 startConnectivityScan(SCAN_IMMEDIATELY); 1213 } 1214 1215 /** 1216 * Triggered when {@link WifiNetworkFactory} has a pending general network request. 1217 */ setTrustedConnectionAllowed(boolean allowed)1218 public void setTrustedConnectionAllowed(boolean allowed) { 1219 localLog("setTrustedConnectionAllowed: allowed=" + allowed); 1220 1221 if (mTrustedConnectionAllowed != allowed) { 1222 mTrustedConnectionAllowed = allowed; 1223 checkStateAndEnable(); 1224 } 1225 } 1226 1227 1228 /** 1229 * Triggered when {@link UntrustedWifiNetworkFactory} has a pending ephemeral network request. 1230 */ setUntrustedConnectionAllowed(boolean allowed)1231 public void setUntrustedConnectionAllowed(boolean allowed) { 1232 localLog("setUntrustedConnectionAllowed: allowed=" + allowed); 1233 1234 if (mUntrustedConnectionAllowed != allowed) { 1235 mUntrustedConnectionAllowed = allowed; 1236 checkStateAndEnable(); 1237 } 1238 } 1239 1240 /** 1241 * Triggered when {@link WifiNetworkFactory} is processing a specific network request. 1242 */ setSpecificNetworkRequestInProgress(boolean inProgress)1243 public void setSpecificNetworkRequestInProgress(boolean inProgress) { 1244 localLog("setsetSpecificNetworkRequestInProgress : inProgress=" + inProgress); 1245 1246 if (mSpecificNetworkRequestInProgress != inProgress) { 1247 mSpecificNetworkRequestInProgress = inProgress; 1248 checkStateAndEnable(); 1249 } 1250 } 1251 1252 /** 1253 * Handler when user specifies a particular network to connect to 1254 */ setUserConnectChoice(int netId)1255 public void setUserConnectChoice(int netId) { 1256 localLog("setUserConnectChoice: netId=" + netId); 1257 1258 mNetworkSelector.setUserConnectChoice(netId); 1259 } 1260 1261 /** 1262 * Handler to prepare for connection to a user or app specified network 1263 */ prepareForForcedConnection(int netId)1264 public void prepareForForcedConnection(int netId) { 1265 localLog("prepareForForcedConnection: netId=" + netId); 1266 1267 clearConnectionAttemptTimeStamps(); 1268 clearBssidBlacklist(); 1269 } 1270 1271 /** 1272 * Handler for on-demand connectivity scan 1273 */ forceConnectivityScan(WorkSource workSource)1274 public void forceConnectivityScan(WorkSource workSource) { 1275 localLog("forceConnectivityScan in request of " + workSource); 1276 1277 mWaitForFullBandScanResults = true; 1278 startSingleScan(true, workSource); 1279 } 1280 1281 /** 1282 * Update the BSSID blacklist when a BSSID is enabled or disabled 1283 * 1284 * @param bssid the bssid to be enabled/disabled 1285 * @param enable -- true enable the bssid 1286 * -- false disable the bssid 1287 * @param reasonCode enable/disable reason code 1288 * @return true if blacklist is updated; false otherwise 1289 */ updateBssidBlacklist(String bssid, boolean enable, int reasonCode)1290 private boolean updateBssidBlacklist(String bssid, boolean enable, int reasonCode) { 1291 // Remove the bssid from blacklist when it is enabled. 1292 if (enable) { 1293 return mBssidBlacklist.remove(bssid) != null; 1294 } 1295 1296 // Do not update BSSID blacklist with information if this is the only 1297 // BSSID for its SSID. By ignoring it we will cause additional failures 1298 // which will trigger Watchdog. 1299 if (mWifiLastResortWatchdog.shouldIgnoreBssidUpdate(bssid)) { 1300 localLog("Ignore update Bssid Blacklist since Watchdog trigger is activated"); 1301 return false; 1302 } 1303 1304 // Update the bssid's blacklist status when it is disabled because of 1305 // association rejection. 1306 BssidBlacklistStatus status = mBssidBlacklist.get(bssid); 1307 if (status == null) { 1308 // First time for this BSSID 1309 status = new BssidBlacklistStatus(); 1310 mBssidBlacklist.put(bssid, status); 1311 } 1312 1313 status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis(); 1314 status.counter++; 1315 if (!status.isBlacklisted) { 1316 if (status.counter >= BSSID_BLACKLIST_THRESHOLD 1317 || reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) { 1318 status.isBlacklisted = true; 1319 return true; 1320 } 1321 } 1322 return false; 1323 } 1324 1325 /** 1326 * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector 1327 * 1328 * @param bssid the bssid to be enabled/disabled 1329 * @param enable -- true enable the bssid 1330 * -- false disable the bssid 1331 * @param reasonCode enable/disable reason code 1332 * @return true if blacklist is updated; false otherwise 1333 */ trackBssid(String bssid, boolean enable, int reasonCode)1334 public boolean trackBssid(String bssid, boolean enable, int reasonCode) { 1335 localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid + " reason code " 1336 + reasonCode); 1337 1338 if (bssid == null) { 1339 return false; 1340 } 1341 1342 if (!updateBssidBlacklist(bssid, enable, reasonCode)) { 1343 return false; 1344 } 1345 1346 // Blacklist was updated, so update firmware roaming configuration. 1347 updateFirmwareRoamingConfiguration(); 1348 1349 if (!enable) { 1350 // Disabling a BSSID can happen when connection to the AP was rejected. 1351 // We start another scan immediately so that WifiNetworkSelector can 1352 // give us another candidate to connect to. 1353 startConnectivityScan(SCAN_IMMEDIATELY); 1354 } 1355 1356 return true; 1357 } 1358 1359 /** 1360 * Check whether a bssid is disabled 1361 */ 1362 @VisibleForTesting isBssidDisabled(String bssid)1363 public boolean isBssidDisabled(String bssid) { 1364 BssidBlacklistStatus status = mBssidBlacklist.get(bssid); 1365 return status == null ? false : status.isBlacklisted; 1366 } 1367 1368 /** 1369 * Compile and return a hashset of the blacklisted BSSIDs 1370 */ buildBssidBlacklist()1371 private HashSet<String> buildBssidBlacklist() { 1372 HashSet<String> blacklistedBssids = new HashSet<String>(); 1373 for (String bssid : mBssidBlacklist.keySet()) { 1374 if (isBssidDisabled(bssid)) { 1375 blacklistedBssids.add(bssid); 1376 } 1377 } 1378 1379 return blacklistedBssids; 1380 } 1381 1382 /** 1383 * Update firmware roaming configuration if the firmware roaming feature is supported. 1384 * Compile and write the BSSID blacklist only. TODO(b/36488259): SSID whitelist is always 1385 * empty for now. 1386 */ updateFirmwareRoamingConfiguration()1387 private void updateFirmwareRoamingConfiguration() { 1388 if (!mConnectivityHelper.isFirmwareRoamingSupported()) { 1389 return; 1390 } 1391 1392 int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid(); 1393 if (maxBlacklistSize <= 0) { 1394 Log.wtf(TAG, "Invalid max BSSID blacklist size: " + maxBlacklistSize); 1395 return; 1396 } 1397 1398 ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist()); 1399 int blacklistSize = blacklistedBssids.size(); 1400 1401 if (blacklistSize > maxBlacklistSize) { 1402 Log.wtf(TAG, "Attempt to write " + blacklistSize + " blacklisted BSSIDs, max size is " 1403 + maxBlacklistSize); 1404 1405 blacklistedBssids = new ArrayList<String>(blacklistedBssids.subList(0, 1406 maxBlacklistSize)); 1407 localLog("Trim down BSSID blacklist size from " + blacklistSize + " to " 1408 + blacklistedBssids.size()); 1409 } 1410 1411 if (!mConnectivityHelper.setFirmwareRoamingConfiguration(blacklistedBssids, 1412 new ArrayList<String>())) { // TODO(b/36488259): SSID whitelist management. 1413 localLog("Failed to set firmware roaming configuration."); 1414 } 1415 } 1416 1417 /** 1418 * Refresh the BSSID blacklist 1419 * 1420 * Go through the BSSID blacklist and check if a BSSID has been blacklisted for 1421 * BSSID_BLACKLIST_EXPIRE_TIME_MS. If yes, re-enable it. 1422 */ refreshBssidBlacklist()1423 private void refreshBssidBlacklist() { 1424 if (mBssidBlacklist.isEmpty()) { 1425 return; 1426 } 1427 1428 boolean updated = false; 1429 Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator(); 1430 Long currentTimeStamp = mClock.getElapsedSinceBootMillis(); 1431 1432 while (iter.hasNext()) { 1433 BssidBlacklistStatus status = iter.next(); 1434 if (status.isBlacklisted && ((currentTimeStamp - status.blacklistedTimeStamp) 1435 >= BSSID_BLACKLIST_EXPIRE_TIME_MS)) { 1436 iter.remove(); 1437 updated = true; 1438 } 1439 } 1440 1441 if (updated) { 1442 updateFirmwareRoamingConfiguration(); 1443 } 1444 } 1445 1446 /** 1447 * Helper method to populate WifiScanner handle. This is done lazily because 1448 * WifiScanningService is started after WifiService. 1449 */ retrieveWifiScanner()1450 private void retrieveWifiScanner() { 1451 if (mScanner != null) return; 1452 mScanner = mWifiInjector.getWifiScanner(); 1453 checkNotNull(mScanner); 1454 // Register for all single scan results 1455 mScanner.registerScanListener(mAllSingleScanListener); 1456 } 1457 1458 1459 /** 1460 * Clear the BSSID blacklist 1461 */ clearBssidBlacklist()1462 private void clearBssidBlacklist() { 1463 mBssidBlacklist.clear(); 1464 updateFirmwareRoamingConfiguration(); 1465 } 1466 1467 /** 1468 * Start WifiConnectivityManager 1469 */ start()1470 private void start() { 1471 if (mRunning) return; 1472 retrieveWifiScanner(); 1473 mConnectivityHelper.getFirmwareRoamingInfo(); 1474 clearBssidBlacklist(); 1475 mRunning = true; 1476 } 1477 1478 /** 1479 * Stop and reset WifiConnectivityManager 1480 */ stop()1481 private void stop() { 1482 if (!mRunning) return; 1483 mRunning = false; 1484 stopConnectivityScan(); 1485 clearBssidBlacklist(); 1486 resetLastPeriodicSingleScanTimeStamp(); 1487 mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); 1488 mCarrierNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); 1489 mLastConnectionAttemptBssid = null; 1490 mWaitForFullBandScanResults = false; 1491 } 1492 1493 /** 1494 * Update WifiConnectivityManager running state 1495 * 1496 * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager 1497 * are enabled, otherwise stop it. 1498 */ updateRunningState()1499 private void updateRunningState() { 1500 if (mWifiEnabled && mWifiConnectivityManagerEnabled) { 1501 localLog("Starting up WifiConnectivityManager"); 1502 start(); 1503 } else { 1504 localLog("Stopping WifiConnectivityManager"); 1505 stop(); 1506 } 1507 } 1508 1509 /** 1510 * Inform WiFi is enabled for connection or not 1511 */ setWifiEnabled(boolean enable)1512 public void setWifiEnabled(boolean enable) { 1513 localLog("Set WiFi " + (enable ? "enabled" : "disabled")); 1514 1515 mWifiEnabled = enable; 1516 updateRunningState(); 1517 } 1518 1519 /** 1520 * Turn on/off the WifiConnectivityManager at runtime 1521 */ enable(boolean enable)1522 public void enable(boolean enable) { 1523 localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled")); 1524 1525 mWifiConnectivityManagerEnabled = enable; 1526 updateRunningState(); 1527 } 1528 1529 @VisibleForTesting getLowRssiNetworkRetryDelay()1530 int getLowRssiNetworkRetryDelay() { 1531 return mPnoScanListener.getLowRssiNetworkRetryDelay(); 1532 } 1533 1534 @VisibleForTesting getLastPeriodicSingleScanTimeStamp()1535 long getLastPeriodicSingleScanTimeStamp() { 1536 return mLastPeriodicSingleScanTimeStamp; 1537 } 1538 1539 /** 1540 * Dump the local logs. 1541 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)1542 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1543 pw.println("Dump of WifiConnectivityManager"); 1544 pw.println("WifiConnectivityManager - Log Begin ----"); 1545 mLocalLog.dump(fd, pw, args); 1546 pw.println("WifiConnectivityManager - Log End ----"); 1547 mOpenNetworkNotifier.dump(fd, pw, args); 1548 mCarrierNetworkNotifier.dump(fd, pw, args); 1549 mCarrierNetworkConfig.dump(fd, pw, args); 1550 } 1551 } 1552