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