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