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 android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT;
20 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY;
21 
22 import static com.android.internal.util.Preconditions.checkNotNull;
23 import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE;
24 
25 import android.app.AlarmManager;
26 import android.content.Context;
27 import android.net.wifi.ScanResult;
28 import android.net.wifi.SupplicantState;
29 import android.net.wifi.WifiConfiguration;
30 import android.net.wifi.WifiInfo;
31 import android.net.wifi.WifiManager;
32 import android.net.wifi.WifiManager.DeviceMobilityState;
33 import android.net.wifi.WifiScanner;
34 import android.net.wifi.WifiScanner.PnoSettings;
35 import android.net.wifi.WifiScanner.ScanSettings;
36 import android.os.Handler;
37 import android.os.Looper;
38 import android.os.Process;
39 import android.os.WorkSource;
40 import android.util.LocalLog;
41 import android.util.Log;
42 
43 import com.android.internal.R;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.server.wifi.util.ScanResultUtil;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 import java.util.ArrayList;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.Iterator;
53 import java.util.LinkedList;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Set;
57 
58 /**
59  * This class manages all the connectivity related scanning activities.
60  *
61  * When the screen is turned on or off, WiFi is connected or disconnected,
62  * or on-demand, a scan is initiatiated and the scan results are passed
63  * to WifiNetworkSelector for it to make a recommendation on which network
64  * to connect to.
65  */
66 public class WifiConnectivityManager {
67     public static final String WATCHDOG_TIMER_TAG =
68             "WifiConnectivityManager Schedule Watchdog Timer";
69     public static final String PERIODIC_SCAN_TIMER_TAG =
70             "WifiConnectivityManager Schedule Periodic Scan Timer";
71     public static final String RESTART_SINGLE_SCAN_TIMER_TAG =
72             "WifiConnectivityManager Restart Single Scan";
73     public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG =
74             "WifiConnectivityManager Restart Scan";
75 
76     private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
77     // Constants to indicate whether a scan should start immediately or
78     // it should comply to the minimum scan interval rule.
79     private static final boolean SCAN_IMMEDIATELY = true;
80     private static final boolean SCAN_ON_SCHEDULE = false;
81     // Periodic scan interval in milli-seconds. This is the scan
82     // performed when screen is on.
83     @VisibleForTesting
84     public static final int PERIODIC_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
85     // When screen is on and WiFi traffic is heavy, exponential backoff
86     // connectivity scans are scheduled. This constant defines the maximum
87     // scan interval in this scenario.
88     @VisibleForTesting
89     public static final int MAX_PERIODIC_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
90     // Initial PNO scan interval in milliseconds when the device is moving. The scan interval backs
91     // off from this initial interval on subsequent scans. This scan is performed when screen is
92     // off and disconnected.
93     @VisibleForTesting
94     static final int MOVING_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
95     // Initial PNO scan interval in milliseconds when the device is stationary. The scan interval
96     // backs off from this initial interval on subsequent scans. This scan is performed when screen
97     // is off and disconnected.
98     @VisibleForTesting
99     static final int STATIONARY_PNO_SCAN_INTERVAL_MS = 60 * 1000; // 1 minute
100 
101     // PNO scan interval in milli-seconds. This is the scan
102     // performed when screen is off and connected.
103     private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
104     // When a network is found by PNO scan but gets rejected by Wifi Network Selector due
105     // to its low RSSI value, scan will be reschduled in an exponential back off manner.
106     private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds
107     private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds
108     // Maximum number of retries when starting a scan failed
109     @VisibleForTesting
110     public static final int MAX_SCAN_RESTART_ALLOWED = 5;
111     // Number of milli-seconds to delay before retry starting
112     // a previously failed scan
113     private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds
114     // When in disconnected mode, a watchdog timer will be fired
115     // every WATCHDOG_INTERVAL_MS to start a single scan. This is
116     // to prevent caveat from things like PNO scan.
117     private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes
118     // Restricted channel list age out value.
119     private static final int CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour
120     // This is the time interval for the connection attempt rate calculation. Connection attempt
121     // timestamps beyond this interval is evicted from the list.
122     public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins
123     // Max number of connection attempts in the above time interval.
124     public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6;
125 
126     // ClientModeImpl has a bunch of states. From the
127     // WifiConnectivityManager's perspective it only cares
128     // if it is in Connected state, Disconnected state or in
129     // transition between these two states.
130     public static final int WIFI_STATE_UNKNOWN = 0;
131     public static final int WIFI_STATE_CONNECTED = 1;
132     public static final int WIFI_STATE_DISCONNECTED = 2;
133     public static final int WIFI_STATE_TRANSITIONING = 3;
134 
135     // Log tag for this class
136     private static final String TAG = "WifiConnectivityManager";
137 
138     private final ClientModeImpl mStateMachine;
139     private final WifiInjector mWifiInjector;
140     private final WifiConfigManager mConfigManager;
141     private final WifiInfo mWifiInfo;
142     private final WifiConnectivityHelper mConnectivityHelper;
143     private final WifiNetworkSelector mNetworkSelector;
144     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
145     private final OpenNetworkNotifier mOpenNetworkNotifier;
146     private final CarrierNetworkNotifier mCarrierNetworkNotifier;
147     private final CarrierNetworkConfig mCarrierNetworkConfig;
148     private final WifiMetrics mWifiMetrics;
149     private final AlarmManager mAlarmManager;
150     private final Handler mEventHandler;
151     private final Clock mClock;
152     private final ScoringParams mScoringParams;
153     private final LocalLog mLocalLog;
154     private final LinkedList<Long> mConnectionAttemptTimeStamps;
155     private WifiScanner mScanner;
156 
157     private boolean mDbg = false;
158     private boolean mWifiEnabled = false;
159     private boolean mWifiConnectivityManagerEnabled = false;
160     private boolean mRunning = false;
161     private boolean mScreenOn = false;
162     private int mWifiState = WIFI_STATE_UNKNOWN;
163     private boolean mUntrustedConnectionAllowed = false;
164     private boolean mTrustedConnectionAllowed = false;
165     private boolean mSpecificNetworkRequestInProgress = false;
166     private int mScanRestartCount = 0;
167     private int mSingleScanRestartCount = 0;
168     private int mTotalConnectivityAttemptsRateLimited = 0;
169     private String mLastConnectionAttemptBssid = null;
170     private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
171     private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
172     private boolean mPnoScanStarted = false;
173     private boolean mPeriodicScanTimerSet = false;
174     // Device configs
175     private boolean mEnableAutoJoinWhenAssociated;
176     private boolean mWaitForFullBandScanResults = false;
177     private boolean mUseSingleRadioChainScanResults = false;
178     private int mFullScanMaxTxRate;
179     private int mFullScanMaxRxRate;
180 
181     // PNO settings
182     private int mCurrentConnectionBonus;
183     private int mSameNetworkBonus;
184     private int mSecureBonus;
185     private int mBand5GHzBonus;
186     private int mRssiScoreOffset;
187     private int mRssiScoreSlope;
188     private int mPnoScanIntervalMs;
189 
190     // BSSID blacklist
191     @VisibleForTesting
192     public static final int BSSID_BLACKLIST_THRESHOLD = 3;
193     @VisibleForTesting
194     public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000;
195     private static class BssidBlacklistStatus {
196         // Number of times this BSSID has been rejected for association.
197         public int counter;
198         public boolean isBlacklisted;
199         public long blacklistedTimeStamp = RESET_TIME_STAMP;
200     }
201     private Map<String, BssidBlacklistStatus> mBssidBlacklist =
202             new HashMap<>();
203 
204     // Association failure reason codes
205     @VisibleForTesting
206     public static final int REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
207 
208     // A helper to log debugging information in the local log buffer, which can
209     // be retrieved in bugreport.
localLog(String log)210     private void localLog(String log) {
211         mLocalLog.log(log);
212     }
213 
214     // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
215     // if the start scan command failed. An timer is used here to make it a deferred retry.
216     private final AlarmManager.OnAlarmListener mRestartScanListener =
217             new AlarmManager.OnAlarmListener() {
218                 public void onAlarm() {
219                     startConnectivityScan(SCAN_IMMEDIATELY);
220                 }
221             };
222 
223     // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
224     // if the start scan command failed. An timer is used here to make it a deferred retry.
225     private class RestartSingleScanListener implements AlarmManager.OnAlarmListener {
226         private final boolean mIsFullBandScan;
227 
RestartSingleScanListener(boolean isFullBandScan)228         RestartSingleScanListener(boolean isFullBandScan) {
229             mIsFullBandScan = isFullBandScan;
230         }
231 
232         @Override
onAlarm()233         public void onAlarm() {
234             startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE);
235         }
236     }
237 
238     // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS
239     // if it is in the WIFI_STATE_DISCONNECTED state.
240     private final AlarmManager.OnAlarmListener mWatchdogListener =
241             new AlarmManager.OnAlarmListener() {
242                 public void onAlarm() {
243                     watchdogHandler();
244                 }
245             };
246 
247     // Due to b/28020168, timer based single scan will be scheduled
248     // to provide periodic scan in an exponential backoff fashion.
249     private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
250             new AlarmManager.OnAlarmListener() {
251                 public void onAlarm() {
252                     periodicScanTimerHandler();
253                 }
254             };
255 
256     /**
257      * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
258      * Executes selection of potential network candidates, initiation of connection attempt to that
259      * network.
260      *
261      * @return true - if a candidate is selected by WifiNetworkSelector
262      *         false - if no candidate is selected by WifiNetworkSelector
263      */
handleScanResults(List<ScanDetail> scanDetails, String listenerName)264     private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
265         // Check if any blacklisted BSSIDs can be freed.
266         refreshBssidBlacklist();
267 
268         if (mStateMachine.isSupplicantTransientState()) {
269             localLog(listenerName
270                     + " onResults: No network selection because supplicantTransientState is "
271                     + mStateMachine.isSupplicantTransientState());
272             return false;
273         }
274 
275         localLog(listenerName + " onResults: start network selection");
276 
277         WifiConfiguration candidate =
278                 mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo,
279                 mStateMachine.isConnected(), mStateMachine.isDisconnected(),
280                 mUntrustedConnectionAllowed);
281         mWifiLastResortWatchdog.updateAvailableNetworks(
282                 mNetworkSelector.getConnectableScanDetails());
283         mWifiMetrics.countScanResults(scanDetails);
284         if (candidate != null) {
285             localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
286             connectToNetwork(candidate);
287             return true;
288         } else {
289             if (mWifiState == WIFI_STATE_DISCONNECTED) {
290                 mOpenNetworkNotifier.handleScanResults(
291                         mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
292                 if (mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) {
293                     mCarrierNetworkNotifier.handleScanResults(
294                             mNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks(
295                                     mCarrierNetworkConfig));
296                 }
297             }
298             return false;
299         }
300     }
301 
302     // All single scan results listener.
303     //
304     // Note: This is the listener for all the available single scan results,
305     //       including the ones initiated by WifiConnectivityManager and
306     //       other modules.
307     private class AllSingleScanListener implements WifiScanner.ScanListener {
308         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
309         private int mNumScanResultsIgnoredDueToSingleRadioChain = 0;
310 
clearScanDetails()311         public void clearScanDetails() {
312             mScanDetails.clear();
313             mNumScanResultsIgnoredDueToSingleRadioChain = 0;
314         }
315 
316         @Override
onSuccess()317         public void onSuccess() {
318         }
319 
320         @Override
onFailure(int reason, String description)321         public void onFailure(int reason, String description) {
322             localLog("registerScanListener onFailure:"
323                       + " reason: " + reason + " description: " + description);
324         }
325 
326         @Override
onPeriodChanged(int periodInMs)327         public void onPeriodChanged(int periodInMs) {
328         }
329 
330         @Override
onResults(WifiScanner.ScanData[] results)331         public void onResults(WifiScanner.ScanData[] results) {
332             if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
333                 clearScanDetails();
334                 mWaitForFullBandScanResults = false;
335                 return;
336             }
337 
338             // We treat any full band scans (with DFS or not) as "full".
339             boolean isFullBandScanResults =
340                     results[0].getBandScanned() == WifiScanner.WIFI_BAND_BOTH_WITH_DFS
341                             || results[0].getBandScanned() == WifiScanner.WIFI_BAND_BOTH;
342             // Full band scan results only.
343             if (mWaitForFullBandScanResults) {
344                 if (!isFullBandScanResults) {
345                     localLog("AllSingleScanListener waiting for full band scan results.");
346                     clearScanDetails();
347                     return;
348                 } else {
349                     mWaitForFullBandScanResults = false;
350                 }
351             }
352             if (results.length > 0) {
353                 mWifiMetrics.incrementAvailableNetworksHistograms(mScanDetails,
354                         isFullBandScanResults);
355             }
356             if (mNumScanResultsIgnoredDueToSingleRadioChain > 0) {
357                 Log.i(TAG, "Number of scan results ignored due to single radio chain scan: "
358                         + mNumScanResultsIgnoredDueToSingleRadioChain);
359             }
360             boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener");
361             clearScanDetails();
362 
363             // Update metrics to see if a single scan detected a valid network
364             // while PNO scan didn't.
365             // Note: We don't update the background scan metrics any more as it is
366             //       not in use.
367             if (mPnoScanStarted) {
368                 if (wasConnectAttempted) {
369                     mWifiMetrics.incrementNumConnectivityWatchdogPnoBad();
370                 } else {
371                     mWifiMetrics.incrementNumConnectivityWatchdogPnoGood();
372                 }
373             }
374         }
375 
376         @Override
onFullResult(ScanResult fullScanResult)377         public void onFullResult(ScanResult fullScanResult) {
378             if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
379                 return;
380             }
381 
382             if (mDbg) {
383                 localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID
384                         + " capabilities " + fullScanResult.capabilities);
385             }
386 
387             // When the scan result has radio chain info, ensure we throw away scan results
388             // not received with both radio chains (if |mUseSingleRadioChainScanResults| is false).
389             if (!mUseSingleRadioChainScanResults
390                     && fullScanResult.radioChainInfos != null
391                     && fullScanResult.radioChainInfos.length == 1) {
392                 // Keep track of the number of dropped scan results for logging.
393                 mNumScanResultsIgnoredDueToSingleRadioChain++;
394                 return;
395             }
396 
397             mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult));
398         }
399     }
400 
401     private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener();
402 
403     // Single scan results listener. A single scan is initiated when
404     // DisconnectedPNO scan found a valid network and woke up
405     // the system, or by the watchdog timer, or to form the timer based
406     // periodic scan.
407     //
408     // Note: This is the listener for the single scans initiated by the
409     //        WifiConnectivityManager.
410     private class SingleScanListener implements WifiScanner.ScanListener {
411         private final boolean mIsFullBandScan;
412 
SingleScanListener(boolean isFullBandScan)413         SingleScanListener(boolean isFullBandScan) {
414             mIsFullBandScan = isFullBandScan;
415         }
416 
417         @Override
onSuccess()418         public void onSuccess() {
419         }
420 
421         @Override
onFailure(int reason, String description)422         public void onFailure(int reason, String description) {
423             localLog("SingleScanListener onFailure:"
424                     + " reason: " + reason + " description: " + description);
425 
426             // reschedule the scan
427             if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
428                 scheduleDelayedSingleScan(mIsFullBandScan);
429             } else {
430                 mSingleScanRestartCount = 0;
431                 localLog("Failed to successfully start single scan for "
432                         + MAX_SCAN_RESTART_ALLOWED + " times");
433             }
434         }
435 
436         @Override
onPeriodChanged(int periodInMs)437         public void onPeriodChanged(int periodInMs) {
438             localLog("SingleScanListener onPeriodChanged: "
439                     + "actual scan period " + periodInMs + "ms");
440         }
441 
442         @Override
onResults(WifiScanner.ScanData[] results)443         public void onResults(WifiScanner.ScanData[] results) {
444             mSingleScanRestartCount = 0;
445         }
446 
447         @Override
onFullResult(ScanResult fullScanResult)448         public void onFullResult(ScanResult fullScanResult) {
449         }
450     }
451 
452     // PNO scan results listener for both disconnected and connected PNO scanning.
453     // A PNO scan is initiated when screen is off.
454     private class PnoScanListener implements WifiScanner.PnoScanListener {
455         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
456         private int mLowRssiNetworkRetryDelay =
457                 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
458 
clearScanDetails()459         public void clearScanDetails() {
460             mScanDetails.clear();
461         }
462 
463         // Reset to the start value when either a non-PNO scan is started or
464         // WifiNetworkSelector selects a candidate from the PNO scan results.
resetLowRssiNetworkRetryDelay()465         public void resetLowRssiNetworkRetryDelay() {
466             mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
467         }
468 
469         @VisibleForTesting
getLowRssiNetworkRetryDelay()470         public int getLowRssiNetworkRetryDelay() {
471             return mLowRssiNetworkRetryDelay;
472         }
473 
474         @Override
onSuccess()475         public void onSuccess() {
476         }
477 
478         @Override
onFailure(int reason, String description)479         public void onFailure(int reason, String description) {
480             localLog("PnoScanListener onFailure:"
481                     + " reason: " + reason + " description: " + description);
482 
483             // reschedule the scan
484             if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
485                 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
486             } else {
487                 mScanRestartCount = 0;
488                 localLog("Failed to successfully start PNO scan for "
489                         + MAX_SCAN_RESTART_ALLOWED + " times");
490             }
491         }
492 
493         @Override
onPeriodChanged(int periodInMs)494         public void onPeriodChanged(int periodInMs) {
495             localLog("PnoScanListener onPeriodChanged: "
496                     + "actual scan period " + periodInMs + "ms");
497         }
498 
499         // Currently the PNO scan results doesn't include IE,
500         // which contains information required by WifiNetworkSelector. Ignore them
501         // for now.
502         @Override
onResults(WifiScanner.ScanData[] results)503         public void onResults(WifiScanner.ScanData[] results) {
504         }
505 
506         @Override
onFullResult(ScanResult fullScanResult)507         public void onFullResult(ScanResult fullScanResult) {
508         }
509 
510         @Override
onPnoNetworkFound(ScanResult[] results)511         public void onPnoNetworkFound(ScanResult[] results) {
512             for (ScanResult result: results) {
513                 if (result.informationElements == null) {
514                     localLog("Skipping scan result with null information elements");
515                     continue;
516                 }
517                 mScanDetails.add(ScanResultUtil.toScanDetail(result));
518             }
519 
520             boolean wasConnectAttempted;
521             wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener");
522             clearScanDetails();
523             mScanRestartCount = 0;
524 
525             if (!wasConnectAttempted) {
526                 // The scan results were rejected by WifiNetworkSelector due to low RSSI values
527                 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) {
528                     mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS;
529                 }
530                 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay);
531 
532                 // Set up the delay value for next retry.
533                 mLowRssiNetworkRetryDelay *= 2;
534             } else {
535                 resetLowRssiNetworkRetryDelay();
536             }
537         }
538     }
539 
540     private final PnoScanListener mPnoScanListener = new PnoScanListener();
541 
542     private class OnSavedNetworkUpdateListener implements
543             WifiConfigManager.OnSavedNetworkUpdateListener {
544         @Override
onSavedNetworkAdded(int networkId)545         public void onSavedNetworkAdded(int networkId) {
546             updatePnoScan();
547         }
548         @Override
onSavedNetworkEnabled(int networkId)549         public void onSavedNetworkEnabled(int networkId) {
550             updatePnoScan();
551         }
552         @Override
onSavedNetworkRemoved(int networkId)553         public void onSavedNetworkRemoved(int networkId) {
554             updatePnoScan();
555         }
556         @Override
onSavedNetworkUpdated(int networkId)557         public void onSavedNetworkUpdated(int networkId) {
558             // User might have changed meteredOverride, so update capabilties
559             mStateMachine.updateCapabilities();
560             updatePnoScan();
561         }
562         @Override
onSavedNetworkTemporarilyDisabled(int networkId, int disableReason)563         public void onSavedNetworkTemporarilyDisabled(int networkId, int disableReason) {
564             if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return;
565             mConnectivityHelper.removeNetworkIfCurrent(networkId);
566         }
567         @Override
onSavedNetworkPermanentlyDisabled(int networkId, int disableReason)568         public void onSavedNetworkPermanentlyDisabled(int networkId, int disableReason) {
569             // For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network
570             // because supplicant won't be trying to reconnect. If this is due to a
571             // preventAutomaticReconnect request from ConnectivityService, that service
572             // will disconnect as appropriate.
573             if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return;
574             mConnectivityHelper.removeNetworkIfCurrent(networkId);
575             updatePnoScan();
576         }
updatePnoScan()577         private void updatePnoScan() {
578             // Update the PNO scan network list when screen is off. Here we
579             // rely on startConnectivityScan() to perform all the checks and clean up.
580             if (!mScreenOn) {
581                 localLog("Saved networks updated");
582                 startConnectivityScan(false);
583             }
584         }
585     }
586 
587     /**
588      * WifiConnectivityManager constructor
589      */
WifiConnectivityManager(Context context, ScoringParams scoringParams, ClientModeImpl stateMachine, WifiInjector injector, WifiConfigManager configManager, WifiInfo wifiInfo, WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier, CarrierNetworkNotifier carrierNetworkNotifier, CarrierNetworkConfig carrierNetworkConfig, WifiMetrics wifiMetrics, Looper looper, Clock clock, LocalLog localLog)590     WifiConnectivityManager(Context context, ScoringParams scoringParams,
591             ClientModeImpl stateMachine,
592             WifiInjector injector, WifiConfigManager configManager, WifiInfo wifiInfo,
593             WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper,
594             WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier,
595             CarrierNetworkNotifier carrierNetworkNotifier,
596             CarrierNetworkConfig carrierNetworkConfig, WifiMetrics wifiMetrics, Looper looper,
597             Clock clock, LocalLog localLog) {
598         mStateMachine = stateMachine;
599         mWifiInjector = injector;
600         mConfigManager = configManager;
601         mWifiInfo = wifiInfo;
602         mNetworkSelector = networkSelector;
603         mConnectivityHelper = connectivityHelper;
604         mLocalLog = localLog;
605         mWifiLastResortWatchdog = wifiLastResortWatchdog;
606         mOpenNetworkNotifier = openNetworkNotifier;
607         mCarrierNetworkNotifier = carrierNetworkNotifier;
608         mCarrierNetworkConfig = carrierNetworkConfig;
609         mWifiMetrics = wifiMetrics;
610         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
611         mEventHandler = new Handler(looper);
612         mClock = clock;
613         mScoringParams = scoringParams;
614         mConnectionAttemptTimeStamps = new LinkedList<>();
615 
616         mBand5GHzBonus = context.getResources().getInteger(
617                 R.integer.config_wifi_framework_5GHz_preference_boost_factor);
618         mCurrentConnectionBonus = context.getResources().getInteger(
619                 R.integer.config_wifi_framework_current_network_boost);
620         mSameNetworkBonus = context.getResources().getInteger(
621                 R.integer.config_wifi_framework_SAME_BSSID_AWARD);
622         mSecureBonus = context.getResources().getInteger(
623                 R.integer.config_wifi_framework_SECURITY_AWARD);
624         mRssiScoreOffset = context.getResources().getInteger(
625                 R.integer.config_wifi_framework_RSSI_SCORE_OFFSET);
626         mRssiScoreSlope = context.getResources().getInteger(
627                 R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
628         mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
629                 R.bool.config_wifi_framework_enable_associated_network_selection);
630         mUseSingleRadioChainScanResults = context.getResources().getBoolean(
631                 R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection);
632 
633 
634         mFullScanMaxTxRate = context.getResources().getInteger(
635                 R.integer.config_wifi_framework_max_tx_rate_for_full_scan);
636         mFullScanMaxRxRate = context.getResources().getInteger(
637                 R.integer.config_wifi_framework_max_rx_rate_for_full_scan);
638 
639         mPnoScanIntervalMs = MOVING_PNO_SCAN_INTERVAL_MS;
640 
641         localLog("PNO settings:"
642                 + " min5GHzRssi " + mScoringParams.getEntryRssi(ScoringParams.BAND5)
643                 + " min24GHzRssi " + mScoringParams.getEntryRssi(ScoringParams.BAND2)
644                 + " currentConnectionBonus " + mCurrentConnectionBonus
645                 + " sameNetworkBonus " + mSameNetworkBonus
646                 + " secureNetworkBonus " + mSecureBonus
647                 + " initialScoreMax " + initialScoreMax());
648 
649         // Listen to WifiConfigManager network update events
650         mConfigManager.setOnSavedNetworkUpdateListener(new OnSavedNetworkUpdateListener());
651     }
652 
653     /** Returns maximum PNO score, before any awards/bonuses. */
initialScoreMax()654     private int initialScoreMax() {
655         return mRssiScoreSlope * (Math.max(mScoringParams.getGoodRssi(ScoringParams.BAND2),
656                                            mScoringParams.getGoodRssi(ScoringParams.BAND5))
657                                   + mRssiScoreOffset);
658     }
659 
660     /**
661      * This checks the connection attempt rate and recommends whether the connection attempt
662      * should be skipped or not. This attempts to rate limit the rate of connections to
663      * prevent us from flapping between networks and draining battery rapidly.
664      */
shouldSkipConnectionAttempt(Long timeMillis)665     private boolean shouldSkipConnectionAttempt(Long timeMillis) {
666         Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator();
667         // First evict old entries from the queue.
668         while (attemptIter.hasNext()) {
669             Long connectionAttemptTimeMillis = attemptIter.next();
670             if ((timeMillis - connectionAttemptTimeMillis)
671                     > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) {
672                 attemptIter.remove();
673             } else {
674                 // This list is sorted by timestamps, so we can skip any more checks
675                 break;
676             }
677         }
678         // If we've reached the max connection attempt rate, skip this connection attempt
679         return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE);
680     }
681 
682     /**
683      * Add the current connection attempt timestamp to our queue of connection attempts.
684      */
noteConnectionAttempt(Long timeMillis)685     private void noteConnectionAttempt(Long timeMillis) {
686         mConnectionAttemptTimeStamps.addLast(timeMillis);
687     }
688 
689     /**
690      * This is used to clear the connection attempt rate limiter. This is done when the user
691      * explicitly tries to connect to a specified network.
692      */
clearConnectionAttemptTimeStamps()693     private void clearConnectionAttemptTimeStamps() {
694         mConnectionAttemptTimeStamps.clear();
695     }
696 
697     /**
698      * Attempt to connect to a network candidate.
699      *
700      * Based on the currently connected network, this menthod determines whether we should
701      * connect or roam to the network candidate recommended by WifiNetworkSelector.
702      */
connectToNetwork(WifiConfiguration candidate)703     private void connectToNetwork(WifiConfiguration candidate) {
704         ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
705         if (scanResultCandidate == null) {
706             localLog("connectToNetwork: bad candidate - "  + candidate
707                     + " scanResult: " + scanResultCandidate);
708             return;
709         }
710 
711         String targetBssid = scanResultCandidate.BSSID;
712         String targetAssociationId = candidate.SSID + " : " + targetBssid;
713 
714         // Check if we are already connected or in the process of connecting to the target
715         // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just
716         // in case the firmware automatically roamed to a BSSID different from what
717         // WifiNetworkSelector selected.
718         if (targetBssid != null
719                 && (targetBssid.equals(mLastConnectionAttemptBssid)
720                     || targetBssid.equals(mWifiInfo.getBSSID()))
721                 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
722             localLog("connectToNetwork: Either already connected "
723                     + "or is connecting to " + targetAssociationId);
724             return;
725         }
726 
727         if (candidate.BSSID != null
728                 && !candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)
729                 && !candidate.BSSID.equals(targetBssid)) {
730             localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the "
731                     + "config specified BSSID " + candidate.BSSID + ". Drop it!");
732             return;
733         }
734 
735         long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
736         if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
737             localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
738             mTotalConnectivityAttemptsRateLimited++;
739             return;
740         }
741         noteConnectionAttempt(elapsedTimeMillis);
742 
743         mLastConnectionAttemptBssid = targetBssid;
744 
745         WifiConfiguration currentConnectedNetwork = mConfigManager
746                 .getConfiguredNetwork(mWifiInfo.getNetworkId());
747         String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
748                 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
749 
750         if (currentConnectedNetwork != null
751                 && (currentConnectedNetwork.networkId == candidate.networkId
752                 //TODO(b/36788683): re-enable linked configuration check
753                 /* || currentConnectedNetwork.isLinked(candidate) */)) {
754             // Framework initiates roaming only if firmware doesn't support
755             // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}.
756             if (mConnectivityHelper.isFirmwareRoamingSupported()) {
757                 // Keep this logging here for now to validate the firmware roaming behavior.
758                 localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "."
759                         + " The actual roaming target is up to the firmware.");
760             } else {
761                 localLog("connectToNetwork: Roaming to " + targetAssociationId + " from "
762                         + currentAssociationId);
763                 mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate);
764             }
765         } else {
766             // Framework specifies the connection target BSSID if firmware doesn't support
767             // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the
768             // candidate configuration contains a specified BSSID.
769             if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null
770                       || candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY))) {
771                 targetBssid = ClientModeImpl.SUPPLICANT_BSSID_ANY;
772                 localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid
773                         + " from " + currentAssociationId);
774             } else {
775                 localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
776                         + currentAssociationId);
777             }
778             mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid);
779         }
780     }
781 
782     // Helper for selecting the band for connectivity scan
getScanBand()783     private int getScanBand() {
784         return getScanBand(true);
785     }
786 
getScanBand(boolean isFullBandScan)787     private int getScanBand(boolean isFullBandScan) {
788         if (isFullBandScan) {
789             return WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
790         } else {
791             // Use channel list instead.
792             return WifiScanner.WIFI_BAND_UNSPECIFIED;
793         }
794     }
795 
796     // Helper for setting the channels for connectivity scan when band is unspecified. Returns
797     // false if we can't retrieve the info.
setScanChannels(ScanSettings settings)798     private boolean setScanChannels(ScanSettings settings) {
799         WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration();
800 
801         if (config == null) {
802             return false;
803         }
804 
805         Set<Integer> freqs =
806                 mConfigManager.fetchChannelSetForNetworkForPartialScan(
807                         config.networkId, CHANNEL_LIST_AGE_MS, mWifiInfo.getFrequency());
808 
809         if (freqs != null && freqs.size() != 0) {
810             int index = 0;
811             settings.channels = new WifiScanner.ChannelSpec[freqs.size()];
812             for (Integer freq : freqs) {
813                 settings.channels[index++] = new WifiScanner.ChannelSpec(freq);
814             }
815             return true;
816         } else {
817             localLog("No scan channels for " + config.configKey() + ". Perform full band scan");
818             return false;
819         }
820     }
821 
822     // Watchdog timer handler
watchdogHandler()823     private void watchdogHandler() {
824         // Schedule the next timer and start a single scan if we are in disconnected state.
825         // Otherwise, the watchdog timer will be scheduled when entering disconnected
826         // state.
827         if (mWifiState == WIFI_STATE_DISCONNECTED) {
828             localLog("start a single scan from watchdogHandler");
829 
830             scheduleWatchdogTimer();
831             startSingleScan(true, WIFI_WORK_SOURCE);
832         }
833     }
834 
835     // Start a single scan and set up the interval for next single scan.
startPeriodicSingleScan()836     private void startPeriodicSingleScan() {
837         long currentTimeStamp = mClock.getElapsedSinceBootMillis();
838 
839         if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) {
840             long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp;
841             if (msSinceLastScan < PERIODIC_SCAN_INTERVAL_MS) {
842                 localLog("Last periodic single scan started " + msSinceLastScan
843                         + "ms ago, defer this new scan request.");
844                 schedulePeriodicScanTimer(PERIODIC_SCAN_INTERVAL_MS - (int) msSinceLastScan);
845                 return;
846             }
847         }
848 
849         boolean isScanNeeded = true;
850         boolean isFullBandScan = true;
851         boolean isTrafficOverThreshold = mWifiInfo.txSuccessRate > mFullScanMaxTxRate
852                 || mWifiInfo.rxSuccessRate > mFullScanMaxRxRate;
853 
854         // If the WiFi traffic is heavy, only partial scan is proposed.
855         if (mWifiState == WIFI_STATE_CONNECTED && isTrafficOverThreshold) {
856             // If only partial scan is proposed and firmware roaming control is supported,
857             // we will not issue any scan because firmware roaming will take care of
858             // intra-SSID roam.
859             if (mConnectivityHelper.isFirmwareRoamingSupported()) {
860                 localLog("No partial scan because firmware roaming is supported.");
861                 isScanNeeded = false;
862             } else {
863                 localLog("No full band scan due to ongoing traffic");
864                 isFullBandScan = false;
865             }
866         }
867 
868         if (isScanNeeded) {
869             mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
870             startSingleScan(isFullBandScan, WIFI_WORK_SOURCE);
871             schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
872 
873             // Set up the next scan interval in an exponential backoff fashion.
874             mPeriodicSingleScanInterval *= 2;
875             if (mPeriodicSingleScanInterval >  MAX_PERIODIC_SCAN_INTERVAL_MS) {
876                 mPeriodicSingleScanInterval = MAX_PERIODIC_SCAN_INTERVAL_MS;
877             }
878         } else {
879             // Since we already skipped this scan, keep the same scan interval for next scan.
880             schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
881         }
882     }
883 
884     // Reset the last periodic single scan time stamp so that the next periodic single
885     // scan can start immediately.
resetLastPeriodicSingleScanTimeStamp()886     private void resetLastPeriodicSingleScanTimeStamp() {
887         mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
888     }
889 
890     // Periodic scan timer handler
periodicScanTimerHandler()891     private void periodicScanTimerHandler() {
892         localLog("periodicScanTimerHandler");
893 
894         // Schedule the next timer and start a single scan if screen is on.
895         if (mScreenOn) {
896             startPeriodicSingleScan();
897         }
898     }
899 
900     // Start a single scan
startSingleScan(boolean isFullBandScan, WorkSource workSource)901     private void startSingleScan(boolean isFullBandScan, WorkSource workSource) {
902         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
903             return;
904         }
905 
906         mPnoScanListener.resetLowRssiNetworkRetryDelay();
907 
908         ScanSettings settings = new ScanSettings();
909         if (!isFullBandScan) {
910             if (!setScanChannels(settings)) {
911                 isFullBandScan = true;
912             }
913         }
914         settings.type = WifiScanner.TYPE_HIGH_ACCURACY; // always do high accuracy scans.
915         settings.band = getScanBand(isFullBandScan);
916         settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
917                             | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
918         settings.numBssidsPerScan = 0;
919         // retrieve the list of hidden network SSIDs from saved network to scan for
920         List<ScanSettings.HiddenNetwork> hiddenNetworkList =
921                 new ArrayList<>(mConfigManager.retrieveHiddenNetworkList());
922         // retrieve the list of hidden network SSIDs from Network suggestion to scan for
923         hiddenNetworkList.addAll(
924                 mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList());
925         settings.hiddenNetworks =
926                 hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[0]);
927 
928         SingleScanListener singleScanListener =
929                 new SingleScanListener(isFullBandScan);
930         mScanner.startScan(settings, singleScanListener, workSource);
931         mWifiMetrics.incrementConnectivityOneshotScanCount();
932     }
933 
934     // Start a periodic scan when screen is on
startPeriodicScan(boolean scanImmediately)935     private void startPeriodicScan(boolean scanImmediately) {
936         mPnoScanListener.resetLowRssiNetworkRetryDelay();
937 
938         // No connectivity scan if auto roaming is disabled.
939         if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) {
940             return;
941         }
942 
943         // Due to b/28020168, timer based single scan will be scheduled
944         // to provide periodic scan in an exponential backoff fashion.
945         if (scanImmediately) {
946             resetLastPeriodicSingleScanTimeStamp();
947         }
948         mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
949         startPeriodicSingleScan();
950     }
951 
deviceMobilityStateToPnoScanIntervalMs(@eviceMobilityState int state)952     private static int deviceMobilityStateToPnoScanIntervalMs(@DeviceMobilityState int state) {
953         switch (state) {
954             case WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN:
955             case WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT:
956             case WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT:
957                 return MOVING_PNO_SCAN_INTERVAL_MS;
958             case WifiManager.DEVICE_MOBILITY_STATE_STATIONARY:
959                 return STATIONARY_PNO_SCAN_INTERVAL_MS;
960             default:
961                 return -1;
962         }
963     }
964 
965     /**
966      * Alters the PNO scan interval based on the current device mobility state.
967      * If the device is stationary, it will likely not find many new Wifi networks. Thus, increase
968      * the interval between scans. Decrease the interval between scans if the device begins to move
969      * again.
970      * @param newState the new device mobility state
971      */
setDeviceMobilityState(@eviceMobilityState int newState)972     public void setDeviceMobilityState(@DeviceMobilityState int newState) {
973         int newPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(newState);
974         if (newPnoScanIntervalMs < 0) {
975             Log.e(TAG, "Invalid device mobility state: " + newState);
976             return;
977         }
978 
979         if (newPnoScanIntervalMs == mPnoScanIntervalMs) {
980             if (mPnoScanStarted) {
981                 mWifiMetrics.logPnoScanStop();
982                 mWifiMetrics.enterDeviceMobilityState(newState);
983                 mWifiMetrics.logPnoScanStart();
984             } else {
985                 mWifiMetrics.enterDeviceMobilityState(newState);
986             }
987         } else {
988             mPnoScanIntervalMs = newPnoScanIntervalMs;
989             Log.d(TAG, "PNO Scan Interval changed to " + mPnoScanIntervalMs + " ms.");
990 
991             if (mPnoScanStarted) {
992                 Log.d(TAG, "Restarting PNO Scan with new scan interval");
993                 stopPnoScan();
994                 mWifiMetrics.enterDeviceMobilityState(newState);
995                 startDisconnectedPnoScan();
996             } else {
997                 mWifiMetrics.enterDeviceMobilityState(newState);
998             }
999         }
1000     }
1001 
1002     // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
startDisconnectedPnoScan()1003     private void startDisconnectedPnoScan() {
1004         // Initialize PNO settings
1005         PnoSettings pnoSettings = new PnoSettings();
1006         List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList();
1007         int listSize = pnoNetworkList.size();
1008 
1009         if (listSize == 0) {
1010             // No saved network
1011             localLog("No saved network for starting disconnected PNO.");
1012             return;
1013         }
1014 
1015         pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
1016         pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
1017         pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScoringParams.BAND5);
1018         pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi(ScoringParams.BAND2);
1019         pnoSettings.initialScoreMax = initialScoreMax();
1020         pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
1021         pnoSettings.sameNetworkBonus = mSameNetworkBonus;
1022         pnoSettings.secureBonus = mSecureBonus;
1023         pnoSettings.band5GHzBonus = mBand5GHzBonus;
1024 
1025         // Initialize scan settings
1026         ScanSettings scanSettings = new ScanSettings();
1027         scanSettings.band = getScanBand();
1028         scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
1029         scanSettings.numBssidsPerScan = 0;
1030         scanSettings.periodInMs = mPnoScanIntervalMs;
1031 
1032         mPnoScanListener.clearScanDetails();
1033 
1034         mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
1035         mPnoScanStarted = true;
1036         mWifiMetrics.logPnoScanStart();
1037     }
1038 
1039     // Stop PNO scan.
stopPnoScan()1040     private void stopPnoScan() {
1041         if (!mPnoScanStarted) return;
1042 
1043         mScanner.stopPnoScan(mPnoScanListener);
1044         mPnoScanStarted = false;
1045         mWifiMetrics.logPnoScanStop();
1046     }
1047 
1048     // Set up watchdog timer
scheduleWatchdogTimer()1049     private void scheduleWatchdogTimer() {
1050         localLog("scheduleWatchdogTimer");
1051 
1052         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1053                             mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS,
1054                             WATCHDOG_TIMER_TAG,
1055                             mWatchdogListener, mEventHandler);
1056     }
1057 
1058     // Set up periodic scan timer
schedulePeriodicScanTimer(int intervalMs)1059     private void schedulePeriodicScanTimer(int intervalMs) {
1060         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1061                             mClock.getElapsedSinceBootMillis() + intervalMs,
1062                             PERIODIC_SCAN_TIMER_TAG,
1063                             mPeriodicScanTimerListener, mEventHandler);
1064         mPeriodicScanTimerSet = true;
1065     }
1066 
1067     // Cancel periodic scan timer
cancelPeriodicScanTimer()1068     private void cancelPeriodicScanTimer() {
1069         if (mPeriodicScanTimerSet) {
1070             mAlarmManager.cancel(mPeriodicScanTimerListener);
1071             mPeriodicScanTimerSet = false;
1072         }
1073     }
1074 
1075     // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
scheduleDelayedSingleScan(boolean isFullBandScan)1076     private void scheduleDelayedSingleScan(boolean isFullBandScan) {
1077         localLog("scheduleDelayedSingleScan");
1078 
1079         RestartSingleScanListener restartSingleScanListener =
1080                 new RestartSingleScanListener(isFullBandScan);
1081         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1082                             mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS,
1083                             RESTART_SINGLE_SCAN_TIMER_TAG,
1084                             restartSingleScanListener, mEventHandler);
1085     }
1086 
1087     // Set up timer to start a delayed scan after msFromNow milli-seconds
scheduleDelayedConnectivityScan(int msFromNow)1088     private void scheduleDelayedConnectivityScan(int msFromNow) {
1089         localLog("scheduleDelayedConnectivityScan");
1090 
1091         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1092                             mClock.getElapsedSinceBootMillis() + msFromNow,
1093                             RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
1094                             mRestartScanListener, mEventHandler);
1095 
1096     }
1097 
1098     // Start a connectivity scan. The scan method is chosen according to
1099     // the current screen state and WiFi state.
startConnectivityScan(boolean scanImmediately)1100     private void startConnectivityScan(boolean scanImmediately) {
1101         localLog("startConnectivityScan: screenOn=" + mScreenOn
1102                 + " wifiState=" + stateToString(mWifiState)
1103                 + " scanImmediately=" + scanImmediately
1104                 + " wifiEnabled=" + mWifiEnabled
1105                 + " wifiConnectivityManagerEnabled="
1106                 + mWifiConnectivityManagerEnabled);
1107 
1108         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
1109             return;
1110         }
1111 
1112         // Always stop outstanding connecivity scan if there is any
1113         stopConnectivityScan();
1114 
1115         // Don't start a connectivity scan while Wifi is in the transition
1116         // between connected and disconnected states.
1117         if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {
1118             return;
1119         }
1120 
1121         if (mScreenOn) {
1122             startPeriodicScan(scanImmediately);
1123         } else {
1124             if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
1125                 startDisconnectedPnoScan();
1126             }
1127         }
1128 
1129     }
1130 
1131     // Stop connectivity scan if there is any.
stopConnectivityScan()1132     private void stopConnectivityScan() {
1133         // Due to b/28020168, timer based single scan will be scheduled
1134         // to provide periodic scan in an exponential backoff fashion.
1135         cancelPeriodicScanTimer();
1136         stopPnoScan();
1137         mScanRestartCount = 0;
1138     }
1139 
1140     /**
1141      * Handler for screen state (on/off) changes
1142      */
handleScreenStateChanged(boolean screenOn)1143     public void handleScreenStateChanged(boolean screenOn) {
1144         localLog("handleScreenStateChanged: screenOn=" + screenOn);
1145 
1146         mScreenOn = screenOn;
1147 
1148         mOpenNetworkNotifier.handleScreenStateChanged(screenOn);
1149         mCarrierNetworkNotifier.handleScreenStateChanged(screenOn);
1150 
1151         startConnectivityScan(SCAN_ON_SCHEDULE);
1152     }
1153 
1154     /**
1155      * Helper function that converts the WIFI_STATE_XXX constants to string
1156      */
stateToString(int state)1157     private static String stateToString(int state) {
1158         switch (state) {
1159             case WIFI_STATE_CONNECTED:
1160                 return "connected";
1161             case WIFI_STATE_DISCONNECTED:
1162                 return "disconnected";
1163             case WIFI_STATE_TRANSITIONING:
1164                 return "transitioning";
1165             default:
1166                 return "unknown";
1167         }
1168     }
1169 
1170     /**
1171      * Handler for WiFi state (connected/disconnected) changes
1172      */
handleConnectionStateChanged(int state)1173     public void handleConnectionStateChanged(int state) {
1174         localLog("handleConnectionStateChanged: state=" + stateToString(state));
1175 
1176         mWifiState = state;
1177 
1178         // Reset BSSID of last connection attempt and kick off
1179         // the watchdog timer if entering disconnected state.
1180         if (mWifiState == WIFI_STATE_DISCONNECTED) {
1181             mLastConnectionAttemptBssid = null;
1182             scheduleWatchdogTimer();
1183             startConnectivityScan(SCAN_IMMEDIATELY);
1184         } else {
1185             startConnectivityScan(SCAN_ON_SCHEDULE);
1186         }
1187     }
1188 
1189     /**
1190      * Handler when a WiFi connection attempt ended.
1191      *
1192      * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code.
1193      */
handleConnectionAttemptEnded(int failureCode)1194     public void handleConnectionAttemptEnded(int failureCode) {
1195         if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) {
1196             String ssid = (mWifiInfo.getWifiSsid() == null)
1197                     ? null
1198                     : mWifiInfo.getWifiSsid().toString();
1199             mOpenNetworkNotifier.handleWifiConnected(ssid);
1200             mCarrierNetworkNotifier.handleWifiConnected(ssid);
1201         } else {
1202             mOpenNetworkNotifier.handleConnectionFailure();
1203             mCarrierNetworkNotifier.handleConnectionFailure();
1204         }
1205     }
1206 
1207     // Enable auto-join if we have any pending network request (trusted or untrusted) and no
1208     // specific network request in progress.
checkStateAndEnable()1209     private void checkStateAndEnable() {
1210         enable(!mSpecificNetworkRequestInProgress
1211                 && (mUntrustedConnectionAllowed || mTrustedConnectionAllowed));
1212         startConnectivityScan(SCAN_IMMEDIATELY);
1213     }
1214 
1215     /**
1216      * Triggered when {@link WifiNetworkFactory} has a pending general network request.
1217      */
setTrustedConnectionAllowed(boolean allowed)1218     public void setTrustedConnectionAllowed(boolean allowed) {
1219         localLog("setTrustedConnectionAllowed: allowed=" + allowed);
1220 
1221         if (mTrustedConnectionAllowed != allowed) {
1222             mTrustedConnectionAllowed = allowed;
1223             checkStateAndEnable();
1224         }
1225     }
1226 
1227 
1228     /**
1229      * Triggered when {@link UntrustedWifiNetworkFactory} has a pending ephemeral network request.
1230      */
setUntrustedConnectionAllowed(boolean allowed)1231     public void setUntrustedConnectionAllowed(boolean allowed) {
1232         localLog("setUntrustedConnectionAllowed: allowed=" + allowed);
1233 
1234         if (mUntrustedConnectionAllowed != allowed) {
1235             mUntrustedConnectionAllowed = allowed;
1236             checkStateAndEnable();
1237         }
1238     }
1239 
1240     /**
1241      * Triggered when {@link WifiNetworkFactory} is processing a specific network request.
1242      */
setSpecificNetworkRequestInProgress(boolean inProgress)1243     public void setSpecificNetworkRequestInProgress(boolean inProgress) {
1244         localLog("setsetSpecificNetworkRequestInProgress : inProgress=" + inProgress);
1245 
1246         if (mSpecificNetworkRequestInProgress != inProgress) {
1247             mSpecificNetworkRequestInProgress = inProgress;
1248             checkStateAndEnable();
1249         }
1250     }
1251 
1252     /**
1253      * Handler when user specifies a particular network to connect to
1254      */
setUserConnectChoice(int netId)1255     public void setUserConnectChoice(int netId) {
1256         localLog("setUserConnectChoice: netId=" + netId);
1257 
1258         mNetworkSelector.setUserConnectChoice(netId);
1259     }
1260 
1261     /**
1262      * Handler to prepare for connection to a user or app specified network
1263      */
prepareForForcedConnection(int netId)1264     public void prepareForForcedConnection(int netId) {
1265         localLog("prepareForForcedConnection: netId=" + netId);
1266 
1267         clearConnectionAttemptTimeStamps();
1268         clearBssidBlacklist();
1269     }
1270 
1271     /**
1272      * Handler for on-demand connectivity scan
1273      */
forceConnectivityScan(WorkSource workSource)1274     public void forceConnectivityScan(WorkSource workSource) {
1275         localLog("forceConnectivityScan in request of " + workSource);
1276 
1277         mWaitForFullBandScanResults = true;
1278         startSingleScan(true, workSource);
1279     }
1280 
1281     /**
1282      * Update the BSSID blacklist when a BSSID is enabled or disabled
1283      *
1284      * @param bssid the bssid to be enabled/disabled
1285      * @param enable -- true enable the bssid
1286      *               -- false disable the bssid
1287      * @param reasonCode enable/disable reason code
1288      * @return true if blacklist is updated; false otherwise
1289      */
updateBssidBlacklist(String bssid, boolean enable, int reasonCode)1290     private boolean updateBssidBlacklist(String bssid, boolean enable, int reasonCode) {
1291         // Remove the bssid from blacklist when it is enabled.
1292         if (enable) {
1293             return mBssidBlacklist.remove(bssid) != null;
1294         }
1295 
1296         // Do not update BSSID blacklist with information if this is the only
1297         // BSSID for its SSID. By ignoring it we will cause additional failures
1298         // which will trigger Watchdog.
1299         if (mWifiLastResortWatchdog.shouldIgnoreBssidUpdate(bssid)) {
1300             localLog("Ignore update Bssid Blacklist since Watchdog trigger is activated");
1301             return false;
1302         }
1303 
1304         // Update the bssid's blacklist status when it is disabled because of
1305         // association rejection.
1306         BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
1307         if (status == null) {
1308             // First time for this BSSID
1309             status = new BssidBlacklistStatus();
1310             mBssidBlacklist.put(bssid, status);
1311         }
1312 
1313         status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis();
1314         status.counter++;
1315         if (!status.isBlacklisted) {
1316             if (status.counter >= BSSID_BLACKLIST_THRESHOLD
1317                     || reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) {
1318                 status.isBlacklisted = true;
1319                 return true;
1320             }
1321         }
1322         return false;
1323     }
1324 
1325     /**
1326      * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector
1327      *
1328      * @param bssid the bssid to be enabled/disabled
1329      * @param enable -- true enable the bssid
1330      *               -- false disable the bssid
1331      * @param reasonCode enable/disable reason code
1332      * @return true if blacklist is updated; false otherwise
1333      */
trackBssid(String bssid, boolean enable, int reasonCode)1334     public boolean trackBssid(String bssid, boolean enable, int reasonCode) {
1335         localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid + " reason code "
1336                 + reasonCode);
1337 
1338         if (bssid == null) {
1339             return false;
1340         }
1341 
1342         if (!updateBssidBlacklist(bssid, enable, reasonCode)) {
1343             return false;
1344         }
1345 
1346         // Blacklist was updated, so update firmware roaming configuration.
1347         updateFirmwareRoamingConfiguration();
1348 
1349         if (!enable) {
1350             // Disabling a BSSID can happen when connection to the AP was rejected.
1351             // We start another scan immediately so that WifiNetworkSelector can
1352             // give us another candidate to connect to.
1353             startConnectivityScan(SCAN_IMMEDIATELY);
1354         }
1355 
1356         return true;
1357     }
1358 
1359     /**
1360      * Check whether a bssid is disabled
1361      */
1362     @VisibleForTesting
isBssidDisabled(String bssid)1363     public boolean isBssidDisabled(String bssid) {
1364         BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
1365         return status == null ? false : status.isBlacklisted;
1366     }
1367 
1368     /**
1369      * Compile and return a hashset of the blacklisted BSSIDs
1370      */
buildBssidBlacklist()1371     private HashSet<String> buildBssidBlacklist() {
1372         HashSet<String> blacklistedBssids = new HashSet<String>();
1373         for (String bssid : mBssidBlacklist.keySet()) {
1374             if (isBssidDisabled(bssid)) {
1375                 blacklistedBssids.add(bssid);
1376             }
1377         }
1378 
1379         return blacklistedBssids;
1380     }
1381 
1382     /**
1383      * Update firmware roaming configuration if the firmware roaming feature is supported.
1384      * Compile and write the BSSID blacklist only. TODO(b/36488259): SSID whitelist is always
1385      * empty for now.
1386      */
updateFirmwareRoamingConfiguration()1387     private void updateFirmwareRoamingConfiguration() {
1388         if (!mConnectivityHelper.isFirmwareRoamingSupported()) {
1389             return;
1390         }
1391 
1392         int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid();
1393         if (maxBlacklistSize <= 0) {
1394             Log.wtf(TAG, "Invalid max BSSID blacklist size:  " + maxBlacklistSize);
1395             return;
1396         }
1397 
1398         ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist());
1399         int blacklistSize = blacklistedBssids.size();
1400 
1401         if (blacklistSize > maxBlacklistSize) {
1402             Log.wtf(TAG, "Attempt to write " + blacklistSize + " blacklisted BSSIDs, max size is "
1403                     + maxBlacklistSize);
1404 
1405             blacklistedBssids = new ArrayList<String>(blacklistedBssids.subList(0,
1406                     maxBlacklistSize));
1407             localLog("Trim down BSSID blacklist size from " + blacklistSize + " to "
1408                     + blacklistedBssids.size());
1409         }
1410 
1411         if (!mConnectivityHelper.setFirmwareRoamingConfiguration(blacklistedBssids,
1412                 new ArrayList<String>())) {  // TODO(b/36488259): SSID whitelist management.
1413             localLog("Failed to set firmware roaming configuration.");
1414         }
1415     }
1416 
1417     /**
1418      * Refresh the BSSID blacklist
1419      *
1420      * Go through the BSSID blacklist and check if a BSSID has been blacklisted for
1421      * BSSID_BLACKLIST_EXPIRE_TIME_MS. If yes, re-enable it.
1422      */
refreshBssidBlacklist()1423     private void refreshBssidBlacklist() {
1424         if (mBssidBlacklist.isEmpty()) {
1425             return;
1426         }
1427 
1428         boolean updated = false;
1429         Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator();
1430         Long currentTimeStamp = mClock.getElapsedSinceBootMillis();
1431 
1432         while (iter.hasNext()) {
1433             BssidBlacklistStatus status = iter.next();
1434             if (status.isBlacklisted && ((currentTimeStamp - status.blacklistedTimeStamp)
1435                     >= BSSID_BLACKLIST_EXPIRE_TIME_MS)) {
1436                 iter.remove();
1437                 updated = true;
1438             }
1439         }
1440 
1441         if (updated) {
1442             updateFirmwareRoamingConfiguration();
1443         }
1444     }
1445 
1446     /**
1447      * Helper method to populate WifiScanner handle. This is done lazily because
1448      * WifiScanningService is started after WifiService.
1449      */
retrieveWifiScanner()1450     private void retrieveWifiScanner() {
1451         if (mScanner != null) return;
1452         mScanner = mWifiInjector.getWifiScanner();
1453         checkNotNull(mScanner);
1454         // Register for all single scan results
1455         mScanner.registerScanListener(mAllSingleScanListener);
1456     }
1457 
1458 
1459     /**
1460      * Clear the BSSID blacklist
1461      */
clearBssidBlacklist()1462     private void clearBssidBlacklist() {
1463         mBssidBlacklist.clear();
1464         updateFirmwareRoamingConfiguration();
1465     }
1466 
1467     /**
1468      * Start WifiConnectivityManager
1469      */
start()1470     private void start() {
1471         if (mRunning) return;
1472         retrieveWifiScanner();
1473         mConnectivityHelper.getFirmwareRoamingInfo();
1474         clearBssidBlacklist();
1475         mRunning = true;
1476     }
1477 
1478     /**
1479      * Stop and reset WifiConnectivityManager
1480      */
stop()1481     private void stop() {
1482         if (!mRunning) return;
1483         mRunning = false;
1484         stopConnectivityScan();
1485         clearBssidBlacklist();
1486         resetLastPeriodicSingleScanTimeStamp();
1487         mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */);
1488         mCarrierNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */);
1489         mLastConnectionAttemptBssid = null;
1490         mWaitForFullBandScanResults = false;
1491     }
1492 
1493     /**
1494      * Update WifiConnectivityManager running state
1495      *
1496      * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager
1497      * are enabled, otherwise stop it.
1498      */
updateRunningState()1499     private void updateRunningState() {
1500         if (mWifiEnabled && mWifiConnectivityManagerEnabled) {
1501             localLog("Starting up WifiConnectivityManager");
1502             start();
1503         } else {
1504             localLog("Stopping WifiConnectivityManager");
1505             stop();
1506         }
1507     }
1508 
1509     /**
1510      * Inform WiFi is enabled for connection or not
1511      */
setWifiEnabled(boolean enable)1512     public void setWifiEnabled(boolean enable) {
1513         localLog("Set WiFi " + (enable ? "enabled" : "disabled"));
1514 
1515         mWifiEnabled = enable;
1516         updateRunningState();
1517     }
1518 
1519     /**
1520      * Turn on/off the WifiConnectivityManager at runtime
1521      */
enable(boolean enable)1522     public void enable(boolean enable) {
1523         localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
1524 
1525         mWifiConnectivityManagerEnabled = enable;
1526         updateRunningState();
1527     }
1528 
1529     @VisibleForTesting
getLowRssiNetworkRetryDelay()1530     int getLowRssiNetworkRetryDelay() {
1531         return mPnoScanListener.getLowRssiNetworkRetryDelay();
1532     }
1533 
1534     @VisibleForTesting
getLastPeriodicSingleScanTimeStamp()1535     long getLastPeriodicSingleScanTimeStamp() {
1536         return mLastPeriodicSingleScanTimeStamp;
1537     }
1538 
1539     /**
1540      * Dump the local logs.
1541      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)1542     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1543         pw.println("Dump of WifiConnectivityManager");
1544         pw.println("WifiConnectivityManager - Log Begin ----");
1545         mLocalLog.dump(fd, pw, args);
1546         pw.println("WifiConnectivityManager - Log End ----");
1547         mOpenNetworkNotifier.dump(fd, pw, args);
1548         mCarrierNetworkNotifier.dump(fd, pw, args);
1549         mCarrierNetworkConfig.dump(fd, pw, args);
1550     }
1551 }
1552