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