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