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