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 
920         List<ScanSettings.HiddenNetwork> hiddenNetworkList =
921                 mConfigManager.retrieveHiddenNetworkList();
922         settings.hiddenNetworks =
923                 hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
924 
925         SingleScanListener singleScanListener =
926                 new SingleScanListener(isFullBandScan);
927         mScanner.startScan(settings, singleScanListener, workSource);
928         mWifiMetrics.incrementConnectivityOneshotScanCount();
929     }
930 
931     // Start a periodic scan when screen is on
startPeriodicScan(boolean scanImmediately)932     private void startPeriodicScan(boolean scanImmediately) {
933         mPnoScanListener.resetLowRssiNetworkRetryDelay();
934 
935         // No connectivity scan if auto roaming is disabled.
936         if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) {
937             return;
938         }
939 
940         // Due to b/28020168, timer based single scan will be scheduled
941         // to provide periodic scan in an exponential backoff fashion.
942         if (scanImmediately) {
943             resetLastPeriodicSingleScanTimeStamp();
944         }
945         mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
946         startPeriodicSingleScan();
947     }
948 
deviceMobilityStateToPnoScanIntervalMs(@eviceMobilityState int state)949     private static int deviceMobilityStateToPnoScanIntervalMs(@DeviceMobilityState int state) {
950         switch (state) {
951             case WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN:
952             case WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT:
953             case WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT:
954                 return MOVING_PNO_SCAN_INTERVAL_MS;
955             case WifiManager.DEVICE_MOBILITY_STATE_STATIONARY:
956                 return STATIONARY_PNO_SCAN_INTERVAL_MS;
957             default:
958                 return -1;
959         }
960     }
961 
962     /**
963      * Alters the PNO scan interval based on the current device mobility state.
964      * If the device is stationary, it will likely not find many new Wifi networks. Thus, increase
965      * the interval between scans. Decrease the interval between scans if the device begins to move
966      * again.
967      * @param newState the new device mobility state
968      */
setDeviceMobilityState(@eviceMobilityState int newState)969     public void setDeviceMobilityState(@DeviceMobilityState int newState) {
970         int newPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(newState);
971         if (newPnoScanIntervalMs < 0) {
972             Log.e(TAG, "Invalid device mobility state: " + newState);
973             return;
974         }
975 
976         if (newPnoScanIntervalMs == mPnoScanIntervalMs) {
977             if (mPnoScanStarted) {
978                 mWifiMetrics.logPnoScanStop();
979                 mWifiMetrics.enterDeviceMobilityState(newState);
980                 mWifiMetrics.logPnoScanStart();
981             } else {
982                 mWifiMetrics.enterDeviceMobilityState(newState);
983             }
984         } else {
985             mPnoScanIntervalMs = newPnoScanIntervalMs;
986             Log.d(TAG, "PNO Scan Interval changed to " + mPnoScanIntervalMs + " ms.");
987 
988             if (mPnoScanStarted) {
989                 Log.d(TAG, "Restarting PNO Scan with new scan interval");
990                 stopPnoScan();
991                 mWifiMetrics.enterDeviceMobilityState(newState);
992                 startDisconnectedPnoScan();
993             } else {
994                 mWifiMetrics.enterDeviceMobilityState(newState);
995             }
996         }
997     }
998 
999     // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
startDisconnectedPnoScan()1000     private void startDisconnectedPnoScan() {
1001         // Initialize PNO settings
1002         PnoSettings pnoSettings = new PnoSettings();
1003         List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList();
1004         int listSize = pnoNetworkList.size();
1005 
1006         if (listSize == 0) {
1007             // No saved network
1008             localLog("No saved network for starting disconnected PNO.");
1009             return;
1010         }
1011 
1012         pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
1013         pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
1014         pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScoringParams.BAND5);
1015         pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi(ScoringParams.BAND2);
1016         pnoSettings.initialScoreMax = initialScoreMax();
1017         pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
1018         pnoSettings.sameNetworkBonus = mSameNetworkBonus;
1019         pnoSettings.secureBonus = mSecureBonus;
1020         pnoSettings.band5GHzBonus = mBand5GHzBonus;
1021 
1022         // Initialize scan settings
1023         ScanSettings scanSettings = new ScanSettings();
1024         scanSettings.band = getScanBand();
1025         scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
1026         scanSettings.numBssidsPerScan = 0;
1027         scanSettings.periodInMs = mPnoScanIntervalMs;
1028 
1029         mPnoScanListener.clearScanDetails();
1030 
1031         mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
1032         mPnoScanStarted = true;
1033         mWifiMetrics.logPnoScanStart();
1034     }
1035 
1036     // Stop PNO scan.
stopPnoScan()1037     private void stopPnoScan() {
1038         if (!mPnoScanStarted) return;
1039 
1040         mScanner.stopPnoScan(mPnoScanListener);
1041         mPnoScanStarted = false;
1042         mWifiMetrics.logPnoScanStop();
1043     }
1044 
1045     // Set up watchdog timer
scheduleWatchdogTimer()1046     private void scheduleWatchdogTimer() {
1047         localLog("scheduleWatchdogTimer");
1048 
1049         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1050                             mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS,
1051                             WATCHDOG_TIMER_TAG,
1052                             mWatchdogListener, mEventHandler);
1053     }
1054 
1055     // Set up periodic scan timer
schedulePeriodicScanTimer(int intervalMs)1056     private void schedulePeriodicScanTimer(int intervalMs) {
1057         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1058                             mClock.getElapsedSinceBootMillis() + intervalMs,
1059                             PERIODIC_SCAN_TIMER_TAG,
1060                             mPeriodicScanTimerListener, mEventHandler);
1061         mPeriodicScanTimerSet = true;
1062     }
1063 
1064     // Cancel periodic scan timer
cancelPeriodicScanTimer()1065     private void cancelPeriodicScanTimer() {
1066         if (mPeriodicScanTimerSet) {
1067             mAlarmManager.cancel(mPeriodicScanTimerListener);
1068             mPeriodicScanTimerSet = false;
1069         }
1070     }
1071 
1072     // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
scheduleDelayedSingleScan(boolean isFullBandScan)1073     private void scheduleDelayedSingleScan(boolean isFullBandScan) {
1074         localLog("scheduleDelayedSingleScan");
1075 
1076         RestartSingleScanListener restartSingleScanListener =
1077                 new RestartSingleScanListener(isFullBandScan);
1078         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1079                             mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS,
1080                             RESTART_SINGLE_SCAN_TIMER_TAG,
1081                             restartSingleScanListener, mEventHandler);
1082     }
1083 
1084     // Set up timer to start a delayed scan after msFromNow milli-seconds
scheduleDelayedConnectivityScan(int msFromNow)1085     private void scheduleDelayedConnectivityScan(int msFromNow) {
1086         localLog("scheduleDelayedConnectivityScan");
1087 
1088         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1089                             mClock.getElapsedSinceBootMillis() + msFromNow,
1090                             RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
1091                             mRestartScanListener, mEventHandler);
1092 
1093     }
1094 
1095     // Start a connectivity scan. The scan method is chosen according to
1096     // the current screen state and WiFi state.
startConnectivityScan(boolean scanImmediately)1097     private void startConnectivityScan(boolean scanImmediately) {
1098         localLog("startConnectivityScan: screenOn=" + mScreenOn
1099                 + " wifiState=" + stateToString(mWifiState)
1100                 + " scanImmediately=" + scanImmediately
1101                 + " wifiEnabled=" + mWifiEnabled
1102                 + " wifiConnectivityManagerEnabled="
1103                 + mWifiConnectivityManagerEnabled);
1104 
1105         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
1106             return;
1107         }
1108 
1109         // Always stop outstanding connecivity scan if there is any
1110         stopConnectivityScan();
1111 
1112         // Don't start a connectivity scan while Wifi is in the transition
1113         // between connected and disconnected states.
1114         if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {
1115             return;
1116         }
1117 
1118         if (mScreenOn) {
1119             startPeriodicScan(scanImmediately);
1120         } else {
1121             if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
1122                 startDisconnectedPnoScan();
1123             }
1124         }
1125 
1126     }
1127 
1128     // Stop connectivity scan if there is any.
stopConnectivityScan()1129     private void stopConnectivityScan() {
1130         // Due to b/28020168, timer based single scan will be scheduled
1131         // to provide periodic scan in an exponential backoff fashion.
1132         cancelPeriodicScanTimer();
1133         stopPnoScan();
1134         mScanRestartCount = 0;
1135     }
1136 
1137     /**
1138      * Handler for screen state (on/off) changes
1139      */
handleScreenStateChanged(boolean screenOn)1140     public void handleScreenStateChanged(boolean screenOn) {
1141         localLog("handleScreenStateChanged: screenOn=" + screenOn);
1142 
1143         mScreenOn = screenOn;
1144 
1145         mOpenNetworkNotifier.handleScreenStateChanged(screenOn);
1146         mCarrierNetworkNotifier.handleScreenStateChanged(screenOn);
1147 
1148         startConnectivityScan(SCAN_ON_SCHEDULE);
1149     }
1150 
1151     /**
1152      * Helper function that converts the WIFI_STATE_XXX constants to string
1153      */
stateToString(int state)1154     private static String stateToString(int state) {
1155         switch (state) {
1156             case WIFI_STATE_CONNECTED:
1157                 return "connected";
1158             case WIFI_STATE_DISCONNECTED:
1159                 return "disconnected";
1160             case WIFI_STATE_TRANSITIONING:
1161                 return "transitioning";
1162             default:
1163                 return "unknown";
1164         }
1165     }
1166 
1167     /**
1168      * Handler for WiFi state (connected/disconnected) changes
1169      */
handleConnectionStateChanged(int state)1170     public void handleConnectionStateChanged(int state) {
1171         localLog("handleConnectionStateChanged: state=" + stateToString(state));
1172 
1173         mWifiState = state;
1174 
1175         // Reset BSSID of last connection attempt and kick off
1176         // the watchdog timer if entering disconnected state.
1177         if (mWifiState == WIFI_STATE_DISCONNECTED) {
1178             mLastConnectionAttemptBssid = null;
1179             scheduleWatchdogTimer();
1180             startConnectivityScan(SCAN_IMMEDIATELY);
1181         } else {
1182             startConnectivityScan(SCAN_ON_SCHEDULE);
1183         }
1184     }
1185 
1186     /**
1187      * Handler when a WiFi connection attempt ended.
1188      *
1189      * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code.
1190      */
handleConnectionAttemptEnded(int failureCode)1191     public void handleConnectionAttemptEnded(int failureCode) {
1192         if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) {
1193             String ssid = (mWifiInfo.getWifiSsid() == null)
1194                     ? null
1195                     : mWifiInfo.getWifiSsid().toString();
1196             mOpenNetworkNotifier.handleWifiConnected(ssid);
1197             mCarrierNetworkNotifier.handleWifiConnected(ssid);
1198         } else {
1199             mOpenNetworkNotifier.handleConnectionFailure();
1200             mCarrierNetworkNotifier.handleConnectionFailure();
1201         }
1202     }
1203 
1204     // Enable auto-join if we have any pending network request (trusted or untrusted) and no
1205     // specific network request in progress.
checkStateAndEnable()1206     private void checkStateAndEnable() {
1207         enable(!mSpecificNetworkRequestInProgress
1208                 && (mUntrustedConnectionAllowed || mTrustedConnectionAllowed));
1209         startConnectivityScan(SCAN_IMMEDIATELY);
1210     }
1211 
1212     /**
1213      * Triggered when {@link WifiNetworkFactory} has a pending general network request.
1214      */
setTrustedConnectionAllowed(boolean allowed)1215     public void setTrustedConnectionAllowed(boolean allowed) {
1216         localLog("setTrustedConnectionAllowed: allowed=" + allowed);
1217 
1218         if (mTrustedConnectionAllowed != allowed) {
1219             mTrustedConnectionAllowed = allowed;
1220             checkStateAndEnable();
1221         }
1222     }
1223 
1224 
1225     /**
1226      * Triggered when {@link UntrustedWifiNetworkFactory} has a pending ephemeral network request.
1227      */
setUntrustedConnectionAllowed(boolean allowed)1228     public void setUntrustedConnectionAllowed(boolean allowed) {
1229         localLog("setUntrustedConnectionAllowed: allowed=" + allowed);
1230 
1231         if (mUntrustedConnectionAllowed != allowed) {
1232             mUntrustedConnectionAllowed = allowed;
1233             checkStateAndEnable();
1234         }
1235     }
1236 
1237     /**
1238      * Triggered when {@link WifiNetworkFactory} is processing a specific network request.
1239      */
setSpecificNetworkRequestInProgress(boolean inProgress)1240     public void setSpecificNetworkRequestInProgress(boolean inProgress) {
1241         localLog("setsetSpecificNetworkRequestInProgress : inProgress=" + inProgress);
1242 
1243         if (mSpecificNetworkRequestInProgress != inProgress) {
1244             mSpecificNetworkRequestInProgress = inProgress;
1245             checkStateAndEnable();
1246         }
1247     }
1248 
1249     /**
1250      * Handler when user specifies a particular network to connect to
1251      */
setUserConnectChoice(int netId)1252     public void setUserConnectChoice(int netId) {
1253         localLog("setUserConnectChoice: netId=" + netId);
1254 
1255         mNetworkSelector.setUserConnectChoice(netId);
1256     }
1257 
1258     /**
1259      * Handler to prepare for connection to a user or app specified network
1260      */
prepareForForcedConnection(int netId)1261     public void prepareForForcedConnection(int netId) {
1262         localLog("prepareForForcedConnection: netId=" + netId);
1263 
1264         clearConnectionAttemptTimeStamps();
1265         clearBssidBlacklist();
1266     }
1267 
1268     /**
1269      * Handler for on-demand connectivity scan
1270      */
forceConnectivityScan(WorkSource workSource)1271     public void forceConnectivityScan(WorkSource workSource) {
1272         localLog("forceConnectivityScan in request of " + workSource);
1273 
1274         mWaitForFullBandScanResults = true;
1275         startSingleScan(true, workSource);
1276     }
1277 
1278     /**
1279      * Update the BSSID blacklist when a BSSID is enabled or disabled
1280      *
1281      * @param bssid the bssid to be enabled/disabled
1282      * @param enable -- true enable the bssid
1283      *               -- false disable the bssid
1284      * @param reasonCode enable/disable reason code
1285      * @return true if blacklist is updated; false otherwise
1286      */
updateBssidBlacklist(String bssid, boolean enable, int reasonCode)1287     private boolean updateBssidBlacklist(String bssid, boolean enable, int reasonCode) {
1288         // Remove the bssid from blacklist when it is enabled.
1289         if (enable) {
1290             return mBssidBlacklist.remove(bssid) != null;
1291         }
1292 
1293         // Do not update BSSID blacklist with information if this is the only
1294         // BSSID for its SSID. By ignoring it we will cause additional failures
1295         // which will trigger Watchdog.
1296         if (mWifiLastResortWatchdog.shouldIgnoreBssidUpdate(bssid)) {
1297             localLog("Ignore update Bssid Blacklist since Watchdog trigger is activated");
1298             return false;
1299         }
1300 
1301         // Update the bssid's blacklist status when it is disabled because of
1302         // association rejection.
1303         BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
1304         if (status == null) {
1305             // First time for this BSSID
1306             status = new BssidBlacklistStatus();
1307             mBssidBlacklist.put(bssid, status);
1308         }
1309 
1310         status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis();
1311         status.counter++;
1312         if (!status.isBlacklisted) {
1313             if (status.counter >= BSSID_BLACKLIST_THRESHOLD
1314                     || reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) {
1315                 status.isBlacklisted = true;
1316                 return true;
1317             }
1318         }
1319         return false;
1320     }
1321 
1322     /**
1323      * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector
1324      *
1325      * @param bssid the bssid to be enabled/disabled
1326      * @param enable -- true enable the bssid
1327      *               -- false disable the bssid
1328      * @param reasonCode enable/disable reason code
1329      * @return true if blacklist is updated; false otherwise
1330      */
trackBssid(String bssid, boolean enable, int reasonCode)1331     public boolean trackBssid(String bssid, boolean enable, int reasonCode) {
1332         localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid + " reason code "
1333                 + reasonCode);
1334 
1335         if (bssid == null) {
1336             return false;
1337         }
1338 
1339         if (!updateBssidBlacklist(bssid, enable, reasonCode)) {
1340             return false;
1341         }
1342 
1343         // Blacklist was updated, so update firmware roaming configuration.
1344         updateFirmwareRoamingConfiguration();
1345 
1346         if (!enable) {
1347             // Disabling a BSSID can happen when connection to the AP was rejected.
1348             // We start another scan immediately so that WifiNetworkSelector can
1349             // give us another candidate to connect to.
1350             startConnectivityScan(SCAN_IMMEDIATELY);
1351         }
1352 
1353         return true;
1354     }
1355 
1356     /**
1357      * Check whether a bssid is disabled
1358      */
1359     @VisibleForTesting
isBssidDisabled(String bssid)1360     public boolean isBssidDisabled(String bssid) {
1361         BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
1362         return status == null ? false : status.isBlacklisted;
1363     }
1364 
1365     /**
1366      * Compile and return a hashset of the blacklisted BSSIDs
1367      */
buildBssidBlacklist()1368     private HashSet<String> buildBssidBlacklist() {
1369         HashSet<String> blacklistedBssids = new HashSet<String>();
1370         for (String bssid : mBssidBlacklist.keySet()) {
1371             if (isBssidDisabled(bssid)) {
1372                 blacklistedBssids.add(bssid);
1373             }
1374         }
1375 
1376         return blacklistedBssids;
1377     }
1378 
1379     /**
1380      * Update firmware roaming configuration if the firmware roaming feature is supported.
1381      * Compile and write the BSSID blacklist only. TODO(b/36488259): SSID whitelist is always
1382      * empty for now.
1383      */
updateFirmwareRoamingConfiguration()1384     private void updateFirmwareRoamingConfiguration() {
1385         if (!mConnectivityHelper.isFirmwareRoamingSupported()) {
1386             return;
1387         }
1388 
1389         int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid();
1390         if (maxBlacklistSize <= 0) {
1391             Log.wtf(TAG, "Invalid max BSSID blacklist size:  " + maxBlacklistSize);
1392             return;
1393         }
1394 
1395         ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist());
1396         int blacklistSize = blacklistedBssids.size();
1397 
1398         if (blacklistSize > maxBlacklistSize) {
1399             Log.wtf(TAG, "Attempt to write " + blacklistSize + " blacklisted BSSIDs, max size is "
1400                     + maxBlacklistSize);
1401 
1402             blacklistedBssids = new ArrayList<String>(blacklistedBssids.subList(0,
1403                     maxBlacklistSize));
1404             localLog("Trim down BSSID blacklist size from " + blacklistSize + " to "
1405                     + blacklistedBssids.size());
1406         }
1407 
1408         if (!mConnectivityHelper.setFirmwareRoamingConfiguration(blacklistedBssids,
1409                 new ArrayList<String>())) {  // TODO(b/36488259): SSID whitelist management.
1410             localLog("Failed to set firmware roaming configuration.");
1411         }
1412     }
1413 
1414     /**
1415      * Refresh the BSSID blacklist
1416      *
1417      * Go through the BSSID blacklist and check if a BSSID has been blacklisted for
1418      * BSSID_BLACKLIST_EXPIRE_TIME_MS. If yes, re-enable it.
1419      */
refreshBssidBlacklist()1420     private void refreshBssidBlacklist() {
1421         if (mBssidBlacklist.isEmpty()) {
1422             return;
1423         }
1424 
1425         boolean updated = false;
1426         Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator();
1427         Long currentTimeStamp = mClock.getElapsedSinceBootMillis();
1428 
1429         while (iter.hasNext()) {
1430             BssidBlacklistStatus status = iter.next();
1431             if (status.isBlacklisted && ((currentTimeStamp - status.blacklistedTimeStamp)
1432                     >= BSSID_BLACKLIST_EXPIRE_TIME_MS)) {
1433                 iter.remove();
1434                 updated = true;
1435             }
1436         }
1437 
1438         if (updated) {
1439             updateFirmwareRoamingConfiguration();
1440         }
1441     }
1442 
1443     /**
1444      * Helper method to populate WifiScanner handle. This is done lazily because
1445      * WifiScanningService is started after WifiService.
1446      */
retrieveWifiScanner()1447     private void retrieveWifiScanner() {
1448         if (mScanner != null) return;
1449         mScanner = mWifiInjector.getWifiScanner();
1450         checkNotNull(mScanner);
1451         // Register for all single scan results
1452         mScanner.registerScanListener(mAllSingleScanListener);
1453     }
1454 
1455 
1456     /**
1457      * Clear the BSSID blacklist
1458      */
clearBssidBlacklist()1459     private void clearBssidBlacklist() {
1460         mBssidBlacklist.clear();
1461         updateFirmwareRoamingConfiguration();
1462     }
1463 
1464     /**
1465      * Start WifiConnectivityManager
1466      */
start()1467     private void start() {
1468         if (mRunning) return;
1469         retrieveWifiScanner();
1470         mConnectivityHelper.getFirmwareRoamingInfo();
1471         clearBssidBlacklist();
1472         mRunning = true;
1473     }
1474 
1475     /**
1476      * Stop and reset WifiConnectivityManager
1477      */
stop()1478     private void stop() {
1479         if (!mRunning) return;
1480         mRunning = false;
1481         stopConnectivityScan();
1482         clearBssidBlacklist();
1483         resetLastPeriodicSingleScanTimeStamp();
1484         mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */);
1485         mCarrierNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */);
1486         mLastConnectionAttemptBssid = null;
1487         mWaitForFullBandScanResults = false;
1488     }
1489 
1490     /**
1491      * Update WifiConnectivityManager running state
1492      *
1493      * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager
1494      * are enabled, otherwise stop it.
1495      */
updateRunningState()1496     private void updateRunningState() {
1497         if (mWifiEnabled && mWifiConnectivityManagerEnabled) {
1498             localLog("Starting up WifiConnectivityManager");
1499             start();
1500         } else {
1501             localLog("Stopping WifiConnectivityManager");
1502             stop();
1503         }
1504     }
1505 
1506     /**
1507      * Inform WiFi is enabled for connection or not
1508      */
setWifiEnabled(boolean enable)1509     public void setWifiEnabled(boolean enable) {
1510         localLog("Set WiFi " + (enable ? "enabled" : "disabled"));
1511 
1512         mWifiEnabled = enable;
1513         updateRunningState();
1514     }
1515 
1516     /**
1517      * Turn on/off the WifiConnectivityManager at runtime
1518      */
enable(boolean enable)1519     public void enable(boolean enable) {
1520         localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
1521 
1522         mWifiConnectivityManagerEnabled = enable;
1523         updateRunningState();
1524     }
1525 
1526     @VisibleForTesting
getLowRssiNetworkRetryDelay()1527     int getLowRssiNetworkRetryDelay() {
1528         return mPnoScanListener.getLowRssiNetworkRetryDelay();
1529     }
1530 
1531     @VisibleForTesting
getLastPeriodicSingleScanTimeStamp()1532     long getLastPeriodicSingleScanTimeStamp() {
1533         return mLastPeriodicSingleScanTimeStamp;
1534     }
1535 
1536     /**
1537      * Dump the local logs.
1538      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)1539     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1540         pw.println("Dump of WifiConnectivityManager");
1541         pw.println("WifiConnectivityManager - Log Begin ----");
1542         mLocalLog.dump(fd, pw, args);
1543         pw.println("WifiConnectivityManager - Log End ----");
1544         mOpenNetworkNotifier.dump(fd, pw, args);
1545         mCarrierNetworkNotifier.dump(fd, pw, args);
1546         mCarrierNetworkConfig.dump(fd, pw, args);
1547     }
1548 }
1549