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