1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_SCAN_THROTTLE_ENABLED;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.ActivityManager;
24 import android.app.AppOpsManager;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.net.wifi.IScanResultsCallback;
28 import android.net.wifi.ScanResult;
29 import android.net.wifi.WifiManager;
30 import android.net.wifi.WifiScanner;
31 import android.os.Handler;
32 import android.os.HandlerExecutor;
33 import android.os.RemoteCallbackList;
34 import android.os.RemoteException;
35 import android.os.UserHandle;
36 import android.os.WorkSource;
37 import android.util.ArrayMap;
38 import android.util.Log;
39 import android.util.Pair;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.modules.utils.build.SdkLevel;
43 import com.android.server.wifi.util.ScanResultUtil;
44 import com.android.server.wifi.util.WifiPermissionsUtil;
45 import com.android.wifi.resources.R;
46 
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.LinkedList;
52 import java.util.List;
53 import java.util.Map;
54 
55 import javax.annotation.concurrent.NotThreadSafe;
56 
57 /**
58  * This class manages all scan requests originating from external apps using the
59  * {@link WifiManager#startScan()}.
60  *
61  * This class is responsible for:
62  * a) Enable/Disable scanning based on the request from {@link ActiveModeWarden}.
63  * a) Forwarding scan requests from {@link WifiManager#startScan()} to
64  * {@link WifiScanner#startScan(WifiScanner.ScanSettings, WifiScanner.ScanListener)}.
65  * Will essentially proxy scan requests from WifiService to WifiScanningService.
66  * b) Cache the results of these scan requests and return them when
67  * {@link WifiManager#getScanResults()} is invoked.
68  * c) Will send out the {@link WifiManager#SCAN_RESULTS_AVAILABLE_ACTION} broadcast when new
69  * scan results are available.
70  * d) Throttle scan requests from non-setting apps:
71  *  a) Each foreground app can request a max of
72  *   {@link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS} scan every
73  *   {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS}.
74  *  b) Background apps combined can request 1 scan every
75  *   {@link #SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}.
76  * Note: This class is not thread-safe. It needs to be invoked from the main Wifi thread only.
77  */
78 @NotThreadSafe
79 public class ScanRequestProxy {
80     private static final String TAG = "WifiScanRequestProxy";
81 
82     @VisibleForTesting
83     public static final int SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS = 120 * 1000;
84     @VisibleForTesting
85     public static final int SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS = 4;
86     @VisibleForTesting
87     public static final int SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS = 30 * 60 * 1000;
88 
89     private final Context mContext;
90     private final Handler mHandler;
91     private final AppOpsManager mAppOps;
92     private final ActivityManager mActivityManager;
93     private final WifiInjector mWifiInjector;
94     private final WifiConfigManager mWifiConfigManager;
95     private final WifiPermissionsUtil mWifiPermissionsUtil;
96     private final WifiMetrics mWifiMetrics;
97     private final Clock mClock;
98     private final WifiSettingsConfigStore mSettingsConfigStore;
99     private WifiScanner mWifiScanner;
100 
101     // Verbose logging flag.
102     private boolean mVerboseLoggingEnabled = false;
103     private boolean mThrottleEnabled = true;
104     // Flag to decide if we need to scan or not.
105     private boolean mScanningEnabled = false;
106     // Flag to decide if we need to scan for hidden networks or not.
107     private boolean mScanningForHiddenNetworksEnabled = false;
108     // Timestamps for the last scan requested by any background app.
109     private long mLastScanTimestampForBgApps = 0;
110     // Timestamps for the list of last few scan requests by each foreground app.
111     // Keys in the map = Pair<Uid, PackageName> of the app.
112     // Values in the map = List of the last few scan request timestamps from the app.
113     private final ArrayMap<Pair<Integer, String>, LinkedList<Long>> mLastScanTimestampsForFgApps =
114             new ArrayMap();
115     // Scan results cached from the last full single scan request.
116     // Stored as a map of bssid -> ScanResult to allow other clients to perform ScanResult lookup
117     // for bssid more efficiently.
118     private final Map<String, ScanResult> mLastScanResultsMap = new HashMap<>();
119     // external ScanResultCallback tracker
120     private final RemoteCallbackList<IScanResultsCallback> mRegisteredScanResultsCallbacks;
121     // Global scan listener for listening to all scan requests.
122     private class GlobalScanListener implements WifiScanner.ScanListener {
123         @Override
onSuccess()124         public void onSuccess() {
125             // Ignore. These will be processed from the scan request listener.
126         }
127 
128         @Override
onFailure(int reason, String description)129         public void onFailure(int reason, String description) {
130             // Ignore. These will be processed from the scan request listener.
131         }
132 
133         @Override
onResults(WifiScanner.ScanData[] scanDatas)134         public void onResults(WifiScanner.ScanData[] scanDatas) {
135             if (mVerboseLoggingEnabled) {
136                 Log.d(TAG, "Scan results received");
137             }
138             // For single scans, the array size should always be 1.
139             if (scanDatas.length != 1) {
140                 Log.wtf(TAG, "Found more than 1 batch of scan results, Failing...");
141                 sendScanResultBroadcast(false);
142                 return;
143             }
144             WifiScanner.ScanData scanData = scanDatas[0];
145             ScanResult[] scanResults = scanData.getResults();
146             if (mVerboseLoggingEnabled) {
147                 Log.d(TAG, "Received " + scanResults.length + " scan results");
148             }
149             // Only process full band scan results.
150             if (WifiScanner.isFullBandScan(scanData.getScannedBandsInternal(), false)) {
151                 // Store the last scan results & send out the scan completion broadcast.
152                 mLastScanResultsMap.clear();
153                 Arrays.stream(scanResults).forEach(s -> mLastScanResultsMap.put(s.BSSID, s));
154                 sendScanResultBroadcast(true);
155                 sendScanResultsAvailableToCallbacks();
156             }
157         }
158 
159         @Override
onFullResult(ScanResult fullScanResult)160         public void onFullResult(ScanResult fullScanResult) {
161             // Ignore for single scans.
162         }
163 
164         @Override
onPeriodChanged(int periodInMs)165         public void onPeriodChanged(int periodInMs) {
166             // Ignore for single scans.
167         }
168     };
169 
170     // Common scan listener for scan requests initiated by this class.
171     private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
172         @Override
onSuccess()173         public void onSuccess() {
174             // Scan request succeeded, wait for results to report to external clients.
175             if (mVerboseLoggingEnabled) {
176                 Log.d(TAG, "Scan request succeeded");
177             }
178         }
179 
180         @Override
onFailure(int reason, String description)181         public void onFailure(int reason, String description) {
182             Log.e(TAG, "Scan failure received. reason: " + reason + ",description: " + description);
183             sendScanResultBroadcast(false);
184         }
185 
186         @Override
onResults(WifiScanner.ScanData[] scanDatas)187         public void onResults(WifiScanner.ScanData[] scanDatas) {
188             // Ignore. These will be processed from the global listener.
189         }
190 
191         @Override
onFullResult(ScanResult fullScanResult)192         public void onFullResult(ScanResult fullScanResult) {
193             // Ignore for single scans.
194         }
195 
196         @Override
onPeriodChanged(int periodInMs)197         public void onPeriodChanged(int periodInMs) {
198             // Ignore for single scans.
199         }
200     };
201 
ScanRequestProxy(Context context, AppOpsManager appOpsManager, ActivityManager activityManager, WifiInjector wifiInjector, WifiConfigManager configManager, WifiPermissionsUtil wifiPermissionUtil, WifiMetrics wifiMetrics, Clock clock, Handler handler, WifiSettingsConfigStore settingsConfigStore)202     ScanRequestProxy(Context context, AppOpsManager appOpsManager, ActivityManager activityManager,
203                      WifiInjector wifiInjector, WifiConfigManager configManager,
204                      WifiPermissionsUtil wifiPermissionUtil, WifiMetrics wifiMetrics, Clock clock,
205                      Handler handler, WifiSettingsConfigStore settingsConfigStore) {
206         mContext = context;
207         mHandler = handler;
208         mAppOps = appOpsManager;
209         mActivityManager = activityManager;
210         mWifiInjector = wifiInjector;
211         mWifiConfigManager = configManager;
212         mWifiPermissionsUtil = wifiPermissionUtil;
213         mWifiMetrics = wifiMetrics;
214         mClock = clock;
215         mSettingsConfigStore = settingsConfigStore;
216         mRegisteredScanResultsCallbacks = new RemoteCallbackList<>();
217     }
218 
219     /**
220      * Enable verbose logging.
221      */
enableVerboseLogging(int verbose)222     public void enableVerboseLogging(int verbose) {
223         mVerboseLoggingEnabled = (verbose > 0);
224     }
225 
226     /**
227      * Helper method to populate WifiScanner handle. This is done lazily because
228      * WifiScanningService is started after WifiService.
229      */
retrieveWifiScannerIfNecessary()230     private boolean retrieveWifiScannerIfNecessary() {
231         if (mWifiScanner == null) {
232             mWifiScanner = mWifiInjector.getWifiScanner();
233             // Start listening for throttle settings change after we retrieve scanner instance.
234             mThrottleEnabled = mSettingsConfigStore.get(WIFI_SCAN_THROTTLE_ENABLED);
235             if (mVerboseLoggingEnabled) {
236                 Log.v(TAG, "Scan throttle enabled " + mThrottleEnabled);
237             }
238             // Register the global scan listener.
239             if (mWifiScanner != null) {
240                 mWifiScanner.registerScanListener(
241                         new HandlerExecutor(mHandler), new GlobalScanListener());
242             }
243         }
244         return mWifiScanner != null;
245     }
246 
247     /**
248      * Method that lets public apps know that scans are available.
249      *
250      * @param context Context to use for the notification
251      * @param available boolean indicating if scanning is available
252      */
sendScanAvailableBroadcast(Context context, boolean available)253     private void sendScanAvailableBroadcast(Context context, boolean available) {
254         Log.d(TAG, "Sending scan available broadcast: " + available);
255         final Intent intent = new Intent(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED);
256         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
257         intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, available);
258         context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
259     }
260 
enableScanningInternal(boolean enable)261     private void enableScanningInternal(boolean enable) {
262         if (!retrieveWifiScannerIfNecessary()) {
263             Log.e(TAG, "Failed to retrieve wifiscanner");
264             return;
265         }
266         mWifiScanner.setScanningEnabled(enable);
267         sendScanAvailableBroadcast(mContext, enable);
268         if (!enable) clearScanResults();
269         Log.i(TAG, "Scanning is " + (enable ? "enabled" : "disabled"));
270     }
271 
272     /**
273      * Enable/disable scanning.
274      *
275      * @param enable true to enable, false to disable.
276      * @param enableScanningForHiddenNetworks true to enable scanning for hidden networks,
277      *                                        false to disable.
278      */
enableScanning(boolean enable, boolean enableScanningForHiddenNetworks)279     public void enableScanning(boolean enable, boolean enableScanningForHiddenNetworks) {
280         if (enable) {
281             enableScanningInternal(true);
282             mScanningForHiddenNetworksEnabled = enableScanningForHiddenNetworks;
283             Log.i(TAG, "Scanning for hidden networks is "
284                     + (enableScanningForHiddenNetworks ? "enabled" : "disabled"));
285         } else {
286             enableScanningInternal(false);
287         }
288         mScanningEnabled = enable;
289     }
290 
291 
292     /**
293      * Helper method to send the scan request status broadcast.
294      */
sendScanResultBroadcast(boolean scanSucceeded)295     private void sendScanResultBroadcast(boolean scanSucceeded) {
296         Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
297         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
298         intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
299         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
300     }
301 
302     /**
303      * Helper method to send the scan request failure broadcast to specified package.
304      */
sendScanResultFailureBroadcastToPackage(String packageName)305     private void sendScanResultFailureBroadcastToPackage(String packageName) {
306         Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
307         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
308         intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
309         intent.setPackage(packageName);
310         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
311     }
312 
trimPastScanRequestTimesForForegroundApp( List<Long> scanRequestTimestamps, long currentTimeMillis)313     private void trimPastScanRequestTimesForForegroundApp(
314             List<Long> scanRequestTimestamps, long currentTimeMillis) {
315         Iterator<Long> timestampsIter = scanRequestTimestamps.iterator();
316         while (timestampsIter.hasNext()) {
317             Long scanRequestTimeMillis = timestampsIter.next();
318             if ((currentTimeMillis - scanRequestTimeMillis)
319                     > SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS) {
320                 timestampsIter.remove();
321             } else {
322                 // This list is sorted by timestamps, so we can skip any more checks
323                 break;
324             }
325         }
326     }
327 
getOrCreateScanRequestTimestampsForForegroundApp( int callingUid, String packageName)328     private LinkedList<Long> getOrCreateScanRequestTimestampsForForegroundApp(
329             int callingUid, String packageName) {
330         Pair<Integer, String> uidAndPackageNamePair = Pair.create(callingUid, packageName);
331         LinkedList<Long> scanRequestTimestamps =
332                 mLastScanTimestampsForFgApps.get(uidAndPackageNamePair);
333         if (scanRequestTimestamps == null) {
334             scanRequestTimestamps = new LinkedList<>();
335             mLastScanTimestampsForFgApps.put(uidAndPackageNamePair, scanRequestTimestamps);
336         }
337         return scanRequestTimestamps;
338     }
339 
340     /**
341      * Checks if the scan request from the app (specified by packageName) needs
342      * to be throttled.
343      * The throttle limit allows a max of {@link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS}
344      * in {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS} window.
345      */
shouldScanRequestBeThrottledForForegroundApp( int callingUid, String packageName)346     private boolean shouldScanRequestBeThrottledForForegroundApp(
347             int callingUid, String packageName) {
348         if (isPackageNameInExceptionList(packageName, true)) {
349             return false;
350         }
351         LinkedList<Long> scanRequestTimestamps =
352                 getOrCreateScanRequestTimestampsForForegroundApp(callingUid, packageName);
353         long currentTimeMillis = mClock.getElapsedSinceBootMillis();
354         // First evict old entries from the list.
355         trimPastScanRequestTimesForForegroundApp(scanRequestTimestamps, currentTimeMillis);
356         if (scanRequestTimestamps.size() >= SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS) {
357             return true;
358         }
359         // Proceed with the scan request and record the time.
360         scanRequestTimestamps.addLast(currentTimeMillis);
361         return false;
362     }
363 
isPackageNameInExceptionList(String packageName, boolean isForeground)364     private boolean isPackageNameInExceptionList(String packageName, boolean isForeground) {
365         if (packageName == null) {
366             return false;
367         }
368         String[] exceptionList = mContext.getResources().getStringArray(isForeground
369                 ? R.array.config_wifiForegroundScanThrottleExceptionList
370                 : R.array.config_wifiBackgroundScanThrottleExceptionList);
371         if (exceptionList == null) {
372             return false;
373         }
374         for (String name : exceptionList) {
375             if (packageName.equals(name)) {
376                 return true;
377             }
378         }
379         return false;
380     }
381 
382     /**
383      * Checks if the scan request from a background app needs to be throttled.
384      */
shouldScanRequestBeThrottledForBackgroundApp(String packageName)385     private boolean shouldScanRequestBeThrottledForBackgroundApp(String packageName) {
386         if (isPackageNameInExceptionList(packageName, false)) {
387             return false;
388         }
389         long lastScanMs = mLastScanTimestampForBgApps;
390         long elapsedRealtime = mClock.getElapsedSinceBootMillis();
391         if (lastScanMs != 0
392                 && (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS) {
393             return true;
394         }
395         // Proceed with the scan request and record the time.
396         mLastScanTimestampForBgApps = elapsedRealtime;
397         return false;
398     }
399 
400     /**
401      * Safely retrieve package importance.
402      */
getPackageImportance(int callingUid, String packageName)403     private int getPackageImportance(int callingUid, String packageName) {
404         mAppOps.checkPackage(callingUid, packageName);
405         try {
406             return mActivityManager.getPackageImportance(packageName);
407         } catch (SecurityException e) {
408             Log.e(TAG, "Failed to check the app state", e);
409             return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
410         }
411     }
412 
413     /**
414      * Checks if the scan request from the app (specified by callingUid & packageName) needs
415      * to be throttled.
416      */
shouldScanRequestBeThrottledForApp(int callingUid, String packageName, int packageImportance)417     private boolean shouldScanRequestBeThrottledForApp(int callingUid, String packageName,
418             int packageImportance) {
419         boolean isThrottled;
420         if (packageImportance
421                 > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
422             isThrottled = shouldScanRequestBeThrottledForBackgroundApp(packageName);
423             if (isThrottled) {
424                 if (mVerboseLoggingEnabled) {
425                     Log.v(TAG, "Background scan app request [" + callingUid + ", "
426                             + packageName + "]");
427                 }
428                 mWifiMetrics.incrementExternalBackgroundAppOneshotScanRequestsThrottledCount();
429             }
430         } else {
431             isThrottled = shouldScanRequestBeThrottledForForegroundApp(callingUid, packageName);
432             if (isThrottled) {
433                 if (mVerboseLoggingEnabled) {
434                     Log.v(TAG, "Foreground scan app request [" + callingUid + ", "
435                             + packageName + "]");
436                 }
437                 mWifiMetrics.incrementExternalForegroundAppOneshotScanRequestsThrottledCount();
438             }
439         }
440         mWifiMetrics.incrementExternalAppOneshotScanRequestsCount();
441         return isThrottled;
442     }
443 
444     /**
445      * Initiate a wifi scan.
446      *
447      * @param callingUid The uid initiating the wifi scan. Blame will be given to this uid.
448      * @return true if the scan request was placed or a scan is already ongoing, false otherwise.
449      */
startScan(int callingUid, String packageName)450     public boolean startScan(int callingUid, String packageName) {
451         if (!mScanningEnabled || !retrieveWifiScannerIfNecessary()) {
452             Log.e(TAG, "Failed to retrieve wifiscanner");
453             sendScanResultFailureBroadcastToPackage(packageName);
454             return false;
455         }
456         boolean fromSettingsOrSetupWizard =
457                 mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
458                         || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
459         // Check and throttle scan request unless,
460         // a) App has either NETWORK_SETTINGS or NETWORK_SETUP_WIZARD permission.
461         // b) Throttling has been disabled by user.
462         int packageImportance = getPackageImportance(callingUid, packageName);
463         if (!fromSettingsOrSetupWizard && mThrottleEnabled
464                 && shouldScanRequestBeThrottledForApp(callingUid, packageName, packageImportance)) {
465             Log.i(TAG, "Scan request from " + packageName + " throttled");
466             sendScanResultFailureBroadcastToPackage(packageName);
467             return false;
468         }
469         // Create a worksource using the caller's UID.
470         WorkSource workSource = new WorkSource(callingUid, packageName);
471         mWifiMetrics.getScanMetrics().setWorkSource(workSource);
472         mWifiMetrics.getScanMetrics().setImportance(packageImportance);
473 
474         // Create the scan settings.
475         WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
476         // Scan requests from apps with network settings will be of high accuracy type.
477         if (fromSettingsOrSetupWizard) {
478             settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
479         } else {
480             if (SdkLevel.isAtLeastS()) {
481                 // since the scan request is from a normal app, do not scan all 6Ghz channels.
482                 settings.set6GhzPscOnlyEnabled(true);
483             }
484         }
485         settings.band = WifiScanner.WIFI_BAND_ALL;
486         settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
487                 | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
488         if (mScanningForHiddenNetworksEnabled) {
489             settings.hiddenNetworks.clear();
490             // retrieve the list of hidden network SSIDs from saved network to scan for, if enabled.
491             settings.hiddenNetworks.addAll(mWifiConfigManager.retrieveHiddenNetworkList());
492             // retrieve the list of hidden network SSIDs from Network suggestion to scan for.
493             settings.hiddenNetworks.addAll(
494                     mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList());
495         }
496         mWifiScanner.startScan(settings, new HandlerExecutor(mHandler),
497                 new ScanRequestProxyScanListener(), workSource);
498         return true;
499     }
500 
501     /**
502      * Return the results of the most recent access point scan, in the form of
503      * a list of {@link ScanResult} objects.
504      * @return the list of results
505      */
getScanResults()506     public List<ScanResult> getScanResults() {
507         // return a copy to prevent external modification
508         return new ArrayList<>(mLastScanResultsMap.values());
509     }
510 
511     /**
512      * Return the ScanResult from the most recent access point scan for the provided bssid.
513      *
514      * @param bssid BSSID as string {@link ScanResult#BSSID}.
515      * @return ScanResult for the corresponding bssid if found, null otherwise.
516      */
getScanResult(@onNull String bssid)517     public @Nullable ScanResult getScanResult(@NonNull String bssid) {
518         ScanResult scanResult = mLastScanResultsMap.get(bssid);
519         if (scanResult == null) return null;
520         // return a copy to prevent external modification
521         return new ScanResult(scanResult);
522     }
523 
524 
525     /**
526      * Clear the stored scan results.
527      */
clearScanResults()528     private void clearScanResults() {
529         mLastScanResultsMap.clear();
530         mLastScanTimestampForBgApps = 0;
531         mLastScanTimestampsForFgApps.clear();
532     }
533 
534     /**
535      * Clear any scan timestamps being stored for the app.
536      *
537      * @param uid Uid of the package.
538      * @param packageName Name of the package.
539      */
clearScanRequestTimestampsForApp(@onNull String packageName, int uid)540     public void clearScanRequestTimestampsForApp(@NonNull String packageName, int uid) {
541         if (mVerboseLoggingEnabled) {
542             Log.v(TAG, "Clearing scan request timestamps for uid=" + uid + ", packageName="
543                     + packageName);
544         }
545         mLastScanTimestampsForFgApps.remove(Pair.create(uid, packageName));
546     }
547 
sendScanResultsAvailableToCallbacks()548     private void sendScanResultsAvailableToCallbacks() {
549         int itemCount = mRegisteredScanResultsCallbacks.beginBroadcast();
550         for (int i = 0; i < itemCount; i++) {
551             try {
552                 mRegisteredScanResultsCallbacks.getBroadcastItem(i).onScanResultsAvailable();
553             } catch (RemoteException e) {
554                 Log.e(TAG, "onScanResultsAvailable: remote exception -- " + e);
555             }
556         }
557         mRegisteredScanResultsCallbacks.finishBroadcast();
558     }
559 
560     /**
561      * Register a callback on scan event
562      * @param callback IScanResultListener instance to add.
563      * @return true if succeed otherwise false.
564      */
registerScanResultsCallback(IScanResultsCallback callback)565     public boolean registerScanResultsCallback(IScanResultsCallback callback) {
566         return mRegisteredScanResultsCallbacks.register(callback);
567     }
568 
569     /**
570      * Unregister a callback on scan event
571      * @param callback IScanResultListener instance to add.
572      */
unregisterScanResultsCallback(IScanResultsCallback callback)573     public void unregisterScanResultsCallback(IScanResultsCallback callback) {
574         mRegisteredScanResultsCallbacks.unregister(callback);
575     }
576 
577     /**
578      * Enable/disable wifi scan throttling from 3rd party apps.
579      */
setScanThrottleEnabled(boolean enable)580     public void setScanThrottleEnabled(boolean enable) {
581         mThrottleEnabled = enable;
582         mSettingsConfigStore.put(WIFI_SCAN_THROTTLE_ENABLED, enable);
583         Log.i(TAG, "Scan throttle enabled " + mThrottleEnabled);
584     }
585 
586     /**
587      * Get the persisted Wi-Fi scan throttle state, set by
588      * {@link #setScanThrottleEnabled(boolean)}.
589      */
isScanThrottleEnabled()590     public boolean isScanThrottleEnabled() {
591         return mThrottleEnabled;
592     }
593 
594     /** Indicate whether there are WPA2 personal only networks. */
isWpa2PersonalOnlyNetworkInRange(String ssid)595     public boolean isWpa2PersonalOnlyNetworkInRange(String ssid) {
596         return mLastScanResultsMap.values().stream().anyMatch(r ->
597                 ssid.equals(ScanResultUtil.createQuotedSSID(r.SSID))
598                 && ScanResultUtil.isScanResultForPskNetwork(r)
599                 && !ScanResultUtil.isScanResultForSaeNetwork(r));
600     }
601 
602     /** Indicate whether there are WPA3 only networks. */
isWpa3PersonalOnlyNetworkInRange(String ssid)603     public boolean isWpa3PersonalOnlyNetworkInRange(String ssid) {
604         return mLastScanResultsMap.values().stream().anyMatch(r ->
605                 ssid.equals(ScanResultUtil.createQuotedSSID(r.SSID))
606                 && ScanResultUtil.isScanResultForSaeNetwork(r)
607                 && !ScanResultUtil.isScanResultForPskNetwork(r));
608     }
609 
610     /** Indicate whether there are OPEN only networks. */
isOpenOnlyNetworkInRange(String ssid)611     public boolean isOpenOnlyNetworkInRange(String ssid) {
612         return mLastScanResultsMap.values().stream().anyMatch(r ->
613                 ssid.equals(ScanResultUtil.createQuotedSSID(r.SSID))
614                 && ScanResultUtil.isScanResultForOpenNetwork(r)
615                 && !ScanResultUtil.isScanResultForOweNetwork(r));
616     }
617 
618     /** Indicate whether there are OWE only networks. */
isOweOnlyNetworkInRange(String ssid)619     public boolean isOweOnlyNetworkInRange(String ssid) {
620         return mLastScanResultsMap.values().stream().anyMatch(r ->
621                 ssid.equals(ScanResultUtil.createQuotedSSID(r.SSID))
622                 && ScanResultUtil.isScanResultForOweNetwork(r)
623                 && !ScanResultUtil.isScanResultForOweTransitionNetwork(r));
624     }
625 
626     /** Indicate whether there are WPA2 Enterprise only networks. */
isWpa2EnterpriseOnlyNetworkInRange(String ssid)627     public boolean isWpa2EnterpriseOnlyNetworkInRange(String ssid) {
628         return mLastScanResultsMap.values().stream().anyMatch(r ->
629                 ssid.equals(ScanResultUtil.createQuotedSSID(r.SSID))
630                 && ScanResultUtil.isScanResultForEapNetwork(r)
631                 && !ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(r)
632                 && !ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r));
633     }
634 
635     /** Indicate whether there are WPA3 Enterprise only networks. */
isWpa3EnterpriseOnlyNetworkInRange(String ssid)636     public boolean isWpa3EnterpriseOnlyNetworkInRange(String ssid) {
637         return mLastScanResultsMap.values().stream().anyMatch(r ->
638                 ssid.equals(ScanResultUtil.createQuotedSSID(r.SSID))
639                 && ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r)
640                 && !ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(r)
641                 && !ScanResultUtil.isScanResultForEapNetwork(r));
642     }
643 }
644