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