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