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