1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE;
21 
22 import android.annotation.NonNull;
23 import android.app.AlarmManager;
24 import android.content.Context;
25 import android.net.MacAddress;
26 import android.net.wifi.ScanResult;
27 import android.net.wifi.SupplicantState;
28 import android.net.wifi.WifiConfiguration;
29 import android.net.wifi.WifiInfo;
30 import android.net.wifi.WifiManager;
31 import android.net.wifi.WifiManager.DeviceMobilityState;
32 import android.net.wifi.WifiNetworkSuggestion;
33 import android.net.wifi.WifiScanner;
34 import android.net.wifi.WifiScanner.PnoSettings;
35 import android.net.wifi.WifiScanner.ScanSettings;
36 import android.net.wifi.hotspot2.PasspointConfiguration;
37 import android.os.Handler;
38 import android.os.HandlerExecutor;
39 import android.os.Process;
40 import android.os.WorkSource;
41 import android.util.ArrayMap;
42 import android.util.LocalLog;
43 import android.util.Log;
44 
45 import com.android.internal.annotations.GuardedBy;
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.server.wifi.util.ScanResultUtil;
48 import com.android.wifi.resources.R;
49 
50 import java.io.FileDescriptor;
51 import java.io.PrintWriter;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collections;
55 import java.util.HashSet;
56 import java.util.Iterator;
57 import java.util.LinkedList;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.stream.Collectors;
62 
63 /**
64  * This class manages all the connectivity related scanning activities.
65  *
66  * When the screen is turned on or off, WiFi is connected or disconnected,
67  * or on-demand, a scan is initiatiated and the scan results are passed
68  * to WifiNetworkSelector for it to make a recommendation on which network
69  * to connect to.
70  */
71 public class WifiConnectivityManager {
72     public static final String WATCHDOG_TIMER_TAG =
73             "WifiConnectivityManager Schedule Watchdog Timer";
74     public static final String PERIODIC_SCAN_TIMER_TAG =
75             "WifiConnectivityManager Schedule Periodic Scan Timer";
76     public static final String RESTART_SINGLE_SCAN_TIMER_TAG =
77             "WifiConnectivityManager Restart Single Scan";
78     public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG =
79             "WifiConnectivityManager Restart Scan";
80     public static final String DELAYED_PARTIAL_SCAN_TIMER_TAG =
81             "WifiConnectivityManager Schedule Delayed Partial Scan Timer";
82 
83     private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
84     // Constants to indicate whether a scan should start immediately or
85     // it should comply to the minimum scan interval rule.
86     private static final boolean SCAN_IMMEDIATELY = true;
87     private static final boolean SCAN_ON_SCHEDULE = false;
88 
89     // PNO scan interval in milli-seconds. This is the scan
90     // performed when screen is off and connected.
91     private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
92     // When a network is found by PNO scan but gets rejected by Wifi Network Selector due
93     // to its low RSSI value, scan will be reschduled in an exponential back off manner.
94     private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds
95     private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds
96     // Maximum number of retries when starting a scan failed
97     @VisibleForTesting
98     public static final int MAX_SCAN_RESTART_ALLOWED = 5;
99     // Number of milli-seconds to delay before retry starting
100     // a previously failed scan
101     private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds
102     // When in disconnected mode, a watchdog timer will be fired
103     // every WATCHDOG_INTERVAL_MS to start a single scan. This is
104     // to prevent caveat from things like PNO scan.
105     private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes
106     // Restricted channel list age out value.
107     private static final long CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour
108     // This is the time interval for the connection attempt rate calculation. Connection attempt
109     // timestamps beyond this interval is evicted from the list.
110     public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins
111     // Max number of connection attempts in the above time interval.
112     public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6;
113     private static final int TEMP_BSSID_BLOCK_DURATION = 10 * 1000; // 10 seconds
114     // Maximum age of frequencies last seen to be included in pno scans. (30 days)
115     private static final long MAX_PNO_SCAN_FREQUENCY_AGE_MS = (long) 1000 * 3600 * 24 * 30;
116     // ClientModeImpl has a bunch of states. From the
117     // WifiConnectivityManager's perspective it only cares
118     // if it is in Connected state, Disconnected state or in
119     // transition between these two states.
120     public static final int WIFI_STATE_UNKNOWN = 0;
121     public static final int WIFI_STATE_CONNECTED = 1;
122     public static final int WIFI_STATE_DISCONNECTED = 2;
123     public static final int WIFI_STATE_TRANSITIONING = 3;
124 
125     // Initial scan state, used to manage performing partial scans in initial scans
126     // Initial scans are the first scan after enabling Wifi or turning on screen when disconnected
127     private static final int INITIAL_SCAN_STATE_START = 0;
128     private static final int INITIAL_SCAN_STATE_AWAITING_RESPONSE = 1;
129     private static final int INITIAL_SCAN_STATE_COMPLETE = 2;
130 
131     // Log tag for this class
132     private static final String TAG = "WifiConnectivityManager";
133     private static final String ALL_SINGLE_SCAN_LISTENER = "AllSingleScanListener";
134     private static final String PNO_SCAN_LISTENER = "PnoScanListener";
135 
136     private final Context mContext;
137     private final ClientModeImpl mStateMachine;
138     private final WifiInjector mWifiInjector;
139     private final WifiConfigManager mConfigManager;
140     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
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 WifiMetrics mWifiMetrics;
147     private final AlarmManager mAlarmManager;
148     private final Handler mEventHandler;
149     private final Clock mClock;
150     private final ScoringParams mScoringParams;
151     private final LocalLog mLocalLog;
152     private final LinkedList<Long> mConnectionAttemptTimeStamps;
153     private final BssidBlocklistMonitor mBssidBlocklistMonitor;
154     private WifiScanner mScanner;
155     private WifiScoreCard mWifiScoreCard;
156 
157     private boolean mDbg = false;
158     private boolean mVerboseLoggingEnabled = false;
159     private boolean mWifiEnabled = false;
160     private boolean mAutoJoinEnabled = false; // disabled by default, enabled by external triggers
161     private boolean mRunning = false;
162     private boolean mScreenOn = false;
163     private int mWifiState = WIFI_STATE_UNKNOWN;
164     private int mInitialScanState = INITIAL_SCAN_STATE_COMPLETE;
165     private boolean mAutoJoinEnabledExternal = true; // enabled by default
166     private boolean mUntrustedConnectionAllowed = false;
167     private boolean mTrustedConnectionAllowed = false;
168     private boolean mSpecificNetworkRequestInProgress = false;
169     private int mScanRestartCount = 0;
170     private int mSingleScanRestartCount = 0;
171     private int mTotalConnectivityAttemptsRateLimited = 0;
172     private String mLastConnectionAttemptBssid = null;
173     private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
174     private long mLastNetworkSelectionTimeStamp = RESET_TIME_STAMP;
175     private boolean mPnoScanStarted = false;
176     private boolean mPeriodicScanTimerSet = false;
177     private boolean mDelayedPartialScanTimerSet = false;
178 
179     // Used for Initial Scan metrics
180     private boolean mFailedInitialPartialScan = false;
181     private int mInitialPartialScanChannelCount;
182 
183     // Device configs
184     private boolean mWaitForFullBandScanResults = false;
185 
186     // Scanning Schedules
187     // Default schedule used in case of invalid configuration
188     private static final int[] DEFAULT_SCANNING_SCHEDULE_SEC = {20, 40, 80, 160};
189     private int[] mConnectedSingleScanScheduleSec;
190     private int[] mDisconnectedSingleScanScheduleSec;
191     private int[] mConnectedSingleSavedNetworkSingleScanScheduleSec;
192     private List<WifiCandidates.Candidate> mLatestCandidates = null;
193     private long mLatestCandidatesTimestampMs = 0;
194 
195     private final Object mLock = new Object();
196 
197     @GuardedBy("mLock")
198     private int[] mCurrentSingleScanScheduleSec;
199 
200     private int mCurrentSingleScanScheduleIndex;
201     private WifiChannelUtilization mWifiChannelUtilization;
202     // Cached WifiCandidates used in high mobility state to avoid connecting to APs that are
203     // moving relative to the user.
204     private CachedWifiCandidates mCachedWifiCandidates = null;
205     private @DeviceMobilityState int mDeviceMobilityState =
206             WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN;
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         if (mVerboseLoggingEnabled) Log.v(TAG, log);
213     }
214 
215     /**
216      * Enable verbose logging for WifiConnectivityManager.
217      */
enableVerboseLogging(boolean verbose)218     public void enableVerboseLogging(boolean verbose) {
219         mVerboseLoggingEnabled = verbose;
220     }
221 
222     // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
223     // if the start scan command failed. A timer is used here to make it a deferred retry.
224     private final AlarmManager.OnAlarmListener mRestartScanListener =
225             new AlarmManager.OnAlarmListener() {
226                 public void onAlarm() {
227                     startConnectivityScan(SCAN_IMMEDIATELY);
228                 }
229             };
230 
231     // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
232     // if the start scan command failed. An timer is used here to make it a deferred retry.
233     private class RestartSingleScanListener implements AlarmManager.OnAlarmListener {
234         private final boolean mIsFullBandScan;
235 
RestartSingleScanListener(boolean isFullBandScan)236         RestartSingleScanListener(boolean isFullBandScan) {
237             mIsFullBandScan = isFullBandScan;
238         }
239 
240         @Override
onAlarm()241         public void onAlarm() {
242             startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE);
243         }
244     }
245 
246     // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS
247     // if it is in the WIFI_STATE_DISCONNECTED state.
248     private final AlarmManager.OnAlarmListener mWatchdogListener =
249             new AlarmManager.OnAlarmListener() {
250                 public void onAlarm() {
251                     watchdogHandler();
252                 }
253             };
254 
255     // Due to b/28020168, timer based single scan will be scheduled
256     // to provide periodic scan in an exponential backoff fashion.
257     private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
258             new AlarmManager.OnAlarmListener() {
259                 public void onAlarm() {
260                     periodicScanTimerHandler();
261                 }
262             };
263 
264     private final AlarmManager.OnAlarmListener mDelayedPartialScanTimerListener =
265             new AlarmManager.OnAlarmListener() {
266                 public void onAlarm() {
267                     if (mCachedWifiCandidates == null
268                             || mCachedWifiCandidates.frequencies == null
269                             || mCachedWifiCandidates.frequencies.size() == 0) {
270                         return;
271                     }
272                     ScanSettings settings = new ScanSettings();
273                     settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
274                     settings.band = getScanBand(false);
275                     settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
276                             | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
277                     settings.numBssidsPerScan = 0;
278                     int index = 0;
279                     settings.channels =
280                             new WifiScanner.ChannelSpec[mCachedWifiCandidates.frequencies.size()];
281                     for (Integer freq : mCachedWifiCandidates.frequencies) {
282                         settings.channels[index++] = new WifiScanner.ChannelSpec(freq);
283                     }
284                     SingleScanListener singleScanListener = new SingleScanListener(false);
285                     mScanner.startScan(settings, new HandlerExecutor(mEventHandler),
286                             singleScanListener, WIFI_WORK_SOURCE);
287                     mWifiMetrics.incrementConnectivityOneshotScanCount();
288                 }
289             };
290 
291     /**
292      * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
293      * Executes selection of potential network candidates, initiation of connection attempt to that
294      * network.
295      *
296      * @return true - if a candidate is selected by WifiNetworkSelector
297      *         false - if no candidate is selected by WifiNetworkSelector
298      */
handleScanResults(List<ScanDetail> scanDetails, String listenerName, boolean isFullScan)299     private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName,
300             boolean isFullScan) {
301         mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(
302                 mStateMachine.getWifiLinkLayerStats(), WifiChannelUtilization.UNKNOWN_FREQ);
303 
304         updateUserDisabledList(scanDetails);
305 
306         // Check if any blocklisted BSSIDs can be freed.
307         Set<String> bssidBlocklist = mBssidBlocklistMonitor.updateAndGetBssidBlocklist();
308 
309         if (mStateMachine.isSupplicantTransientState()) {
310             localLog(listenerName
311                     + " onResults: No network selection because supplicantTransientState is "
312                     + mStateMachine.isSupplicantTransientState());
313             return false;
314         }
315 
316         localLog(listenerName + " onResults: start network selection");
317 
318         List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan(
319                 scanDetails, bssidBlocklist, mWifiInfo, mStateMachine.isConnected(),
320                 mStateMachine.isDisconnected(), mUntrustedConnectionAllowed);
321         mLatestCandidates = candidates;
322         mLatestCandidatesTimestampMs = mClock.getElapsedSinceBootMillis();
323 
324         if (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT
325                 && mContext.getResources().getBoolean(
326                         R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled)) {
327             candidates = filterCandidatesHighMovement(candidates, listenerName, isFullScan);
328         }
329 
330         WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates);
331         mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
332         mWifiLastResortWatchdog.updateAvailableNetworks(
333                 mNetworkSelector.getConnectableScanDetails());
334         mWifiMetrics.countScanResults(scanDetails);
335         if (candidate != null) {
336             localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
337             connectToNetwork(candidate);
338             return true;
339         } else {
340             if (mWifiState == WIFI_STATE_DISCONNECTED) {
341                 mOpenNetworkNotifier.handleScanResults(
342                         mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
343             }
344             return false;
345         }
346     }
347 
filterCandidatesHighMovement( List<WifiCandidates.Candidate> candidates, String listenerName, boolean isFullScan)348     private List<WifiCandidates.Candidate> filterCandidatesHighMovement(
349             List<WifiCandidates.Candidate> candidates, String listenerName, boolean isFullScan) {
350         boolean isNotPartialScan = isFullScan || listenerName.equals(PNO_SCAN_LISTENER);
351         if (candidates == null || candidates.isEmpty()) {
352             // No connectable networks nearby or network selection is unnecessary
353             if (isNotPartialScan) {
354                 mCachedWifiCandidates = new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(),
355                         null);
356             }
357             return null;
358         }
359 
360         long minimumTimeBetweenScansMs = mContext.getResources().getInteger(
361                 R.integer.config_wifiHighMovementNetworkSelectionOptimizationScanDelayMs);
362         if (mCachedWifiCandidates != null && mCachedWifiCandidates.candidateRssiMap != null) {
363             // cached candidates are too recent, wait for next scan
364             if (mClock.getElapsedSinceBootMillis() - mCachedWifiCandidates.timeSinceBootMs
365                     < minimumTimeBetweenScansMs) {
366                 mWifiMetrics.incrementNumHighMovementConnectionSkipped();
367                 return null;
368             }
369 
370             int rssiDelta = mContext.getResources().getInteger(R.integer
371                     .config_wifiHighMovementNetworkSelectionOptimizationRssiDelta);
372             List<WifiCandidates.Candidate> filteredCandidates = candidates.stream().filter(
373                     item -> mCachedWifiCandidates.candidateRssiMap.containsKey(item.getKey())
374                             && Math.abs(mCachedWifiCandidates.candidateRssiMap.get(item.getKey())
375                             - item.getScanRssi()) < rssiDelta)
376                     .collect(Collectors.toList());
377 
378             if (!filteredCandidates.isEmpty()) {
379                 if (isNotPartialScan) {
380                     mCachedWifiCandidates =
381                             new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(),
382                             candidates);
383                 }
384                 mWifiMetrics.incrementNumHighMovementConnectionStarted();
385                 return filteredCandidates;
386             }
387         }
388 
389         // Either no cached candidates, or all candidates got filtered out.
390         // Update the cached candidates here and schedule a delayed partial scan.
391         if (isNotPartialScan) {
392             mCachedWifiCandidates = new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(),
393                     candidates);
394             localLog("Found " + candidates.size() + " candidates at high mobility state. "
395                     + "Re-doing scan to confirm network quality.");
396             scheduleDelayedPartialScan(minimumTimeBetweenScansMs);
397         }
398         mWifiMetrics.incrementNumHighMovementConnectionSkipped();
399         return null;
400     }
401 
updateUserDisabledList(List<ScanDetail> scanDetails)402     private void updateUserDisabledList(List<ScanDetail> scanDetails) {
403         List<String> results = new ArrayList<>();
404         List<ScanResult> passpointAp = new ArrayList<>();
405         for (ScanDetail scanDetail : scanDetails) {
406             results.add(ScanResultUtil.createQuotedSSID(scanDetail.getScanResult().SSID));
407             if (!scanDetail.getScanResult().isPasspointNetwork()) {
408                 continue;
409             }
410             passpointAp.add(scanDetail.getScanResult());
411         }
412         if (!passpointAp.isEmpty()) {
413             results.addAll(new ArrayList<>(mWifiInjector.getPasspointManager()
414                     .getAllMatchingPasspointProfilesForScanResults(passpointAp).keySet()));
415         }
416         mConfigManager.updateUserDisabledList(results);
417     }
418 
419     /**
420      * Set whether bluetooth is in the connected state
421      */
setBluetoothConnected(boolean isBluetoothConnected)422     public void setBluetoothConnected(boolean isBluetoothConnected) {
423         mNetworkSelector.setBluetoothConnected(isBluetoothConnected);
424     }
425 
426     private class CachedWifiCandidates {
427         public final long timeSinceBootMs;
428         public final Map<WifiCandidates.Key, Integer> candidateRssiMap;
429         public final Set<Integer> frequencies;
430 
CachedWifiCandidates(long timeSinceBootMs, List<WifiCandidates.Candidate> candidates)431         CachedWifiCandidates(long timeSinceBootMs, List<WifiCandidates.Candidate> candidates) {
432             this.timeSinceBootMs = timeSinceBootMs;
433             if (candidates == null) {
434                 this.candidateRssiMap = null;
435                 this.frequencies = null;
436             } else {
437                 this.candidateRssiMap = new ArrayMap<WifiCandidates.Key, Integer>();
438                 this.frequencies = new HashSet<Integer>();
439                 for (WifiCandidates.Candidate c : candidates) {
440                     candidateRssiMap.put(c.getKey(), c.getScanRssi());
441                     frequencies.add(c.getFrequency());
442                 }
443             }
444         }
445     }
446 
447     // All single scan results listener.
448     //
449     // Note: This is the listener for all the available single scan results,
450     //       including the ones initiated by WifiConnectivityManager and
451     //       other modules.
452     private class AllSingleScanListener implements WifiScanner.ScanListener {
453         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
454         private int mNumScanResultsIgnoredDueToSingleRadioChain = 0;
455 
clearScanDetails()456         public void clearScanDetails() {
457             mScanDetails.clear();
458             mNumScanResultsIgnoredDueToSingleRadioChain = 0;
459         }
460 
461         @Override
onSuccess()462         public void onSuccess() {
463         }
464 
465         @Override
onFailure(int reason, String description)466         public void onFailure(int reason, String description) {
467             localLog("registerScanListener onFailure:"
468                       + " reason: " + reason + " description: " + description);
469         }
470 
471         @Override
onPeriodChanged(int periodInMs)472         public void onPeriodChanged(int periodInMs) {
473         }
474 
475         @Override
onResults(WifiScanner.ScanData[] results)476         public void onResults(WifiScanner.ScanData[] results) {
477             if (!mWifiEnabled || !mAutoJoinEnabled) {
478                 clearScanDetails();
479                 mWaitForFullBandScanResults = false;
480                 return;
481             }
482 
483             // We treat any full band scans (with DFS or not) as "full".
484             boolean isFullBandScanResults = false;
485             if (results != null && results.length > 0) {
486                 isFullBandScanResults =
487                         WifiScanner.isFullBandScan(results[0].getBandScanned(), true);
488             }
489             // Full band scan results only.
490             if (mWaitForFullBandScanResults) {
491                 if (!isFullBandScanResults) {
492                     localLog("AllSingleScanListener waiting for full band scan results.");
493                     clearScanDetails();
494                     return;
495                 } else {
496                     mWaitForFullBandScanResults = false;
497                 }
498             }
499             if (results != null && results.length > 0) {
500                 mWifiMetrics.incrementAvailableNetworksHistograms(mScanDetails,
501                         isFullBandScanResults);
502             }
503             if (mNumScanResultsIgnoredDueToSingleRadioChain > 0) {
504                 Log.i(TAG, "Number of scan results ignored due to single radio chain scan: "
505                         + mNumScanResultsIgnoredDueToSingleRadioChain);
506             }
507             boolean wasConnectAttempted = handleScanResults(mScanDetails,
508                     ALL_SINGLE_SCAN_LISTENER, isFullBandScanResults);
509             clearScanDetails();
510 
511             // Update metrics to see if a single scan detected a valid network
512             // while PNO scan didn't.
513             // Note: We don't update the background scan metrics any more as it is
514             //       not in use.
515             if (mPnoScanStarted) {
516                 if (wasConnectAttempted) {
517                     mWifiMetrics.incrementNumConnectivityWatchdogPnoBad();
518                 } else {
519                     mWifiMetrics.incrementNumConnectivityWatchdogPnoGood();
520                 }
521             }
522 
523             // Check if we are in the middle of initial partial scan
524             if (mInitialScanState == INITIAL_SCAN_STATE_AWAITING_RESPONSE) {
525                 // Done with initial scan
526                 setInitialScanState(INITIAL_SCAN_STATE_COMPLETE);
527 
528                 if (wasConnectAttempted) {
529                     Log.i(TAG, "Connection attempted with the reduced initial scans");
530                     schedulePeriodicScanTimer(
531                             getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex));
532                     mWifiMetrics.reportInitialPartialScan(mInitialPartialScanChannelCount, true);
533                     mInitialPartialScanChannelCount = 0;
534                 } else {
535                     Log.i(TAG, "Connection was not attempted, issuing a full scan");
536                     startConnectivityScan(SCAN_IMMEDIATELY);
537                     mFailedInitialPartialScan = true;
538                 }
539             } else if (mInitialScanState == INITIAL_SCAN_STATE_COMPLETE) {
540                 if (mFailedInitialPartialScan && wasConnectAttempted) {
541                     // Initial scan failed, but following full scan succeeded
542                     mWifiMetrics.reportInitialPartialScan(mInitialPartialScanChannelCount, false);
543                 }
544                 mFailedInitialPartialScan = false;
545                 mInitialPartialScanChannelCount = 0;
546             }
547         }
548 
549         @Override
onFullResult(ScanResult fullScanResult)550         public void onFullResult(ScanResult fullScanResult) {
551             if (!mWifiEnabled || !mAutoJoinEnabled) {
552                 return;
553             }
554 
555             if (mDbg) {
556                 localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID
557                         + " capabilities " + fullScanResult.capabilities);
558             }
559 
560             // When the scan result has radio chain info, ensure we throw away scan results
561             // not received with both radio chains (if |mUseSingleRadioChainScanResults| is false).
562             if (!mContext.getResources().getBoolean(
563                     R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection)
564                     && fullScanResult.radioChainInfos != null
565                     && fullScanResult.radioChainInfos.length == 1) {
566                 // Keep track of the number of dropped scan results for logging.
567                 mNumScanResultsIgnoredDueToSingleRadioChain++;
568                 return;
569             }
570 
571             mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult));
572         }
573     }
574 
575     private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener();
576 
577     // Single scan results listener. A single scan is initiated when
578     // DisconnectedPNO scan found a valid network and woke up
579     // the system, or by the watchdog timer, or to form the timer based
580     // periodic scan.
581     //
582     // Note: This is the listener for the single scans initiated by the
583     //        WifiConnectivityManager.
584     private class SingleScanListener implements WifiScanner.ScanListener {
585         private final boolean mIsFullBandScan;
586 
SingleScanListener(boolean isFullBandScan)587         SingleScanListener(boolean isFullBandScan) {
588             mIsFullBandScan = isFullBandScan;
589         }
590 
591         @Override
onSuccess()592         public void onSuccess() {
593         }
594 
595         @Override
onFailure(int reason, String description)596         public void onFailure(int reason, String description) {
597             localLog("SingleScanListener onFailure:"
598                     + " reason: " + reason + " description: " + description);
599 
600             // reschedule the scan
601             if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
602                 scheduleDelayedSingleScan(mIsFullBandScan);
603             } else {
604                 mSingleScanRestartCount = 0;
605                 localLog("Failed to successfully start single scan for "
606                         + MAX_SCAN_RESTART_ALLOWED + " times");
607             }
608         }
609 
610         @Override
onPeriodChanged(int periodInMs)611         public void onPeriodChanged(int periodInMs) {
612             localLog("SingleScanListener onPeriodChanged: "
613                     + "actual scan period " + periodInMs + "ms");
614         }
615 
616         @Override
onResults(WifiScanner.ScanData[] results)617         public void onResults(WifiScanner.ScanData[] results) {
618             mSingleScanRestartCount = 0;
619         }
620 
621         @Override
onFullResult(ScanResult fullScanResult)622         public void onFullResult(ScanResult fullScanResult) {
623         }
624     }
625 
626     // PNO scan results listener for both disconnected and connected PNO scanning.
627     // A PNO scan is initiated when screen is off.
628     private class PnoScanListener implements WifiScanner.PnoScanListener {
629         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
630         private int mLowRssiNetworkRetryDelay =
631                 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
632 
clearScanDetails()633         public void clearScanDetails() {
634             mScanDetails.clear();
635         }
636 
637         // Reset to the start value when either a non-PNO scan is started or
638         // WifiNetworkSelector selects a candidate from the PNO scan results.
resetLowRssiNetworkRetryDelay()639         public void resetLowRssiNetworkRetryDelay() {
640             mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
641         }
642 
643         @VisibleForTesting
getLowRssiNetworkRetryDelay()644         public int getLowRssiNetworkRetryDelay() {
645             return mLowRssiNetworkRetryDelay;
646         }
647 
648         @Override
onSuccess()649         public void onSuccess() {
650         }
651 
652         @Override
onFailure(int reason, String description)653         public void onFailure(int reason, String description) {
654             localLog("PnoScanListener onFailure:"
655                     + " reason: " + reason + " description: " + description);
656 
657             // reschedule the scan
658             if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
659                 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
660             } else {
661                 mScanRestartCount = 0;
662                 localLog("Failed to successfully start PNO scan for "
663                         + MAX_SCAN_RESTART_ALLOWED + " times");
664             }
665         }
666 
667         @Override
onPeriodChanged(int periodInMs)668         public void onPeriodChanged(int periodInMs) {
669             localLog("PnoScanListener onPeriodChanged: "
670                     + "actual scan period " + periodInMs + "ms");
671         }
672 
673         // Currently the PNO scan results doesn't include IE,
674         // which contains information required by WifiNetworkSelector. Ignore them
675         // for now.
676         @Override
onResults(WifiScanner.ScanData[] results)677         public void onResults(WifiScanner.ScanData[] results) {
678         }
679 
680         @Override
onFullResult(ScanResult fullScanResult)681         public void onFullResult(ScanResult fullScanResult) {
682         }
683 
684         @Override
onPnoNetworkFound(ScanResult[] results)685         public void onPnoNetworkFound(ScanResult[] results) {
686             for (ScanResult result: results) {
687                 if (result.informationElements == null) {
688                     localLog("Skipping scan result with null information elements");
689                     continue;
690                 }
691                 mScanDetails.add(ScanResultUtil.toScanDetail(result));
692             }
693 
694             boolean wasConnectAttempted;
695             wasConnectAttempted = handleScanResults(mScanDetails, PNO_SCAN_LISTENER, false);
696             clearScanDetails();
697             mScanRestartCount = 0;
698 
699             if (!wasConnectAttempted) {
700                 // The scan results were rejected by WifiNetworkSelector due to low RSSI values
701                 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) {
702                     mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS;
703                 }
704                 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay);
705 
706                 // Set up the delay value for next retry.
707                 mLowRssiNetworkRetryDelay *= 2;
708             } else {
709                 resetLowRssiNetworkRetryDelay();
710             }
711         }
712     }
713 
714     private final PnoScanListener mPnoScanListener = new PnoScanListener();
715 
716     private class OnNetworkUpdateListener implements
717             WifiConfigManager.OnNetworkUpdateListener {
718         @Override
onNetworkAdded(WifiConfiguration config)719         public void onNetworkAdded(WifiConfiguration config) {
720             triggerScanOnNetworkChanges();
721         }
722         @Override
onNetworkEnabled(WifiConfiguration config)723         public void onNetworkEnabled(WifiConfiguration config) {
724             triggerScanOnNetworkChanges();
725         }
726         @Override
onNetworkRemoved(WifiConfiguration config)727         public void onNetworkRemoved(WifiConfiguration config) {
728             triggerScanOnNetworkChanges();
729         }
730         @Override
onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig)731         public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig) {
732             triggerScanOnNetworkChanges();
733         }
734         @Override
onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason)735         public void onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason) { }
736 
737         @Override
onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason)738         public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) {
739             triggerScanOnNetworkChanges();
740         }
741     }
742 
743     private class OnSuggestionUpdateListener implements
744             WifiNetworkSuggestionsManager.OnSuggestionUpdateListener {
745         @Override
onSuggestionsAddedOrUpdated(List<WifiNetworkSuggestion> suggestions)746         public void onSuggestionsAddedOrUpdated(List<WifiNetworkSuggestion> suggestions) {
747             triggerScanOnNetworkChanges();
748         }
749 
750         @Override
onSuggestionsRemoved(List<WifiNetworkSuggestion> suggestions)751         public void onSuggestionsRemoved(List<WifiNetworkSuggestion> suggestions) {
752             triggerScanOnNetworkChanges();
753         }
754     }
755 
756     /**
757      * WifiConnectivityManager constructor
758      */
WifiConnectivityManager(Context context, ScoringParams scoringParams, ClientModeImpl stateMachine, WifiInjector injector, WifiConfigManager configManager, WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager, WifiInfo wifiInfo, WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier, WifiMetrics wifiMetrics, Handler handler, Clock clock, LocalLog localLog, WifiScoreCard scoreCard)759     WifiConnectivityManager(Context context, ScoringParams scoringParams,
760             ClientModeImpl stateMachine,
761             WifiInjector injector, WifiConfigManager configManager,
762             WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager, WifiInfo wifiInfo,
763             WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper,
764             WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier,
765             WifiMetrics wifiMetrics, Handler handler,
766             Clock clock, LocalLog localLog, WifiScoreCard scoreCard) {
767         mContext = context;
768         mStateMachine = stateMachine;
769         mWifiInjector = injector;
770         mConfigManager = configManager;
771         mWifiNetworkSuggestionsManager = wifiNetworkSuggestionsManager;
772         mWifiInfo = wifiInfo;
773         mNetworkSelector = networkSelector;
774         mConnectivityHelper = connectivityHelper;
775         mLocalLog = localLog;
776         mWifiLastResortWatchdog = wifiLastResortWatchdog;
777         mOpenNetworkNotifier = openNetworkNotifier;
778         mWifiMetrics = wifiMetrics;
779         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
780         mEventHandler = handler;
781         mClock = clock;
782         mScoringParams = scoringParams;
783         mConnectionAttemptTimeStamps = new LinkedList<>();
784 
785         // Listen to WifiConfigManager network update events
786         mConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener());
787         // Listen to WifiNetworkSuggestionsManager suggestion update events
788         mWifiNetworkSuggestionsManager.addOnSuggestionUpdateListener(
789                 new OnSuggestionUpdateListener());
790         mBssidBlocklistMonitor = mWifiInjector.getBssidBlocklistMonitor();
791         mWifiChannelUtilization = mWifiInjector.getWifiChannelUtilizationScan();
792         mNetworkSelector.setWifiChannelUtilization(mWifiChannelUtilization);
793         mWifiScoreCard = scoreCard;
794     }
795 
796     /** Initialize single scanning schedules, and validate them */
initializeScanningSchedule(int state)797     private int[] initializeScanningSchedule(int state) {
798         int[] scheduleSec;
799 
800         if (state == WIFI_STATE_CONNECTED) {
801             scheduleSec = mContext.getResources().getIntArray(
802                     R.array.config_wifiConnectedScanIntervalScheduleSec);
803         } else if (state == WIFI_STATE_DISCONNECTED) {
804             scheduleSec = mContext.getResources().getIntArray(
805                     R.array.config_wifiDisconnectedScanIntervalScheduleSec);
806         } else {
807             scheduleSec = null;
808         }
809 
810         boolean invalidConfig = false;
811         if (scheduleSec == null || scheduleSec.length == 0) {
812             invalidConfig = true;
813         } else {
814             for (int val : scheduleSec) {
815                 if (val <= 0) {
816                     invalidConfig = true;
817                     break;
818                 }
819             }
820         }
821         if (!invalidConfig) {
822             return scheduleSec;
823         }
824 
825         Log.e(TAG, "Configuration for wifi scanning schedule is mis-configured,"
826                 + "using default schedule");
827         return DEFAULT_SCANNING_SCHEDULE_SEC;
828     }
829 
830     /**
831      * This checks the connection attempt rate and recommends whether the connection attempt
832      * should be skipped or not. This attempts to rate limit the rate of connections to
833      * prevent us from flapping between networks and draining battery rapidly.
834      */
shouldSkipConnectionAttempt(Long timeMillis)835     private boolean shouldSkipConnectionAttempt(Long timeMillis) {
836         Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator();
837         // First evict old entries from the queue.
838         while (attemptIter.hasNext()) {
839             Long connectionAttemptTimeMillis = attemptIter.next();
840             if ((timeMillis - connectionAttemptTimeMillis)
841                     > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) {
842                 attemptIter.remove();
843             } else {
844                 // This list is sorted by timestamps, so we can skip any more checks
845                 break;
846             }
847         }
848         // If we've reached the max connection attempt rate, skip this connection attempt
849         return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE);
850     }
851 
852     /**
853      * Add the current connection attempt timestamp to our queue of connection attempts.
854      */
noteConnectionAttempt(Long timeMillis)855     private void noteConnectionAttempt(Long timeMillis) {
856         mConnectionAttemptTimeStamps.addLast(timeMillis);
857     }
858 
859     /**
860      * This is used to clear the connection attempt rate limiter. This is done when the user
861      * explicitly tries to connect to a specified network.
862      */
clearConnectionAttemptTimeStamps()863     private void clearConnectionAttemptTimeStamps() {
864         mConnectionAttemptTimeStamps.clear();
865     }
866 
867     /**
868      * Attempt to connect to a network candidate.
869      *
870      * Based on the currently connected network, this menthod determines whether we should
871      * connect or roam to the network candidate recommended by WifiNetworkSelector.
872      */
connectToNetwork(WifiConfiguration candidate)873     private void connectToNetwork(WifiConfiguration candidate) {
874         ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
875         if (scanResultCandidate == null) {
876             localLog("connectToNetwork: bad candidate - "  + candidate
877                     + " scanResult: " + scanResultCandidate);
878             return;
879         }
880 
881         String targetBssid = scanResultCandidate.BSSID;
882         String targetAssociationId = candidate.SSID + " : " + targetBssid;
883 
884         // Check if we are already connected or in the process of connecting to the target
885         // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just
886         // in case the firmware automatically roamed to a BSSID different from what
887         // WifiNetworkSelector selected.
888         if (targetBssid != null
889                 && (targetBssid.equals(mLastConnectionAttemptBssid)
890                     || targetBssid.equals(mWifiInfo.getBSSID()))
891                 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
892             localLog("connectToNetwork: Either already connected "
893                     + "or is connecting to " + targetAssociationId);
894             return;
895         }
896 
897         if (candidate.BSSID != null
898                 && !candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)
899                 && !candidate.BSSID.equals(targetBssid)) {
900             localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the "
901                     + "config specified BSSID " + candidate.BSSID + ". Drop it!");
902             return;
903         }
904 
905         long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
906         if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
907             localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
908             mTotalConnectivityAttemptsRateLimited++;
909             return;
910         }
911         noteConnectionAttempt(elapsedTimeMillis);
912 
913         mLastConnectionAttemptBssid = targetBssid;
914 
915         WifiConfiguration currentConnectedNetwork = mConfigManager
916                 .getConfiguredNetwork(mWifiInfo.getNetworkId());
917         String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
918                 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
919 
920         if (currentConnectedNetwork != null
921                 && (currentConnectedNetwork.networkId == candidate.networkId
922                 //TODO(b/36788683): re-enable linked configuration check
923                 /* || currentConnectedNetwork.isLinked(candidate) */)) {
924             // Framework initiates roaming only if firmware doesn't support
925             // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}.
926             if (mConnectivityHelper.isFirmwareRoamingSupported()) {
927                 // Keep this logging here for now to validate the firmware roaming behavior.
928                 localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "."
929                         + " The actual roaming target is up to the firmware.");
930             } else {
931                 localLog("connectToNetwork: Roaming to " + targetAssociationId + " from "
932                         + currentAssociationId);
933                 mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate);
934             }
935         } else {
936             // Framework specifies the connection target BSSID if firmware doesn't support
937             // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the
938             // candidate configuration contains a specified BSSID.
939             if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null
940                       || candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY))) {
941                 targetBssid = ClientModeImpl.SUPPLICANT_BSSID_ANY;
942                 localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid
943                         + " from " + currentAssociationId);
944             } else {
945                 localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
946                         + currentAssociationId);
947             }
948             mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid);
949         }
950     }
951 
952     // Helper for selecting the band for connectivity scan
getScanBand()953     private int getScanBand() {
954         return getScanBand(true);
955     }
956 
getScanBand(boolean isFullBandScan)957     private int getScanBand(boolean isFullBandScan) {
958         if (isFullBandScan) {
959             return WifiScanner.WIFI_BAND_ALL;
960         } else {
961             // Use channel list instead.
962             return WifiScanner.WIFI_BAND_UNSPECIFIED;
963         }
964     }
965 
966     // Helper for setting the channels for connectivity scan when band is unspecified. Returns
967     // false if we can't retrieve the info.
968     // If connected, return channels used for the connected network
969     // If disconnected, return channels used for any network.
setScanChannels(ScanSettings settings)970     private boolean setScanChannels(ScanSettings settings) {
971         Set<Integer> freqs;
972 
973         WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration();
974         if (config == null) {
975             long ageInMillis = 1000 * 60 * mContext.getResources().getInteger(
976                     R.integer.config_wifiInitialPartialScanChannelCacheAgeMins);
977             int maxCount = mContext.getResources().getInteger(
978                     R.integer.config_wifiInitialPartialScanChannelMaxCount);
979             freqs = fetchChannelSetForPartialScan(maxCount, ageInMillis);
980         } else {
981             freqs = fetchChannelSetForNetworkForPartialScan(config.networkId);
982         }
983 
984         if (freqs != null && freqs.size() != 0) {
985             int index = 0;
986             settings.channels = new WifiScanner.ChannelSpec[freqs.size()];
987             for (Integer freq : freqs) {
988                 settings.channels[index++] = new WifiScanner.ChannelSpec(freq);
989             }
990             return true;
991         } else {
992             localLog("No history scan channels found, Perform full band scan");
993             return false;
994         }
995     }
996 
997     /**
998      * Add the channels into the channel set with a size limit.
999      * If maxCount equals to 0, will add all available channels into the set.
1000      * @param channelSet Target set for adding channel to.
1001      * @param config Network for query channel from WifiScoreCard
1002      * @param maxCount Size limit of the set. If equals to 0, means no limit.
1003      * @param ageInMillis Only consider channel info whose timestamps are younger than this value.
1004      * @return True if all available channels for this network are added, otherwise false.
1005      */
addChannelFromWifiScoreCard(@onNull Set<Integer> channelSet, @NonNull WifiConfiguration config, int maxCount, long ageInMillis)1006     private boolean addChannelFromWifiScoreCard(@NonNull Set<Integer> channelSet,
1007             @NonNull WifiConfiguration config, int maxCount, long ageInMillis) {
1008         WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID);
1009         for (Integer channel : network.getFrequencies(ageInMillis)) {
1010             if (maxCount > 0 && channelSet.size() >= maxCount) {
1011                 localLog("addChannelFromWifiScoreCard: size limit reached for network:"
1012                         + config.SSID);
1013                 return false;
1014             }
1015             channelSet.add(channel);
1016         }
1017         return true;
1018     }
1019 
1020     /**
1021      * Fetch channel set for target network.
1022      */
1023     @VisibleForTesting
fetchChannelSetForNetworkForPartialScan(int networkId)1024     public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId) {
1025         WifiConfiguration config = mConfigManager.getConfiguredNetwork(networkId);
1026         if (config == null) {
1027             return null;
1028         }
1029         final int maxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
1030                 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
1031         Set<Integer> channelSet = new HashSet<>();
1032         // First add the currently connected network channel.
1033         if (mWifiInfo.getFrequency() > 0) {
1034             channelSet.add(mWifiInfo.getFrequency());
1035         }
1036         // Then get channels for the network.
1037         addChannelFromWifiScoreCard(channelSet, config, maxNumActiveChannelsForPartialScans,
1038                 CHANNEL_LIST_AGE_MS);
1039         return channelSet;
1040     }
1041 
1042     /**
1043      * Fetch channel set for all saved and suggestion non-passpoint network for partial scan.
1044      */
1045     @VisibleForTesting
fetchChannelSetForPartialScan(int maxCount, long ageInMillis)1046     public Set<Integer> fetchChannelSetForPartialScan(int maxCount, long ageInMillis) {
1047         List<WifiConfiguration> networks = getAllScanOptimizationNetworks();
1048         if (networks.isEmpty()) {
1049             return null;
1050         }
1051 
1052         // Sort the networks with the most frequent ones at the front of the network list.
1053         Collections.sort(networks, mConfigManager.getScanListComparator());
1054 
1055         Set<Integer> channelSet = new HashSet<>();
1056 
1057         for (WifiConfiguration config : networks) {
1058             if (!addChannelFromWifiScoreCard(channelSet, config, maxCount, ageInMillis)) {
1059                 return channelSet;
1060             }
1061         }
1062 
1063         return channelSet;
1064     }
1065 
1066     // Watchdog timer handler
watchdogHandler()1067     private void watchdogHandler() {
1068         // Schedule the next timer and start a single scan if we are in disconnected state.
1069         // Otherwise, the watchdog timer will be scheduled when entering disconnected
1070         // state.
1071         if (mWifiState == WIFI_STATE_DISCONNECTED) {
1072             localLog("start a single scan from watchdogHandler");
1073 
1074             scheduleWatchdogTimer();
1075             startSingleScan(true, WIFI_WORK_SOURCE);
1076         }
1077     }
1078 
triggerScanOnNetworkChanges()1079     private void triggerScanOnNetworkChanges() {
1080         if (mScreenOn) {
1081             // Update scanning schedule if needed
1082             if (updateSingleScanningSchedule()) {
1083                 localLog("Saved networks / suggestions updated impacting single scan schedule");
1084                 startConnectivityScan(false);
1085             }
1086         } else {
1087             // Update the PNO scan network list when screen is off. Here we
1088             // rely on startConnectivityScan() to perform all the checks and clean up.
1089             localLog("Saved networks / suggestions updated impacting pno scan");
1090             startConnectivityScan(false);
1091         }
1092     }
1093 
1094     // Start a single scan and set up the interval for next single scan.
startPeriodicSingleScan()1095     private void startPeriodicSingleScan() {
1096         // Reaching here with scanning schedule is null means this is a false timer alarm
1097         if (getSingleScanningSchedule() == null) {
1098             return;
1099         }
1100 
1101         long currentTimeStamp = mClock.getElapsedSinceBootMillis();
1102 
1103         if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) {
1104             long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp;
1105             if (msSinceLastScan < getScheduledSingleScanIntervalMs(0)) {
1106                 localLog("Last periodic single scan started " + msSinceLastScan
1107                         + "ms ago, defer this new scan request.");
1108                 schedulePeriodicScanTimer(
1109                         getScheduledSingleScanIntervalMs(0) - (int) msSinceLastScan);
1110                 return;
1111             }
1112         }
1113 
1114         boolean isScanNeeded = true;
1115         boolean isFullBandScan = true;
1116 
1117         boolean isShortTimeSinceLastNetworkSelection =
1118                 ((currentTimeStamp - mLastNetworkSelectionTimeStamp)
1119                 <= 1000 * mContext.getResources().getInteger(
1120                 R.integer.config_wifiConnectedHighRssiScanMinimumWindowSizeSec));
1121 
1122         boolean isGoodLinkAndAcceptableInternetAndShortTimeSinceLastNetworkSelection =
1123                 mNetworkSelector.hasSufficientLinkQuality(mWifiInfo)
1124                 && mNetworkSelector.hasInternetOrExpectNoInternet(mWifiInfo)
1125                 && isShortTimeSinceLastNetworkSelection;
1126         // Check it is one of following conditions to skip scan (with firmware roaming)
1127         // or do partial scan only (without firmware roaming).
1128         // 1) Network is sufficient
1129         // 2) link is good, internet status is acceptable
1130         //    and it is a short time since last network selection
1131         // 3) There is active stream such that scan will be likely disruptive
1132         if (mWifiState == WIFI_STATE_CONNECTED
1133                 && (mNetworkSelector.isNetworkSufficient(mWifiInfo)
1134                 || isGoodLinkAndAcceptableInternetAndShortTimeSinceLastNetworkSelection
1135                 || mNetworkSelector.hasActiveStream(mWifiInfo))) {
1136             // If only partial scan is proposed and firmware roaming control is supported,
1137             // we will not issue any scan because firmware roaming will take care of
1138             // intra-SSID roam.
1139             if (mConnectivityHelper.isFirmwareRoamingSupported()) {
1140                 localLog("No partial scan because firmware roaming is supported.");
1141                 isScanNeeded = false;
1142             } else {
1143                 localLog("No full band scan because current network is sufficient");
1144                 isFullBandScan = false;
1145             }
1146         }
1147 
1148         if (isScanNeeded) {
1149             mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
1150 
1151             if (mWifiState == WIFI_STATE_DISCONNECTED
1152                     && mInitialScanState == INITIAL_SCAN_STATE_START) {
1153                 startSingleScan(false, WIFI_WORK_SOURCE);
1154 
1155                 // Note, initial partial scan may fail due to lack of channel history
1156                 // Hence, we verify state before changing to AWIATING_RESPONSE
1157                 if (mInitialScanState == INITIAL_SCAN_STATE_START) {
1158                     setInitialScanState(INITIAL_SCAN_STATE_AWAITING_RESPONSE);
1159                     mWifiMetrics.incrementInitialPartialScanCount();
1160                 }
1161                 // No scheduling for another scan (until we get the results)
1162                 return;
1163             }
1164 
1165             startSingleScan(isFullBandScan, WIFI_WORK_SOURCE);
1166             schedulePeriodicScanTimer(
1167                     getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex));
1168 
1169             // Set up the next scan interval in an exponential backoff fashion.
1170             mCurrentSingleScanScheduleIndex++;
1171         } else {
1172             // Since we already skipped this scan, keep the same scan interval for next scan.
1173             schedulePeriodicScanTimer(
1174                     getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex));
1175         }
1176     }
1177 
1178     // Retrieve a value from single scanning schedule in ms
getScheduledSingleScanIntervalMs(int index)1179     private int getScheduledSingleScanIntervalMs(int index) {
1180         synchronized (mLock) {
1181             if (mCurrentSingleScanScheduleSec == null) {
1182                 Log.e(TAG, "Invalid attempt to get schedule interval, Schedule array is null ");
1183 
1184                 // Use a default value
1185                 return DEFAULT_SCANNING_SCHEDULE_SEC[0] * 1000;
1186             }
1187 
1188             if (index >= mCurrentSingleScanScheduleSec.length) {
1189                 index = mCurrentSingleScanScheduleSec.length - 1;
1190             }
1191 
1192             return mCurrentSingleScanScheduleSec[index] * 1000;
1193         }
1194     }
1195 
1196     // Set the single scanning schedule
setSingleScanningSchedule(int[] scheduleSec)1197     private void setSingleScanningSchedule(int[] scheduleSec) {
1198         synchronized (mLock) {
1199             mCurrentSingleScanScheduleSec = scheduleSec;
1200         }
1201     }
1202 
1203     // Get the single scanning schedule
getSingleScanningSchedule()1204     private int[] getSingleScanningSchedule() {
1205         synchronized (mLock) {
1206             return mCurrentSingleScanScheduleSec;
1207         }
1208     }
1209 
1210     // Update the single scanning schedule if needed, and return true if update occurs
updateSingleScanningSchedule()1211     private boolean updateSingleScanningSchedule() {
1212         if (mWifiState != WIFI_STATE_CONNECTED) {
1213             // No need to update the scanning schedule
1214             return false;
1215         }
1216 
1217         boolean shouldUseSingleSavedNetworkSchedule = useSingleSavedNetworkSchedule();
1218 
1219         if (mCurrentSingleScanScheduleSec == mConnectedSingleScanScheduleSec
1220                 && shouldUseSingleSavedNetworkSchedule) {
1221             mCurrentSingleScanScheduleSec = mConnectedSingleSavedNetworkSingleScanScheduleSec;
1222             return true;
1223         }
1224         if (mCurrentSingleScanScheduleSec == mConnectedSingleSavedNetworkSingleScanScheduleSec
1225                 && !shouldUseSingleSavedNetworkSchedule) {
1226             mCurrentSingleScanScheduleSec = mConnectedSingleScanScheduleSec;
1227             return true;
1228         }
1229         return false;
1230     }
1231 
1232     // Set initial scan state
setInitialScanState(int state)1233     private void setInitialScanState(int state) {
1234         Log.i(TAG, "SetInitialScanState to : " + state);
1235         mInitialScanState = state;
1236     }
1237 
1238     // Reset the last periodic single scan time stamp so that the next periodic single
1239     // scan can start immediately.
resetLastPeriodicSingleScanTimeStamp()1240     private void resetLastPeriodicSingleScanTimeStamp() {
1241         mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
1242     }
1243 
1244     // Periodic scan timer handler
periodicScanTimerHandler()1245     private void periodicScanTimerHandler() {
1246         localLog("periodicScanTimerHandler");
1247 
1248         // Schedule the next timer and start a single scan if screen is on.
1249         if (mScreenOn) {
1250             startPeriodicSingleScan();
1251         }
1252     }
1253 
1254     // Start a single scan
startForcedSingleScan(boolean isFullBandScan, WorkSource workSource)1255     private void startForcedSingleScan(boolean isFullBandScan, WorkSource workSource) {
1256         mPnoScanListener.resetLowRssiNetworkRetryDelay();
1257 
1258         ScanSettings settings = new ScanSettings();
1259         if (!isFullBandScan) {
1260             if (!setScanChannels(settings)) {
1261                 isFullBandScan = true;
1262                 // Skip the initial scan since no channel history available
1263                 setInitialScanState(INITIAL_SCAN_STATE_COMPLETE);
1264             } else {
1265                 mInitialPartialScanChannelCount = settings.channels.length;
1266             }
1267         }
1268         settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; // always do high accuracy scans.
1269         settings.band = getScanBand(isFullBandScan);
1270         settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
1271                             | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
1272         settings.numBssidsPerScan = 0;
1273         settings.hiddenNetworks.clear();
1274         // retrieve the list of hidden network SSIDs from saved network to scan for
1275         settings.hiddenNetworks.addAll(mConfigManager.retrieveHiddenNetworkList());
1276         // retrieve the list of hidden network SSIDs from Network suggestion to scan for
1277         settings.hiddenNetworks.addAll(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList());
1278 
1279         SingleScanListener singleScanListener =
1280                 new SingleScanListener(isFullBandScan);
1281         mScanner.startScan(
1282                 settings, new HandlerExecutor(mEventHandler), singleScanListener, workSource);
1283         mWifiMetrics.incrementConnectivityOneshotScanCount();
1284     }
1285 
startSingleScan(boolean isFullBandScan, WorkSource workSource)1286     private void startSingleScan(boolean isFullBandScan, WorkSource workSource) {
1287         if (!mWifiEnabled || !mAutoJoinEnabled) {
1288             return;
1289         }
1290         startForcedSingleScan(isFullBandScan, workSource);
1291     }
1292 
1293     // Start a periodic scan when screen is on
startPeriodicScan(boolean scanImmediately)1294     private void startPeriodicScan(boolean scanImmediately) {
1295         mPnoScanListener.resetLowRssiNetworkRetryDelay();
1296 
1297         // No connectivity scan if auto roaming is disabled.
1298         if (mWifiState == WIFI_STATE_CONNECTED && !mContext.getResources().getBoolean(
1299                 R.bool.config_wifi_framework_enable_associated_network_selection)) {
1300             return;
1301         }
1302 
1303         // Due to b/28020168, timer based single scan will be scheduled
1304         // to provide periodic scan in an exponential backoff fashion.
1305         if (scanImmediately) {
1306             resetLastPeriodicSingleScanTimeStamp();
1307         }
1308         mCurrentSingleScanScheduleIndex = 0;
1309         startPeriodicSingleScan();
1310     }
1311 
deviceMobilityStateToPnoScanIntervalMs(@eviceMobilityState int state)1312     private int deviceMobilityStateToPnoScanIntervalMs(@DeviceMobilityState int state) {
1313         switch (state) {
1314             case WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN:
1315             case WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT:
1316             case WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT:
1317                 return mContext.getResources()
1318                         .getInteger(R.integer.config_wifiMovingPnoScanIntervalMillis);
1319             case WifiManager.DEVICE_MOBILITY_STATE_STATIONARY:
1320                 return mContext.getResources()
1321                         .getInteger(R.integer.config_wifiStationaryPnoScanIntervalMillis);
1322             default:
1323                 return -1;
1324         }
1325     }
1326 
1327     /**
1328      * Pass device mobility state to WifiChannelUtilization and
1329      * alter the PNO scan interval based on the current device mobility state.
1330      * If the device is stationary, it will likely not find many new Wifi networks. Thus, increase
1331      * the interval between scans. Decrease the interval between scans if the device begins to move
1332      * again.
1333      * @param newState the new device mobility state
1334      */
setDeviceMobilityState(@eviceMobilityState int newState)1335     public void setDeviceMobilityState(@DeviceMobilityState int newState) {
1336         int oldDeviceMobilityState = mDeviceMobilityState;
1337         localLog("Device mobility state changed. state=" + newState);
1338         int newPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(newState);
1339         if (newPnoScanIntervalMs < 0) {
1340             Log.e(TAG, "Invalid device mobility state: " + newState);
1341             return;
1342         }
1343         mDeviceMobilityState = newState;
1344         mWifiChannelUtilization.setDeviceMobilityState(newState);
1345 
1346         int oldPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(oldDeviceMobilityState);
1347         if (newPnoScanIntervalMs == oldPnoScanIntervalMs) {
1348             if (mPnoScanStarted) {
1349                 mWifiMetrics.logPnoScanStop();
1350                 mWifiMetrics.enterDeviceMobilityState(newState);
1351                 mWifiMetrics.logPnoScanStart();
1352             } else {
1353                 mWifiMetrics.enterDeviceMobilityState(newState);
1354             }
1355         } else {
1356             Log.d(TAG, "PNO Scan Interval changed to " + newPnoScanIntervalMs + " ms.");
1357 
1358             if (mPnoScanStarted) {
1359                 Log.d(TAG, "Restarting PNO Scan with new scan interval");
1360                 stopPnoScan();
1361                 mWifiMetrics.enterDeviceMobilityState(newState);
1362                 startDisconnectedPnoScan();
1363             } else {
1364                 mWifiMetrics.enterDeviceMobilityState(newState);
1365             }
1366         }
1367     }
1368 
1369     // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
startDisconnectedPnoScan()1370     private void startDisconnectedPnoScan() {
1371         // Initialize PNO settings
1372         PnoSettings pnoSettings = new PnoSettings();
1373         List<PnoSettings.PnoNetwork> pnoNetworkList = retrievePnoNetworkList();
1374         int listSize = pnoNetworkList.size();
1375 
1376         if (listSize == 0) {
1377             // No saved network
1378             localLog("No saved network for starting disconnected PNO.");
1379             return;
1380         }
1381 
1382         pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
1383         pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
1384         pnoSettings.min6GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_6_GHZ_START_FREQ_MHZ);
1385         pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_5_GHZ_START_FREQ_MHZ);
1386         pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi(
1387                 ScanResult.BAND_24_GHZ_START_FREQ_MHZ);
1388 
1389         // Initialize scan settings
1390         ScanSettings scanSettings = new ScanSettings();
1391         scanSettings.band = getScanBand();
1392         scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
1393         scanSettings.numBssidsPerScan = 0;
1394         scanSettings.periodInMs = deviceMobilityStateToPnoScanIntervalMs(mDeviceMobilityState);
1395 
1396         mPnoScanListener.clearScanDetails();
1397 
1398         mScanner.startDisconnectedPnoScan(
1399                 scanSettings, pnoSettings, new HandlerExecutor(mEventHandler), mPnoScanListener);
1400         mPnoScanStarted = true;
1401         mWifiMetrics.logPnoScanStart();
1402     }
1403 
getAllScanOptimizationNetworks()1404     private @NonNull List<WifiConfiguration> getAllScanOptimizationNetworks() {
1405         List<WifiConfiguration> networks = mConfigManager.getSavedNetworks(-1);
1406         networks.addAll(mWifiNetworkSuggestionsManager.getAllScanOptimizationSuggestionNetworks());
1407         // remove all auto-join disabled or network selection disabled network.
1408         networks.removeIf(config -> !config.allowAutojoin
1409                 || !config.getNetworkSelectionStatus().isNetworkEnabled());
1410         return networks;
1411     }
1412 
1413     /**
1414      * Retrieve the PnoNetworks from Saved and suggestion non-passpoint network.
1415      */
1416     @VisibleForTesting
retrievePnoNetworkList()1417     public List<PnoSettings.PnoNetwork> retrievePnoNetworkList() {
1418         List<WifiConfiguration> networks = getAllScanOptimizationNetworks();
1419 
1420         if (networks.isEmpty()) {
1421             return Collections.EMPTY_LIST;
1422         }
1423         Collections.sort(networks, mConfigManager.getScanListComparator());
1424         boolean pnoFrequencyCullingEnabled = mContext.getResources()
1425                 .getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled);
1426 
1427         List<PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
1428         Set<WifiScanner.PnoSettings.PnoNetwork> pnoSet = new HashSet<>();
1429         for (WifiConfiguration config : networks) {
1430             WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
1431                     WifiConfigurationUtil.createPnoNetwork(config);
1432             if (pnoSet.contains(pnoNetwork)) {
1433                 continue;
1434             }
1435             pnoList.add(pnoNetwork);
1436             pnoSet.add(pnoNetwork);
1437             if (!pnoFrequencyCullingEnabled) {
1438                 continue;
1439             }
1440             Set<Integer> channelList = new HashSet<>();
1441             addChannelFromWifiScoreCard(channelList, config, 0,
1442                     MAX_PNO_SCAN_FREQUENCY_AGE_MS);
1443             pnoNetwork.frequencies = channelList.stream().mapToInt(Integer::intValue).toArray();
1444             localLog("retrievePnoNetworkList " + pnoNetwork.ssid + ":"
1445                     + Arrays.toString(pnoNetwork.frequencies));
1446         }
1447         return pnoList;
1448     }
1449 
1450     // Stop PNO scan.
stopPnoScan()1451     private void stopPnoScan() {
1452         if (!mPnoScanStarted) return;
1453 
1454         mScanner.stopPnoScan(mPnoScanListener);
1455         mPnoScanStarted = false;
1456         mWifiMetrics.logPnoScanStop();
1457     }
1458 
1459     // Set up watchdog timer
scheduleWatchdogTimer()1460     private void scheduleWatchdogTimer() {
1461         localLog("scheduleWatchdogTimer");
1462 
1463         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1464                             mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS,
1465                             WATCHDOG_TIMER_TAG,
1466                             mWatchdogListener, mEventHandler);
1467     }
1468 
1469     // Schedules a delayed partial scan, which will scan the frequencies in mCachedWifiCandidates.
scheduleDelayedPartialScan(long delayMillis)1470     private void scheduleDelayedPartialScan(long delayMillis) {
1471         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1472                 mClock.getElapsedSinceBootMillis() + delayMillis, DELAYED_PARTIAL_SCAN_TIMER_TAG,
1473                 mDelayedPartialScanTimerListener, mEventHandler);
1474         mDelayedPartialScanTimerSet = true;
1475     }
1476 
1477     // Cancel the delayed partial scan timer.
cancelDelayedPartialScan()1478     private void cancelDelayedPartialScan() {
1479         if (mDelayedPartialScanTimerSet) {
1480             mAlarmManager.cancel(mDelayedPartialScanTimerListener);
1481             mDelayedPartialScanTimerSet = false;
1482         }
1483     }
1484 
1485     // Set up periodic scan timer
schedulePeriodicScanTimer(int intervalMs)1486     private void schedulePeriodicScanTimer(int intervalMs) {
1487         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1488                             mClock.getElapsedSinceBootMillis() + intervalMs,
1489                             PERIODIC_SCAN_TIMER_TAG,
1490                             mPeriodicScanTimerListener, mEventHandler);
1491         mPeriodicScanTimerSet = true;
1492     }
1493 
1494     // Cancel periodic scan timer
cancelPeriodicScanTimer()1495     private void cancelPeriodicScanTimer() {
1496         if (mPeriodicScanTimerSet) {
1497             mAlarmManager.cancel(mPeriodicScanTimerListener);
1498             mPeriodicScanTimerSet = false;
1499         }
1500     }
1501 
1502     // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
scheduleDelayedSingleScan(boolean isFullBandScan)1503     private void scheduleDelayedSingleScan(boolean isFullBandScan) {
1504         localLog("scheduleDelayedSingleScan");
1505 
1506         RestartSingleScanListener restartSingleScanListener =
1507                 new RestartSingleScanListener(isFullBandScan);
1508         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1509                             mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS,
1510                             RESTART_SINGLE_SCAN_TIMER_TAG,
1511                             restartSingleScanListener, mEventHandler);
1512     }
1513 
1514     // Set up timer to start a delayed scan after msFromNow milli-seconds
scheduleDelayedConnectivityScan(int msFromNow)1515     private void scheduleDelayedConnectivityScan(int msFromNow) {
1516         localLog("scheduleDelayedConnectivityScan");
1517 
1518         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1519                             mClock.getElapsedSinceBootMillis() + msFromNow,
1520                             RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
1521                             mRestartScanListener, mEventHandler);
1522 
1523     }
1524 
1525     // Start a connectivity scan. The scan method is chosen according to
1526     // the current screen state and WiFi state.
startConnectivityScan(boolean scanImmediately)1527     private void startConnectivityScan(boolean scanImmediately) {
1528         localLog("startConnectivityScan: screenOn=" + mScreenOn
1529                 + " wifiState=" + stateToString(mWifiState)
1530                 + " scanImmediately=" + scanImmediately
1531                 + " wifiEnabled=" + mWifiEnabled
1532                 + " wifiConnectivityManagerEnabled="
1533                 + mAutoJoinEnabled);
1534 
1535         if (!mWifiEnabled || !mAutoJoinEnabled) {
1536             return;
1537         }
1538 
1539         // Always stop outstanding connecivity scan if there is any
1540         stopConnectivityScan();
1541 
1542         // Don't start a connectivity scan while Wifi is in the transition
1543         // between connected and disconnected states.
1544         if ((mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED)
1545                 || (getSingleScanningSchedule() == null)) {
1546             return;
1547         }
1548 
1549         if (mScreenOn) {
1550             startPeriodicScan(scanImmediately);
1551         } else {
1552             if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
1553                 startDisconnectedPnoScan();
1554             }
1555         }
1556 
1557     }
1558 
1559     // Stop connectivity scan if there is any.
stopConnectivityScan()1560     private void stopConnectivityScan() {
1561         // Due to b/28020168, timer based single scan will be scheduled
1562         // to provide periodic scan in an exponential backoff fashion.
1563         cancelPeriodicScanTimer();
1564         cancelDelayedPartialScan();
1565         stopPnoScan();
1566     }
1567 
1568     /**
1569      * Handler for screen state (on/off) changes
1570      */
handleScreenStateChanged(boolean screenOn)1571     public void handleScreenStateChanged(boolean screenOn) {
1572         localLog("handleScreenStateChanged: screenOn=" + screenOn);
1573 
1574         mScreenOn = screenOn;
1575 
1576         if (mWifiState == WIFI_STATE_DISCONNECTED
1577                 && mContext.getResources().getBoolean(R.bool.config_wifiEnablePartialInitialScan)) {
1578             setInitialScanState(INITIAL_SCAN_STATE_START);
1579         }
1580 
1581         mOpenNetworkNotifier.handleScreenStateChanged(screenOn);
1582 
1583         startConnectivityScan(SCAN_ON_SCHEDULE);
1584     }
1585 
1586     /**
1587      * Helper function that converts the WIFI_STATE_XXX constants to string
1588      */
stateToString(int state)1589     private static String stateToString(int state) {
1590         switch (state) {
1591             case WIFI_STATE_CONNECTED:
1592                 return "connected";
1593             case WIFI_STATE_DISCONNECTED:
1594                 return "disconnected";
1595             case WIFI_STATE_TRANSITIONING:
1596                 return "transitioning";
1597             default:
1598                 return "unknown";
1599         }
1600     }
1601 
1602     /**
1603      * Check if Single saved network schedule should be used
1604      * This is true if the one of the following is satisfied:
1605      * 1. Device has a total of 1 network whether saved, passpoint, or suggestion.
1606      * 2. The device is connected to that network.
1607      */
useSingleSavedNetworkSchedule()1608     private boolean useSingleSavedNetworkSchedule() {
1609         WifiConfiguration currentNetwork = mStateMachine.getCurrentWifiConfiguration();
1610         if (currentNetwork == null) {
1611             localLog("Current network is missing, may caused by remove network and disconnecting ");
1612             return false;
1613         }
1614         List<WifiConfiguration> savedNetworks =
1615                 mConfigManager.getSavedNetworks(Process.WIFI_UID);
1616         // If we have multiple saved networks, then no need to proceed
1617         if (savedNetworks.size() > 1) {
1618             return false;
1619         }
1620 
1621         List<PasspointConfiguration> passpointNetworks =
1622                 mWifiInjector.getPasspointManager().getProviderConfigs(Process.WIFI_UID, true);
1623         // If we have multiple networks (saved + passpoint), then no need to proceed
1624         if (passpointNetworks.size() + savedNetworks.size() > 1) {
1625             return false;
1626         }
1627 
1628         Set<WifiNetworkSuggestion> suggestionsNetworks =
1629                 mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions();
1630         // If total size not equal to 1, then no need to proceed
1631         if (passpointNetworks.size() + savedNetworks.size() + suggestionsNetworks.size() != 1) {
1632             return false;
1633         }
1634 
1635         // Next verify that this network is the one device is connected to
1636         int currentNetworkId = currentNetwork.networkId;
1637 
1638         // If we have a single saved network, and we are connected to it, return true.
1639         if (savedNetworks.size() == 1) {
1640             return (savedNetworks.get(0).networkId == currentNetworkId);
1641         }
1642 
1643         // If we have a single passpoint network, and we are connected to it, return true.
1644         if (passpointNetworks.size() == 1) {
1645             String passpointKey = passpointNetworks.get(0).getUniqueId();
1646             WifiConfiguration config = mConfigManager.getConfiguredNetwork(passpointKey);
1647             return (config != null && config.networkId == currentNetworkId);
1648         }
1649 
1650         // If we have a single suggestion network, and we are connected to it, return true.
1651         WifiNetworkSuggestion network = suggestionsNetworks.iterator().next();
1652         String suggestionKey = network.getWifiConfiguration().getKey();
1653         WifiConfiguration config = mConfigManager.getConfiguredNetwork(suggestionKey);
1654         return (config != null && config.networkId == currentNetworkId);
1655     }
1656 
initSingleSavedNetworkSchedule()1657     private int[] initSingleSavedNetworkSchedule() {
1658         int[] schedule = mContext.getResources().getIntArray(
1659                     R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec);
1660         if (schedule == null || schedule.length == 0) {
1661             return null;
1662         }
1663 
1664         for (int val : schedule) {
1665             if (val <= 0) {
1666                 return null;
1667             }
1668         }
1669         return schedule;
1670     }
1671 
1672     /**
1673      * Handler for WiFi state (connected/disconnected) changes
1674      */
handleConnectionStateChanged(int state)1675     public void handleConnectionStateChanged(int state) {
1676         localLog("handleConnectionStateChanged: state=" + stateToString(state));
1677 
1678         if (mConnectedSingleScanScheduleSec == null) {
1679             mConnectedSingleScanScheduleSec = initializeScanningSchedule(WIFI_STATE_CONNECTED);
1680         }
1681         if (mDisconnectedSingleScanScheduleSec == null) {
1682             mDisconnectedSingleScanScheduleSec =
1683                     initializeScanningSchedule(WIFI_STATE_DISCONNECTED);
1684         }
1685         if (mConnectedSingleSavedNetworkSingleScanScheduleSec == null) {
1686             mConnectedSingleSavedNetworkSingleScanScheduleSec =
1687                     initSingleSavedNetworkSchedule();
1688             if (mConnectedSingleSavedNetworkSingleScanScheduleSec == null) {
1689                 mConnectedSingleSavedNetworkSingleScanScheduleSec = mConnectedSingleScanScheduleSec;
1690             }
1691         }
1692 
1693         mWifiState = state;
1694 
1695         // Reset BSSID of last connection attempt and kick off
1696         // the watchdog timer if entering disconnected state.
1697         if (mWifiState == WIFI_STATE_DISCONNECTED) {
1698             mLastConnectionAttemptBssid = null;
1699             scheduleWatchdogTimer();
1700             // Switch to the disconnected scanning schedule
1701             setSingleScanningSchedule(mDisconnectedSingleScanScheduleSec);
1702             startConnectivityScan(SCAN_IMMEDIATELY);
1703         } else if (mWifiState == WIFI_STATE_CONNECTED) {
1704             if (useSingleSavedNetworkSchedule()) {
1705                 // Switch to Single-Saved-Network connected schedule
1706                 setSingleScanningSchedule(mConnectedSingleSavedNetworkSingleScanScheduleSec);
1707             } else {
1708                 // Switch to connected single scanning schedule
1709                 setSingleScanningSchedule(mConnectedSingleScanScheduleSec);
1710             }
1711             startConnectivityScan(SCAN_ON_SCHEDULE);
1712         } else {
1713             // Intermediate state, no applicable single scanning schedule
1714             setSingleScanningSchedule(null);
1715             startConnectivityScan(SCAN_ON_SCHEDULE);
1716         }
1717     }
1718 
1719     /**
1720      * Handler when a WiFi connection attempt ended.
1721      *
1722      * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code.
1723      * @param bssid the failed network.
1724      * @param ssid identifies the failed network.
1725      */
handleConnectionAttemptEnded(int failureCode, @NonNull String bssid, @NonNull String ssid)1726     public void handleConnectionAttemptEnded(int failureCode, @NonNull String bssid,
1727             @NonNull String ssid) {
1728         if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) {
1729             String ssidUnquoted = (mWifiInfo.getWifiSsid() == null)
1730                     ? null
1731                     : mWifiInfo.getWifiSsid().toString();
1732             mOpenNetworkNotifier.handleWifiConnected(ssidUnquoted);
1733         } else {
1734             mOpenNetworkNotifier.handleConnectionFailure();
1735             retryConnectionOnLatestCandidates(bssid, ssid);
1736         }
1737     }
1738 
retryConnectionOnLatestCandidates(String bssid, String ssid)1739     private void retryConnectionOnLatestCandidates(String bssid, String ssid) {
1740         try {
1741             if (mLatestCandidates == null || mLatestCandidates.size() == 0
1742                     || mClock.getElapsedSinceBootMillis() - mLatestCandidatesTimestampMs
1743                     > TEMP_BSSID_BLOCK_DURATION) {
1744                 mLatestCandidates = null;
1745                 return;
1746             }
1747             MacAddress macAddress = MacAddress.fromString(bssid);
1748             int prevNumCandidates = mLatestCandidates.size();
1749             mLatestCandidates = mLatestCandidates.stream()
1750                     .filter(candidate -> !macAddress.equals(candidate.getKey().bssid))
1751                     .collect(Collectors.toList());
1752             if (prevNumCandidates == mLatestCandidates.size()) {
1753                 return;
1754             }
1755             WifiConfiguration candidate = mNetworkSelector.selectNetwork(mLatestCandidates);
1756             if (candidate != null) {
1757                 localLog("Automatic retry on the next best WNS candidate-" + candidate.SSID);
1758                 // Make sure that the failed BSSID is blocked for at least TEMP_BSSID_BLOCK_DURATION
1759                 // to prevent the supplicant from trying it again.
1760                 mBssidBlocklistMonitor.blockBssidForDurationMs(bssid, ssid,
1761                         TEMP_BSSID_BLOCK_DURATION);
1762                 connectToNetwork(candidate);
1763             }
1764         } catch (IllegalArgumentException e) {
1765             localLog("retryConnectionOnLatestCandidates: failed to create MacAddress from bssid="
1766                     + bssid);
1767             mLatestCandidates = null;
1768             return;
1769         }
1770     }
1771 
1772     // Enable auto-join if WifiConnectivityManager is enabled & we have any pending generic network
1773     // request (trusted or untrusted) and no specific network request in progress.
checkAllStatesAndEnableAutoJoin()1774     private void checkAllStatesAndEnableAutoJoin() {
1775         // if auto-join was disabled externally, don't re-enable for any triggers.
1776         // External triggers to disable always trumps any internal state.
1777         setAutoJoinEnabled(mAutoJoinEnabledExternal
1778                 && (mUntrustedConnectionAllowed || mTrustedConnectionAllowed)
1779                 && !mSpecificNetworkRequestInProgress);
1780         startConnectivityScan(SCAN_IMMEDIATELY);
1781     }
1782 
1783     /**
1784      * Triggered when {@link WifiNetworkFactory} has a pending general network request.
1785      */
setTrustedConnectionAllowed(boolean allowed)1786     public void setTrustedConnectionAllowed(boolean allowed) {
1787         localLog("setTrustedConnectionAllowed: allowed=" + allowed);
1788 
1789         if (mTrustedConnectionAllowed != allowed) {
1790             mTrustedConnectionAllowed = allowed;
1791             checkAllStatesAndEnableAutoJoin();
1792         }
1793     }
1794 
1795 
1796     /**
1797      * Triggered when {@link UntrustedWifiNetworkFactory} has a pending ephemeral network request.
1798      */
setUntrustedConnectionAllowed(boolean allowed)1799     public void setUntrustedConnectionAllowed(boolean allowed) {
1800         localLog("setUntrustedConnectionAllowed: allowed=" + allowed);
1801 
1802         if (mUntrustedConnectionAllowed != allowed) {
1803             mUntrustedConnectionAllowed = allowed;
1804             checkAllStatesAndEnableAutoJoin();
1805         }
1806     }
1807 
1808     /**
1809      * Triggered when {@link WifiNetworkFactory} is processing a specific network request.
1810      */
setSpecificNetworkRequestInProgress(boolean inProgress)1811     public void setSpecificNetworkRequestInProgress(boolean inProgress) {
1812         localLog("setsetSpecificNetworkRequestInProgress : inProgress=" + inProgress);
1813 
1814         if (mSpecificNetworkRequestInProgress != inProgress) {
1815             mSpecificNetworkRequestInProgress = inProgress;
1816             checkAllStatesAndEnableAutoJoin();
1817         }
1818     }
1819 
1820     /**
1821      * Handler when user specifies a particular network to connect to
1822      */
setUserConnectChoice(int netId)1823     public void setUserConnectChoice(int netId) {
1824         localLog("setUserConnectChoice: netId=" + netId);
1825 
1826         mNetworkSelector.setUserConnectChoice(netId);
1827     }
1828 
1829     /**
1830      * Handler to prepare for connection to a user or app specified network
1831      */
prepareForForcedConnection(int netId)1832     public void prepareForForcedConnection(int netId) {
1833         WifiConfiguration config = mConfigManager.getConfiguredNetwork(netId);
1834         if (config == null) {
1835             return;
1836         }
1837         localLog("prepareForForcedConnection: SSID=" + config.SSID);
1838 
1839         clearConnectionAttemptTimeStamps();
1840         mBssidBlocklistMonitor.clearBssidBlocklistForSsid(config.SSID);
1841     }
1842 
1843     /**
1844      * Handler for on-demand connectivity scan
1845      */
forceConnectivityScan(WorkSource workSource)1846     public void forceConnectivityScan(WorkSource workSource) {
1847         if (!mWifiEnabled) return;
1848         localLog("forceConnectivityScan in request of " + workSource);
1849 
1850         clearConnectionAttemptTimeStamps();
1851         mWaitForFullBandScanResults = true;
1852         startForcedSingleScan(true, workSource);
1853     }
1854 
1855     /**
1856      * Helper method to populate WifiScanner handle. This is done lazily because
1857      * WifiScanningService is started after WifiService.
1858      */
retrieveWifiScanner()1859     private void retrieveWifiScanner() {
1860         if (mScanner != null) return;
1861         mScanner = mWifiInjector.getWifiScanner();
1862         checkNotNull(mScanner);
1863         // Register for all single scan results
1864         mScanner.registerScanListener(new HandlerExecutor(mEventHandler), mAllSingleScanListener);
1865     }
1866 
1867     /**
1868      * Start WifiConnectivityManager
1869      */
start()1870     private void start() {
1871         if (mRunning) return;
1872         retrieveWifiScanner();
1873         mConnectivityHelper.getFirmwareRoamingInfo();
1874         mBssidBlocklistMonitor.clearBssidBlocklist();
1875         mWifiChannelUtilization.init(mStateMachine.getWifiLinkLayerStats());
1876 
1877         if (mContext.getResources().getBoolean(R.bool.config_wifiEnablePartialInitialScan)) {
1878             setInitialScanState(INITIAL_SCAN_STATE_START);
1879         }
1880 
1881         mRunning = true;
1882         mLatestCandidates = null;
1883         mLatestCandidatesTimestampMs = 0;
1884     }
1885 
1886     /**
1887      * Stop and reset WifiConnectivityManager
1888      */
stop()1889     private void stop() {
1890         if (!mRunning) return;
1891         mRunning = false;
1892         stopConnectivityScan();
1893         resetLastPeriodicSingleScanTimeStamp();
1894         mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */);
1895         mLastConnectionAttemptBssid = null;
1896         mWaitForFullBandScanResults = false;
1897         mLatestCandidates = null;
1898         mLatestCandidatesTimestampMs = 0;
1899         mScanRestartCount = 0;
1900     }
1901 
1902     /**
1903      * Update WifiConnectivityManager running state
1904      *
1905      * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager
1906      * are enabled, otherwise stop it.
1907      */
updateRunningState()1908     private void updateRunningState() {
1909         if (mWifiEnabled && mAutoJoinEnabled) {
1910             localLog("Starting up WifiConnectivityManager");
1911             start();
1912         } else {
1913             localLog("Stopping WifiConnectivityManager");
1914             stop();
1915         }
1916     }
1917 
1918     /**
1919      * Inform WiFi is enabled for connection or not
1920      */
setWifiEnabled(boolean enable)1921     public void setWifiEnabled(boolean enable) {
1922         localLog("Set WiFi " + (enable ? "enabled" : "disabled"));
1923 
1924         if (mWifiEnabled && !enable) {
1925             mNetworkSelector.resetOnDisable();
1926             mBssidBlocklistMonitor.clearBssidBlocklist();
1927         }
1928         mWifiEnabled = enable;
1929         updateRunningState();
1930     }
1931 
1932     /**
1933      * Turn on/off the WifiConnectivityManager at runtime
1934      */
setAutoJoinEnabled(boolean enable)1935     private void setAutoJoinEnabled(boolean enable) {
1936         mAutoJoinEnabled = enable;
1937         updateRunningState();
1938     }
1939 
1940     /**
1941      * Turn on/off the auto join at runtime
1942      */
setAutoJoinEnabledExternal(boolean enable)1943     public void setAutoJoinEnabledExternal(boolean enable) {
1944         localLog("Set auto join " + (enable ? "enabled" : "disabled"));
1945 
1946         if (mAutoJoinEnabledExternal != enable) {
1947             mAutoJoinEnabledExternal = enable;
1948             checkAllStatesAndEnableAutoJoin();
1949         }
1950     }
1951 
1952     @VisibleForTesting
getLowRssiNetworkRetryDelay()1953     int getLowRssiNetworkRetryDelay() {
1954         return mPnoScanListener.getLowRssiNetworkRetryDelay();
1955     }
1956 
1957     @VisibleForTesting
getLastPeriodicSingleScanTimeStamp()1958     long getLastPeriodicSingleScanTimeStamp() {
1959         return mLastPeriodicSingleScanTimeStamp;
1960     }
1961 
1962     /**
1963      * Dump the local logs.
1964      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)1965     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1966         pw.println("Dump of WifiConnectivityManager");
1967         pw.println("WifiConnectivityManager - Log Begin ----");
1968         mLocalLog.dump(fd, pw, args);
1969         pw.println("WifiConnectivityManager - Log End ----");
1970         mOpenNetworkNotifier.dump(fd, pw, args);
1971         mBssidBlocklistMonitor.dump(fd, pw, args);
1972     }
1973 }
1974