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