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