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