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