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