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