WifiConnectivityManager.java revision f2a46f11dd92f2820e96b1b8b69b433012d6bcef
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi; 18 19import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; 20 21import android.app.AlarmManager; 22import android.content.Context; 23import android.net.wifi.ScanResult; 24import android.net.wifi.SupplicantState; 25import android.net.wifi.WifiConfiguration; 26import android.net.wifi.WifiInfo; 27import android.net.wifi.WifiScanner; 28import android.net.wifi.WifiScanner.PnoSettings; 29import android.net.wifi.WifiScanner.ScanSettings; 30import android.os.Handler; 31import android.os.Looper; 32import android.util.LocalLog; 33import android.util.Log; 34 35import com.android.internal.R; 36import com.android.internal.annotations.VisibleForTesting; 37import com.android.server.wifi.util.ScanResultUtil; 38 39import java.util.ArrayList; 40import java.util.Iterator; 41import java.util.LinkedList; 42import java.util.List; 43import java.util.Set; 44 45/** 46 * This class manages all the connectivity related scanning activities. 47 * 48 * When the screen is turned on or off, WiFi is connected or disconnected, 49 * or on-demand, a scan is initiatiated and the scan results are passed 50 * to WifiNetworkSelector for it to make a recommendation on which network 51 * to connect to. 52 */ 53public class WifiConnectivityManager { 54 public static final String WATCHDOG_TIMER_TAG = 55 "WifiConnectivityManager Schedule Watchdog Timer"; 56 public static final String PERIODIC_SCAN_TIMER_TAG = 57 "WifiConnectivityManager Schedule Periodic Scan Timer"; 58 public static final String RESTART_SINGLE_SCAN_TIMER_TAG = 59 "WifiConnectivityManager Restart Single Scan"; 60 public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG = 61 "WifiConnectivityManager Restart Scan"; 62 63 private static final String TAG = "WifiConnectivityManager"; 64 private static final long RESET_TIME_STAMP = Long.MIN_VALUE; 65 // Constants to indicate whether a scan should start immediately or 66 // it should comply to the minimum scan interval rule. 67 private static final boolean SCAN_IMMEDIATELY = true; 68 private static final boolean SCAN_ON_SCHEDULE = false; 69 // Periodic scan interval in milli-seconds. This is the scan 70 // performed when screen is on. 71 @VisibleForTesting 72 public static final int PERIODIC_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds 73 // When screen is on and WiFi traffic is heavy, exponential backoff 74 // connectivity scans are scheduled. This constant defines the maximum 75 // scan interval in this scenario. 76 @VisibleForTesting 77 public static final int MAX_PERIODIC_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds 78 // PNO scan interval in milli-seconds. This is the scan 79 // performed when screen is off and disconnected. 80 private static final int DISCONNECTED_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds 81 // PNO scan interval in milli-seconds. This is the scan 82 // performed when screen is off and connected. 83 private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds 84 // When a network is found by PNO scan but gets rejected by Wifi Network Selector due 85 // to its low RSSI value, scan will be reschduled in an exponential back off manner. 86 private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds 87 private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds 88 // Maximum number of retries when starting a scan failed 89 @VisibleForTesting 90 public static final int MAX_SCAN_RESTART_ALLOWED = 5; 91 // Number of milli-seconds to delay before retry starting 92 // a previously failed scan 93 private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds 94 // When in disconnected mode, a watchdog timer will be fired 95 // every WATCHDOG_INTERVAL_MS to start a single scan. This is 96 // to prevent caveat from things like PNO scan. 97 private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes 98 // Restricted channel list age out value. 99 private static final int CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour 100 // This is the time interval for the connection attempt rate calculation. Connection attempt 101 // timestamps beyond this interval is evicted from the list. 102 public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins 103 // Max number of connection attempts in the above time interval. 104 public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6; 105 // Packet tx/rx rates to determine if we want to do partial vs full scans. 106 // TODO(b/31180330): Make these device configs. 107 public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8; 108 public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16; 109 110 // WifiStateMachine has a bunch of states. From the 111 // WifiConnectivityManager's perspective it only cares 112 // if it is in Connected state, Disconnected state or in 113 // transition between these two states. 114 public static final int WIFI_STATE_UNKNOWN = 0; 115 public static final int WIFI_STATE_CONNECTED = 1; 116 public static final int WIFI_STATE_DISCONNECTED = 2; 117 public static final int WIFI_STATE_TRANSITIONING = 3; 118 119 // Saved network evaluator priority 120 private static final int SAVED_NETWORK_EVALUATOR_PRIORITY = 1; 121 private static final int EXTERNAL_SCORE_EVALUATOR_PRIORITY = 2; 122 123 private final WifiStateMachine mStateMachine; 124 private final WifiScanner mScanner; 125 private final WifiConfigManager mConfigManager; 126 private final WifiInfo mWifiInfo; 127 private final WifiNetworkSelector mNetworkSelector; 128 private final WifiLastResortWatchdog mWifiLastResortWatchdog; 129 private final WifiMetrics mWifiMetrics; 130 private final AlarmManager mAlarmManager; 131 private final Handler mEventHandler; 132 private final Clock mClock; 133 private final LocalLog mLocalLog; 134 private final LinkedList<Long> mConnectionAttemptTimeStamps; 135 136 private boolean mDbg = false; 137 private boolean mWifiEnabled = false; 138 private boolean mWifiConnectivityManagerEnabled = true; 139 private boolean mScreenOn = false; 140 private int mWifiState = WIFI_STATE_UNKNOWN; 141 private boolean mUntrustedConnectionAllowed = false; 142 private int mScanRestartCount = 0; 143 private int mSingleScanRestartCount = 0; 144 private int mTotalConnectivityAttemptsRateLimited = 0; 145 private String mLastConnectionAttemptBssid = null; 146 private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; 147 private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 148 private boolean mPnoScanStarted = false; 149 private boolean mPeriodicScanTimerSet = false; 150 // Device configs 151 private boolean mEnableAutoJoinWhenAssociated; 152 // PNO settings 153 private int mMin5GHzRssi; 154 private int mMin24GHzRssi; 155 private int mInitialScoreMax; 156 private int mCurrentConnectionBonus; 157 private int mSameNetworkBonus; 158 private int mSecureBonus; 159 private int mBand5GHzBonus; 160 161 // A helper to log debugging information in the local log buffer, which can 162 // be retrieved in bugreport. 163 private void localLog(String log) { 164 if (mLocalLog != null) { 165 mLocalLog.log(log); 166 } 167 } 168 169 // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 170 // if the start scan command failed. An timer is used here to make it a deferred retry. 171 private final AlarmManager.OnAlarmListener mRestartScanListener = 172 new AlarmManager.OnAlarmListener() { 173 public void onAlarm() { 174 startConnectivityScan(SCAN_IMMEDIATELY); 175 } 176 }; 177 178 // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 179 // if the start scan command failed. An timer is used here to make it a deferred retry. 180 private class RestartSingleScanListener implements AlarmManager.OnAlarmListener { 181 private final boolean mIsFullBandScan; 182 183 RestartSingleScanListener(boolean isFullBandScan) { 184 mIsFullBandScan = isFullBandScan; 185 } 186 187 @Override 188 public void onAlarm() { 189 startSingleScan(mIsFullBandScan); 190 } 191 } 192 193 // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS 194 // if it is in the WIFI_STATE_DISCONNECTED state. 195 private final AlarmManager.OnAlarmListener mWatchdogListener = 196 new AlarmManager.OnAlarmListener() { 197 public void onAlarm() { 198 watchdogHandler(); 199 } 200 }; 201 202 // Due to b/28020168, timer based single scan will be scheduled 203 // to provide periodic scan in an exponential backoff fashion. 204 private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener = 205 new AlarmManager.OnAlarmListener() { 206 public void onAlarm() { 207 periodicScanTimerHandler(); 208 } 209 }; 210 211 /** 212 * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener. 213 * Executes selection of potential network candidates, initiation of connection attempt to that 214 * network. 215 * 216 * @return true - if a candidate is selected by WifiNetworkSelector 217 * false - if no candidate is selected by WifiNetworkSelector 218 */ 219 private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) { 220 if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) { 221 localLog(listenerName + " onResults: No network selection because linkDebouncing is " 222 + mStateMachine.isLinkDebouncing() + " and supplicantTransient is " 223 + mStateMachine.isSupplicantTransientState()); 224 return false; 225 } 226 227 localLog(listenerName + " onResults: start network selection"); 228 229 WifiConfiguration candidate = 230 mNetworkSelector.selectNetwork(scanDetails, 231 mStateMachine.isConnected(), mStateMachine.isDisconnected(), 232 mUntrustedConnectionAllowed); 233 mWifiLastResortWatchdog.updateAvailableNetworks( 234 mNetworkSelector.getFilteredScanDetails()); 235 mWifiMetrics.countScanResults(scanDetails); 236 if (candidate != null) { 237 localLog(listenerName + ": WNS candidate-" + candidate.SSID); 238 connectToNetwork(candidate); 239 return true; 240 } else { 241 return false; 242 } 243 } 244 245 // All single scan results listener. 246 // 247 // Note: This is the listener for all the available single scan results, 248 // including the ones initiated by WifiConnectivityManager and 249 // other modules. 250 private class AllSingleScanListener implements WifiScanner.ScanListener { 251 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 252 253 public void clearScanDetails() { 254 mScanDetails.clear(); 255 } 256 257 @Override 258 public void onSuccess() { 259 localLog("registerScanListener onSuccess"); 260 } 261 262 @Override 263 public void onFailure(int reason, String description) { 264 Log.e(TAG, "registerScanListener onFailure:" 265 + " reason: " + reason 266 + " description: " + description); 267 } 268 269 @Override 270 public void onPeriodChanged(int periodInMs) { 271 } 272 273 @Override 274 public void onResults(WifiScanner.ScanData[] results) { 275 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 276 clearScanDetails(); 277 return; 278 } 279 280 boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener"); 281 clearScanDetails(); 282 283 // Update metrics to see if a single scan detected a valid network 284 // while PNO scan didn't. 285 // Note: We don't update the background scan metrics any more as it is 286 // not in use. 287 if (mPnoScanStarted) { 288 if (wasConnectAttempted) { 289 mWifiMetrics.incrementNumConnectivityWatchdogPnoBad(); 290 } else { 291 mWifiMetrics.incrementNumConnectivityWatchdogPnoGood(); 292 } 293 } 294 } 295 296 @Override 297 public void onFullResult(ScanResult fullScanResult) { 298 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 299 return; 300 } 301 302 if (mDbg) { 303 localLog("AllSingleScanListener onFullResult: " 304 + fullScanResult.SSID + " capabilities " 305 + fullScanResult.capabilities); 306 } 307 308 mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult)); 309 } 310 } 311 312 private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener(); 313 314 // Single scan results listener. A single scan is initiated when 315 // DisconnectedPNO scan found a valid network and woke up 316 // the system, or by the watchdog timer, or to form the timer based 317 // periodic scan. 318 // 319 // Note: This is the listener for the single scans initiated by the 320 // WifiConnectivityManager. 321 private class SingleScanListener implements WifiScanner.ScanListener { 322 private final boolean mIsFullBandScan; 323 324 SingleScanListener(boolean isFullBandScan) { 325 mIsFullBandScan = isFullBandScan; 326 } 327 328 @Override 329 public void onSuccess() { 330 localLog("SingleScanListener onSuccess"); 331 } 332 333 @Override 334 public void onFailure(int reason, String description) { 335 Log.e(TAG, "SingleScanListener onFailure:" 336 + " reason: " + reason 337 + " description: " + description); 338 339 // reschedule the scan 340 if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 341 scheduleDelayedSingleScan(mIsFullBandScan); 342 } else { 343 mSingleScanRestartCount = 0; 344 Log.e(TAG, "Failed to successfully start single scan for " 345 + MAX_SCAN_RESTART_ALLOWED + " times"); 346 } 347 } 348 349 @Override 350 public void onPeriodChanged(int periodInMs) { 351 localLog("SingleScanListener onPeriodChanged: " 352 + "actual scan period " + periodInMs + "ms"); 353 } 354 355 @Override 356 public void onResults(WifiScanner.ScanData[] results) { 357 } 358 359 @Override 360 public void onFullResult(ScanResult fullScanResult) { 361 } 362 } 363 364 // PNO scan results listener for both disconected and connected PNO scanning. 365 // A PNO scan is initiated when screen is off. 366 private class PnoScanListener implements WifiScanner.PnoScanListener { 367 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 368 private int mLowRssiNetworkRetryDelay = 369 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 370 371 public void clearScanDetails() { 372 mScanDetails.clear(); 373 } 374 375 // Reset to the start value when either a non-PNO scan is started or 376 // WifiNetworkSelector selects a candidate from the PNO scan results. 377 public void resetLowRssiNetworkRetryDelay() { 378 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 379 } 380 381 @VisibleForTesting 382 public int getLowRssiNetworkRetryDelay() { 383 return mLowRssiNetworkRetryDelay; 384 } 385 386 @Override 387 public void onSuccess() { 388 localLog("PnoScanListener onSuccess"); 389 } 390 391 @Override 392 public void onFailure(int reason, String description) { 393 Log.e(TAG, "PnoScanListener onFailure:" 394 + " reason: " + reason 395 + " description: " + description); 396 397 // reschedule the scan 398 if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 399 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS); 400 } else { 401 mScanRestartCount = 0; 402 Log.e(TAG, "Failed to successfully start PNO scan for " 403 + MAX_SCAN_RESTART_ALLOWED + " times"); 404 } 405 } 406 407 @Override 408 public void onPeriodChanged(int periodInMs) { 409 localLog("PnoScanListener onPeriodChanged: " 410 + "actual scan period " + periodInMs + "ms"); 411 } 412 413 // Currently the PNO scan results doesn't include IE, 414 // which contains information required by WifiNetworkSelector. Ignore them 415 // for now. 416 @Override 417 public void onResults(WifiScanner.ScanData[] results) { 418 } 419 420 @Override 421 public void onFullResult(ScanResult fullScanResult) { 422 } 423 424 @Override 425 public void onPnoNetworkFound(ScanResult[] results) { 426 localLog("PnoScanListener: onPnoNetworkFound: results len = " + results.length); 427 428 for (ScanResult result: results) { 429 mScanDetails.add(ScanResultUtil.toScanDetail(result)); 430 } 431 432 boolean wasConnectAttempted; 433 wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener"); 434 clearScanDetails(); 435 mScanRestartCount = 0; 436 437 if (!wasConnectAttempted) { 438 // The scan results were rejected by WifiNetworkSelector due to low RSSI values 439 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) { 440 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS; 441 } 442 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay); 443 444 // Set up the delay value for next retry. 445 mLowRssiNetworkRetryDelay *= 2; 446 } else { 447 resetLowRssiNetworkRetryDelay(); 448 } 449 } 450 } 451 452 private final PnoScanListener mPnoScanListener = new PnoScanListener(); 453 454 /** 455 * WifiConnectivityManager constructor 456 */ 457 WifiConnectivityManager(Context context, WifiStateMachine stateMachine, 458 WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo, 459 WifiNetworkSelector networkSelector, WifiInjector wifiInjector, Looper looper, 460 boolean enable) { 461 mStateMachine = stateMachine; 462 mScanner = scanner; 463 mConfigManager = configManager; 464 mWifiInfo = wifiInfo; 465 mNetworkSelector = networkSelector; 466 mLocalLog = networkSelector.getLocalLog(); 467 mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog(); 468 mWifiMetrics = wifiInjector.getWifiMetrics(); 469 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 470 mEventHandler = new Handler(looper); 471 mClock = wifiInjector.getClock(); 472 mConnectionAttemptTimeStamps = new LinkedList<>(); 473 474 mMin5GHzRssi = context.getResources().getInteger( 475 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); 476 mMin24GHzRssi = context.getResources().getInteger( 477 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); 478 mBand5GHzBonus = context.getResources().getInteger( 479 R.integer.config_wifi_framework_5GHz_preference_boost_factor); 480 mCurrentConnectionBonus = context.getResources().getInteger( 481 R.integer.config_wifi_framework_current_network_boost); 482 mSameNetworkBonus = context.getResources().getInteger( 483 R.integer.config_wifi_framework_SAME_BSSID_AWARD); 484 mSecureBonus = context.getResources().getInteger( 485 R.integer.config_wifi_framework_SECURITY_AWARD); 486 int thresholdSaturatedRssi24 = context.getResources().getInteger( 487 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); 488 mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( 489 R.bool.config_wifi_framework_enable_associated_network_selection); 490 mInitialScoreMax = (context.getResources().getInteger( 491 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz) 492 + context.getResources().getInteger( 493 R.integer.config_wifi_framework_RSSI_SCORE_OFFSET)) 494 * context.getResources().getInteger( 495 R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); 496 497 Log.i(TAG, "PNO settings:" + " min5GHzRssi " + mMin5GHzRssi 498 + " min24GHzRssi " + mMin24GHzRssi 499 + " currentConnectionBonus " + mCurrentConnectionBonus 500 + " sameNetworkBonus " + mSameNetworkBonus 501 + " secureNetworkBonus " + mSecureBonus 502 + " initialScoreMax " + mInitialScoreMax); 503 504 // Register the network evaluators 505 SavedNetworkEvaluator savedNetworkEvaluator = new SavedNetworkEvaluator(context, 506 mConfigManager, mClock, mLocalLog); 507 mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator, 508 SAVED_NETWORK_EVALUATOR_PRIORITY); 509 510 ExternalScoreEvaluator externalScoreEvaluator = new ExternalScoreEvaluator(context, 511 mConfigManager, mClock, mLocalLog); 512 mNetworkSelector.registerNetworkEvaluator(externalScoreEvaluator, 513 EXTERNAL_SCORE_EVALUATOR_PRIORITY); 514 515 // Register for all single scan results 516 mScanner.registerScanListener(mAllSingleScanListener); 517 518 mWifiConnectivityManagerEnabled = enable; 519 520 Log.i(TAG, "ConnectivityScanManager initialized and " 521 + (enable ? "enabled" : "disabled")); 522 } 523 524 /** 525 * This checks the connection attempt rate and recommends whether the connection attempt 526 * should be skipped or not. This attempts to rate limit the rate of connections to 527 * prevent us from flapping between networks and draining battery rapidly. 528 */ 529 private boolean shouldSkipConnectionAttempt(Long timeMillis) { 530 Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator(); 531 // First evict old entries from the queue. 532 while (attemptIter.hasNext()) { 533 Long connectionAttemptTimeMillis = attemptIter.next(); 534 if ((timeMillis - connectionAttemptTimeMillis) 535 > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) { 536 attemptIter.remove(); 537 } else { 538 // This list is sorted by timestamps, so we can skip any more checks 539 break; 540 } 541 } 542 // If we've reached the max connection attempt rate, skip this connection attempt 543 return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE); 544 } 545 546 /** 547 * Add the current connection attempt timestamp to our queue of connection attempts. 548 */ 549 private void noteConnectionAttempt(Long timeMillis) { 550 mConnectionAttemptTimeStamps.addLast(timeMillis); 551 } 552 553 /** 554 * This is used to clear the connection attempt rate limiter. This is done when the user 555 * explicitly tries to connect to a specified network. 556 */ 557 private void clearConnectionAttemptTimeStamps() { 558 mConnectionAttemptTimeStamps.clear(); 559 } 560 561 /** 562 * Attempt to connect to a network candidate. 563 * 564 * Based on the currently connected network, this menthod determines whether we should 565 * connect or roam to the network candidate recommended by WifiNetworkSelector. 566 */ 567 private void connectToNetwork(WifiConfiguration candidate) { 568 ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate(); 569 if (scanResultCandidate == null) { 570 Log.e(TAG, "connectToNetwork: bad candidate - " + candidate 571 + " scanResult: " + scanResultCandidate); 572 return; 573 } 574 575 String targetBssid = scanResultCandidate.BSSID; 576 String targetAssociationId = candidate.SSID + " : " + targetBssid; 577 578 // Check if we are already connected or in the process of connecting to the target 579 // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just 580 // in case the firmware automatically roamed to a BSSID different from what 581 // WifiNetworkSelector selected. 582 if (targetBssid != null 583 && (targetBssid.equals(mLastConnectionAttemptBssid) 584 || targetBssid.equals(mWifiInfo.getBSSID())) 585 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) { 586 localLog("connectToNetwork: Either already connected " 587 + "or is connecting to " + targetAssociationId); 588 return; 589 } 590 591 Long elapsedTimeMillis = mClock.getElapsedSinceBootMillis(); 592 if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) { 593 localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!"); 594 mTotalConnectivityAttemptsRateLimited++; 595 return; 596 } 597 noteConnectionAttempt(elapsedTimeMillis); 598 599 mLastConnectionAttemptBssid = targetBssid; 600 601 WifiConfiguration currentConnectedNetwork = mConfigManager 602 .getConfiguredNetwork(mWifiInfo.getNetworkId()); 603 String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" : 604 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID()); 605 606 if (currentConnectedNetwork != null 607 && (currentConnectedNetwork.networkId == candidate.networkId 608 || currentConnectedNetwork.isLinked(candidate))) { 609 localLog("connectToNetwork: Roaming from " + currentAssociationId + " to " 610 + targetAssociationId); 611 mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate); 612 } else { 613 localLog("connectToNetwork: Reconnect from " + currentAssociationId + " to " 614 + targetAssociationId); 615 mStateMachine.startConnectToNetwork(candidate.networkId, scanResultCandidate.BSSID); 616 } 617 } 618 619 // Helper for selecting the band for connectivity scan 620 private int getScanBand() { 621 return getScanBand(true); 622 } 623 624 private int getScanBand(boolean isFullBandScan) { 625 if (isFullBandScan) { 626 return WifiScanner.WIFI_BAND_BOTH_WITH_DFS; 627 } else { 628 // Use channel list instead. 629 return WifiScanner.WIFI_BAND_UNSPECIFIED; 630 } 631 } 632 633 // Helper for setting the channels for connectivity scan when band is unspecified. Returns 634 // false if we can't retrieve the info. 635 private boolean setScanChannels(ScanSettings settings) { 636 WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration(); 637 638 if (config == null) { 639 return false; 640 } 641 642 Set<Integer> freqs = 643 mConfigManager.fetchChannelSetForNetworkForPartialScan( 644 config.networkId, CHANNEL_LIST_AGE_MS); 645 646 if (freqs != null && freqs.size() != 0) { 647 int index = 0; 648 settings.channels = new WifiScanner.ChannelSpec[freqs.size()]; 649 for (Integer freq : freqs) { 650 settings.channels[index++] = new WifiScanner.ChannelSpec(freq); 651 } 652 return true; 653 } else { 654 localLog("No scan channels for " + config.configKey() + ". Perform full band scan"); 655 return false; 656 } 657 } 658 659 // Watchdog timer handler 660 private void watchdogHandler() { 661 localLog("watchdogHandler"); 662 663 // Schedule the next timer and start a single scan if we are in disconnected state. 664 // Otherwise, the watchdog timer will be scheduled when entering disconnected 665 // state. 666 if (mWifiState == WIFI_STATE_DISCONNECTED) { 667 localLog("start a single scan from watchdogHandler"); 668 669 scheduleWatchdogTimer(); 670 startSingleScan(true); 671 } 672 } 673 674 // Start a single scan and set up the interval for next single scan. 675 private void startPeriodicSingleScan() { 676 long currentTimeStamp = mClock.getElapsedSinceBootMillis(); 677 678 if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) { 679 long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp; 680 if (msSinceLastScan < PERIODIC_SCAN_INTERVAL_MS) { 681 localLog("Last periodic single scan started " + msSinceLastScan 682 + "ms ago, defer this new scan request."); 683 schedulePeriodicScanTimer(PERIODIC_SCAN_INTERVAL_MS - (int) msSinceLastScan); 684 return; 685 } 686 } 687 688 boolean isFullBandScan = true; 689 690 // If the WiFi traffic is heavy, only partial scan is initiated. 691 if (mWifiState == WIFI_STATE_CONNECTED 692 && (mWifiInfo.txSuccessRate > MAX_TX_PACKET_FOR_FULL_SCANS 693 || mWifiInfo.rxSuccessRate > MAX_RX_PACKET_FOR_FULL_SCANS)) { 694 localLog("No full band scan due to heavy traffic, txSuccessRate=" 695 + mWifiInfo.txSuccessRate + " rxSuccessRate=" 696 + mWifiInfo.rxSuccessRate); 697 isFullBandScan = false; 698 } 699 700 mLastPeriodicSingleScanTimeStamp = currentTimeStamp; 701 startSingleScan(isFullBandScan); 702 schedulePeriodicScanTimer(mPeriodicSingleScanInterval); 703 704 // Set up the next scan interval in an exponential backoff fashion. 705 mPeriodicSingleScanInterval *= 2; 706 if (mPeriodicSingleScanInterval > MAX_PERIODIC_SCAN_INTERVAL_MS) { 707 mPeriodicSingleScanInterval = MAX_PERIODIC_SCAN_INTERVAL_MS; 708 } 709 } 710 711 // Reset the last periodic single scan time stamp so that the next periodic single 712 // scan can start immediately. 713 private void resetLastPeriodicSingleScanTimeStamp() { 714 mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 715 } 716 717 // Periodic scan timer handler 718 private void periodicScanTimerHandler() { 719 localLog("periodicScanTimerHandler"); 720 721 // Schedule the next timer and start a single scan if screen is on. 722 if (mScreenOn) { 723 startPeriodicSingleScan(); 724 } 725 } 726 727 // Start a single scan 728 private void startSingleScan(boolean isFullBandScan) { 729 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 730 return; 731 } 732 733 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 734 735 ScanSettings settings = new ScanSettings(); 736 if (!isFullBandScan) { 737 if (!setScanChannels(settings)) { 738 isFullBandScan = true; 739 } 740 } 741 settings.band = getScanBand(isFullBandScan); 742 settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 743 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 744 settings.numBssidsPerScan = 0; 745 746 List<ScanSettings.HiddenNetwork> hiddenNetworkList = 747 mConfigManager.retrieveHiddenNetworkList(); 748 settings.hiddenNetworks = 749 hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]); 750 751 SingleScanListener singleScanListener = 752 new SingleScanListener(isFullBandScan); 753 mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE); 754 } 755 756 // Start a periodic scan when screen is on 757 private void startPeriodicScan(boolean scanImmediately) { 758 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 759 760 // No connectivity scan if auto roaming is disabled. 761 if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) { 762 return; 763 } 764 765 // Due to b/28020168, timer based single scan will be scheduled 766 // to provide periodic scan in an exponential backoff fashion. 767 if (scanImmediately) { 768 resetLastPeriodicSingleScanTimeStamp(); 769 } 770 mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; 771 startPeriodicSingleScan(); 772 } 773 774 // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected 775 private void startDisconnectedPnoScan() { 776 // TODO(b/29503772): Need to change this interface. 777 778 // Initialize PNO settings 779 PnoSettings pnoSettings = new PnoSettings(); 780 List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList(); 781 int listSize = pnoNetworkList.size(); 782 783 if (listSize == 0) { 784 // No saved network 785 localLog("No saved network for starting disconnected PNO."); 786 return; 787 } 788 789 pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize]; 790 pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList); 791 pnoSettings.min5GHzRssi = mMin5GHzRssi; 792 pnoSettings.min24GHzRssi = mMin24GHzRssi; 793 pnoSettings.initialScoreMax = mInitialScoreMax; 794 pnoSettings.currentConnectionBonus = mCurrentConnectionBonus; 795 pnoSettings.sameNetworkBonus = mSameNetworkBonus; 796 pnoSettings.secureBonus = mSecureBonus; 797 pnoSettings.band5GHzBonus = mBand5GHzBonus; 798 799 // Initialize scan settings 800 ScanSettings scanSettings = new ScanSettings(); 801 scanSettings.band = getScanBand(); 802 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; 803 scanSettings.numBssidsPerScan = 0; 804 scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS; 805 806 mPnoScanListener.clearScanDetails(); 807 808 mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener); 809 mPnoScanStarted = true; 810 } 811 812 // Stop PNO scan. 813 private void stopPnoScan() { 814 if (mPnoScanStarted) { 815 mScanner.stopPnoScan(mPnoScanListener); 816 } 817 818 mPnoScanStarted = false; 819 } 820 821 // Set up watchdog timer 822 private void scheduleWatchdogTimer() { 823 localLog("scheduleWatchdogTimer"); 824 825 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 826 mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS, 827 WATCHDOG_TIMER_TAG, 828 mWatchdogListener, mEventHandler); 829 } 830 831 // Set up periodic scan timer 832 private void schedulePeriodicScanTimer(int intervalMs) { 833 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 834 mClock.getElapsedSinceBootMillis() + intervalMs, 835 PERIODIC_SCAN_TIMER_TAG, 836 mPeriodicScanTimerListener, mEventHandler); 837 mPeriodicScanTimerSet = true; 838 } 839 840 // Cancel periodic scan timer 841 private void cancelPeriodicScanTimer() { 842 if (mPeriodicScanTimerSet) { 843 mAlarmManager.cancel(mPeriodicScanTimerListener); 844 mPeriodicScanTimerSet = false; 845 } 846 } 847 848 // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS 849 private void scheduleDelayedSingleScan(boolean isFullBandScan) { 850 localLog("scheduleDelayedSingleScan"); 851 852 RestartSingleScanListener restartSingleScanListener = 853 new RestartSingleScanListener(isFullBandScan); 854 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 855 mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS, 856 RESTART_SINGLE_SCAN_TIMER_TAG, 857 restartSingleScanListener, mEventHandler); 858 } 859 860 // Set up timer to start a delayed scan after msFromNow milli-seconds 861 private void scheduleDelayedConnectivityScan(int msFromNow) { 862 localLog("scheduleDelayedConnectivityScan"); 863 864 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 865 mClock.getElapsedSinceBootMillis() + msFromNow, 866 RESTART_CONNECTIVITY_SCAN_TIMER_TAG, 867 mRestartScanListener, mEventHandler); 868 869 } 870 871 // Start a connectivity scan. The scan method is chosen according to 872 // the current screen state and WiFi state. 873 private void startConnectivityScan(boolean scanImmediately) { 874 localLog("startConnectivityScan: screenOn=" + mScreenOn 875 + " wifiState=" + mWifiState 876 + " scanImmediately=" + scanImmediately 877 + " wifiEnabled=" + mWifiEnabled 878 + " wifiConnectivityManagerEnabled=" 879 + mWifiConnectivityManagerEnabled); 880 881 if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { 882 return; 883 } 884 885 // Always stop outstanding connecivity scan if there is any 886 stopConnectivityScan(); 887 888 // Don't start a connectivity scan while Wifi is in the transition 889 // between connected and disconnected states. 890 if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) { 891 return; 892 } 893 894 if (mScreenOn) { 895 startPeriodicScan(scanImmediately); 896 } else { // screenOff 897 if (mWifiState == WIFI_STATE_DISCONNECTED) { 898 startDisconnectedPnoScan(); 899 } 900 } 901 } 902 903 // Stop connectivity scan if there is any. 904 private void stopConnectivityScan() { 905 // Due to b/28020168, timer based single scan will be scheduled 906 // to provide periodic scan in an exponential backoff fashion. 907 cancelPeriodicScanTimer(); 908 stopPnoScan(); 909 mScanRestartCount = 0; 910 } 911 912 /** 913 * Handler for screen state (on/off) changes 914 */ 915 public void handleScreenStateChanged(boolean screenOn) { 916 localLog("handleScreenStateChanged: screenOn=" + screenOn); 917 918 mScreenOn = screenOn; 919 920 startConnectivityScan(SCAN_ON_SCHEDULE); 921 } 922 923 /** 924 * Handler for WiFi state (connected/disconnected) changes 925 */ 926 public void handleConnectionStateChanged(int state) { 927 localLog("handleConnectionStateChanged: state=" + state); 928 929 mWifiState = state; 930 931 // Reset BSSID of last connection attempt and kick off 932 // the watchdog timer if entering disconnected state. 933 if (mWifiState == WIFI_STATE_DISCONNECTED) { 934 mLastConnectionAttemptBssid = null; 935 scheduleWatchdogTimer(); 936 } 937 938 startConnectivityScan(SCAN_ON_SCHEDULE); 939 } 940 941 /** 942 * Handler when user toggles whether untrusted connection is allowed 943 */ 944 public void setUntrustedConnectionAllowed(boolean allowed) { 945 localLog("setUntrustedConnectionAllowed: allowed=" + allowed); 946 947 if (mUntrustedConnectionAllowed != allowed) { 948 mUntrustedConnectionAllowed = allowed; 949 startConnectivityScan(SCAN_IMMEDIATELY); 950 } 951 } 952 953 /** 954 * Handler when user specifies a particular network to connect to 955 */ 956 public void setUserConnectChoice(int netId) { 957 localLog("setUserConnectChoice: netId=" + netId); 958 959 mNetworkSelector.setUserConnectChoice(netId); 960 clearConnectionAttemptTimeStamps(); 961 } 962 963 /** 964 * Handler for on-demand connectivity scan 965 */ 966 public void forceConnectivityScan() { 967 localLog("forceConnectivityScan"); 968 969 startConnectivityScan(SCAN_IMMEDIATELY); 970 } 971 972 /** 973 * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector 974 */ 975 public boolean trackBssid(String bssid, boolean enable) { 976 localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid); 977 978 boolean ret = mNetworkSelector 979 .enableBssidForNetworkSelection(bssid, enable); 980 981 if (ret && !enable) { 982 // Disabling a BSSID can happen when the AP candidate to connect to has 983 // no capacity for new stations. We start another scan immediately so that 984 // WifiNetworkSelector can give us another candidate to connect to. 985 startConnectivityScan(SCAN_IMMEDIATELY); 986 } 987 988 return ret; 989 } 990 991 /** 992 * Inform WiFi is enabled for connection or not 993 */ 994 public void setWifiEnabled(boolean enable) { 995 localLog("Set WiFi " + (enable ? "enabled" : "disabled")); 996 997 mWifiEnabled = enable; 998 999 if (!mWifiEnabled) { 1000 stopConnectivityScan(); 1001 resetLastPeriodicSingleScanTimeStamp(); 1002 mLastConnectionAttemptBssid = null; 1003 } else if (mWifiConnectivityManagerEnabled) { 1004 startConnectivityScan(SCAN_IMMEDIATELY); 1005 } 1006 } 1007 1008 /** 1009 * Turn on/off the WifiConnectivityMangager at runtime 1010 */ 1011 public void enable(boolean enable) { 1012 localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled")); 1013 1014 mWifiConnectivityManagerEnabled = enable; 1015 1016 if (!mWifiConnectivityManagerEnabled) { 1017 stopConnectivityScan(); 1018 resetLastPeriodicSingleScanTimeStamp(); 1019 mLastConnectionAttemptBssid = null; 1020 } else if (mWifiEnabled) { 1021 startConnectivityScan(SCAN_IMMEDIATELY); 1022 } 1023 } 1024 1025 @VisibleForTesting 1026 int getLowRssiNetworkRetryDelay() { 1027 return mPnoScanListener.getLowRssiNetworkRetryDelay(); 1028 } 1029 1030 @VisibleForTesting 1031 long getLastPeriodicSingleScanTimeStamp() { 1032 return mLastPeriodicSingleScanTimeStamp; 1033 } 1034} 1035