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 17 package com.android.server.wifi; 18 19 import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; 20 21 import android.app.ActivityManager; 22 import android.app.AlarmManager; 23 import android.content.Context; 24 import android.net.wifi.ScanResult; 25 import android.net.wifi.SupplicantState; 26 import android.net.wifi.WifiConfiguration; 27 import android.net.wifi.WifiInfo; 28 import android.net.wifi.WifiManager; 29 import android.net.wifi.WifiScanner; 30 import android.net.wifi.WifiScanner.PnoSettings; 31 import android.net.wifi.WifiScanner.ScanSettings; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.util.LocalLog; 35 import android.util.Log; 36 37 import com.android.internal.R; 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.server.wifi.util.ScanDetailUtil; 40 41 import java.io.FileDescriptor; 42 import java.io.PrintWriter; 43 import java.util.ArrayList; 44 import java.util.HashSet; 45 import java.util.Iterator; 46 import java.util.LinkedList; 47 import java.util.List; 48 import 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 */ 57 public 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. localLog(String log)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 RestartSingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan)181 RestartSingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan) { 182 mIsWatchdogTriggered = isWatchdogTriggered; 183 mIsFullBandScan = isFullBandScan; 184 } 185 186 @Override onAlarm()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 */ handleScanResults(List<ScanDetail> scanDetails, String listenerName)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 clearScanDetails()242 public void clearScanDetails() { 243 mScanDetails.clear(); 244 } 245 246 @Override onSuccess()247 public void onSuccess() { 248 localLog("PeriodicScanListener onSuccess"); 249 250 // reset the count 251 mScanRestartCount = 0; 252 } 253 254 @Override onFailure(int reason, String description)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 onPeriodChanged(int periodInMs)271 public void onPeriodChanged(int periodInMs) { 272 localLog("PeriodicScanListener onPeriodChanged: " 273 + "actual scan period " + periodInMs + "ms"); 274 } 275 276 @Override onResults(WifiScanner.ScanData[] results)277 public void onResults(WifiScanner.ScanData[] results) { 278 handleScanResults(mScanDetails, "PeriodicScanListener"); 279 clearScanDetails(); 280 } 281 282 @Override onFullResult(ScanResult fullScanResult)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 SingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan)304 SingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan) { 305 mIsWatchdogTriggered = isWatchdogTriggered; 306 mIsFullBandScan = isFullBandScan; 307 } 308 clearScanDetails()309 public void clearScanDetails() { 310 mScanDetails.clear(); 311 } 312 313 @Override onSuccess()314 public void onSuccess() { 315 localLog("SingleScanListener onSuccess"); 316 317 // reset the count 318 mSingleScanRestartCount = 0; 319 } 320 321 @Override onFailure(int reason, String description)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 onPeriodChanged(int periodInMs)338 public void onPeriodChanged(int periodInMs) { 339 localLog("SingleScanListener onPeriodChanged: " 340 + "actual scan period " + periodInMs + "ms"); 341 } 342 343 @Override onResults(WifiScanner.ScanData[] results)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 onFullResult(ScanResult fullScanResult)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 clearScanDetails()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. resetLowRssiNetworkRetryDelay()393 public void resetLowRssiNetworkRetryDelay() { 394 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 395 } 396 397 @VisibleForTesting getLowRssiNetworkRetryDelay()398 public int getLowRssiNetworkRetryDelay() { 399 return mLowRssiNetworkRetryDelay; 400 } 401 402 @Override onSuccess()403 public void onSuccess() { 404 localLog("PnoScanListener onSuccess"); 405 406 // reset the count 407 mScanRestartCount = 0; 408 } 409 410 @Override onFailure(int reason, String description)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 onPeriodChanged(int periodInMs)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 onResults(WifiScanner.ScanData[] results)436 public void onResults(WifiScanner.ScanData[] results) { 437 } 438 439 @Override onFullResult(ScanResult fullScanResult)440 public void onFullResult(ScanResult fullScanResult) { 441 } 442 443 @Override onPnoNetworkFound(ScanResult[] results)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 */ WifiConnectivityManager(Context context, WifiStateMachine stateMachine, WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo, WifiQualifiedNetworkSelector qualifiedNetworkSelector, WifiInjector wifiInjector, Looper looper)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 */ shouldSkipConnectionAttempt(Long timeMillis)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 */ noteConnectionAttempt(Long timeMillis)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 */ clearConnectionAttemptTimeStamps()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 */ connectToNetwork(WifiConfiguration candidate)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 getScanBand()609 private int getScanBand() { 610 return getScanBand(true); 611 } 612 getScanBand(boolean isFullBandScan)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. setScanChannels(ScanSettings settings)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 watchdogHandler()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. startPeriodicSingleScan()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. resetLastPeriodicSingleScanTimeStamp()709 private void resetLastPeriodicSingleScanTimeStamp() { 710 mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 711 } 712 713 // Periodic scan timer handler periodicScanTimerHandler()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 startSingleScan(boolean isWatchdogTriggered, boolean isFullBandScan)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 startPeriodicScan(boolean scanImmediately)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 startDisconnectedPnoScan()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 startConnectedPnoScan()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 scheduleWatchdogTimer()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 schedulePeriodicScanTimer(int intervalMs)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 scheduleDelayedSingleScan(boolean isWatchdogTriggered, boolean isFullBandScan)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 scheduleDelayedConnectivityScan(int msFromNow)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. startConnectivityScan(boolean scanImmediately)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. stopConnectivityScan()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 */ handleScreenStateChanged(boolean screenOn)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 */ handleConnectionStateChanged(int state)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 */ setUntrustedConnectionAllowed(boolean allowed)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 */ connectToUserSelectNetwork(int netId, boolean persistent)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 */ forceConnectivityScan()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 */ trackBssid(String bssid, boolean enable)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 */ setUserPreferredBand(int band)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 */ setWifiEnabled(boolean enable)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 */ enable(boolean enable)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 */ enableVerboseLogging(int verbose)1074 public void enableVerboseLogging(int verbose) { 1075 mDbg = verbose > 0; 1076 } 1077 1078 /** 1079 * Dump the local log buffer 1080 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)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 getLowRssiNetworkRetryDelay()1091 int getLowRssiNetworkRetryDelay() { 1092 return mPnoScanListener.getLowRssiNetworkRetryDelay(); 1093 } 1094 1095 @VisibleForTesting getLastPeriodicSingleScanTimeStamp()1096 long getLastPeriodicSingleScanTimeStamp() { 1097 return mLastPeriodicSingleScanTimeStamp; 1098 } 1099 } 1100