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