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