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