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