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