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