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 android.annotation.NonNull; 20 import android.app.ActivityManager; 21 import android.app.AppOpsManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.database.ContentObserver; 25 import android.net.wifi.ScanResult; 26 import android.net.wifi.WifiManager; 27 import android.net.wifi.WifiScanner; 28 import android.os.Handler; 29 import android.os.UserHandle; 30 import android.os.WorkSource; 31 import android.provider.Settings; 32 import android.util.ArrayMap; 33 import android.util.Log; 34 import android.util.Pair; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.server.wifi.util.WifiPermissionsUtil; 38 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Iterator; 42 import java.util.LinkedList; 43 import java.util.List; 44 45 import javax.annotation.concurrent.NotThreadSafe; 46 47 /** 48 * This class manages all scan requests originating from external apps using the 49 * {@link WifiManager#startScan()}. 50 * 51 * This class is responsible for: 52 * a) Enable/Disable scanning based on the request from {@link ActiveModeWarden}. 53 * a) Forwarding scan requests from {@link WifiManager#startScan()} to 54 * {@link WifiScanner#startScan(WifiScanner.ScanSettings, WifiScanner.ScanListener)}. 55 * Will essentially proxy scan requests from WifiService to WifiScanningService. 56 * b) Cache the results of these scan requests and return them when 57 * {@link WifiManager#getScanResults()} is invoked. 58 * c) Will send out the {@link WifiManager#SCAN_RESULTS_AVAILABLE_ACTION} broadcast when new 59 * scan results are available. 60 * d) Throttle scan requests from non-setting apps: 61 * a) Each foreground app can request a max of 62 * {@link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS} scan every 63 * {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS}. 64 * b) Background apps combined can request 1 scan every 65 * {@link #SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}. 66 * Note: This class is not thread-safe. It needs to be invoked from ClientModeImpl thread only. 67 */ 68 @NotThreadSafe 69 public class ScanRequestProxy { 70 private static final String TAG = "WifiScanRequestProxy"; 71 72 @VisibleForTesting 73 public static final int SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS = 120 * 1000; 74 @VisibleForTesting 75 public static final int SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS = 4; 76 @VisibleForTesting 77 public static final int SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS = 30 * 60 * 1000; 78 79 private final Context mContext; 80 private final AppOpsManager mAppOps; 81 private final ActivityManager mActivityManager; 82 private final WifiInjector mWifiInjector; 83 private final WifiConfigManager mWifiConfigManager; 84 private final WifiPermissionsUtil mWifiPermissionsUtil; 85 private final WifiMetrics mWifiMetrics; 86 private final Clock mClock; 87 private final FrameworkFacade mFrameworkFacade; 88 private final ThrottleEnabledSettingObserver mThrottleEnabledSettingObserver; 89 private WifiScanner mWifiScanner; 90 91 // Verbose logging flag. 92 private boolean mVerboseLoggingEnabled = false; 93 // Flag to decide if we need to scan or not. 94 private boolean mScanningEnabled = false; 95 // Flag to decide if we need to scan for hidden networks or not. 96 private boolean mScanningForHiddenNetworksEnabled = false; 97 // Timestamps for the last scan requested by any background app. 98 private long mLastScanTimestampForBgApps = 0; 99 // Timestamps for the list of last few scan requests by each foreground app. 100 // Keys in the map = Pair<Uid, PackageName> of the app. 101 // Values in the map = List of the last few scan request timestamps from the app. 102 private final ArrayMap<Pair<Integer, String>, LinkedList<Long>> mLastScanTimestampsForFgApps = 103 new ArrayMap(); 104 // Scan results cached from the last full single scan request. 105 private final List<ScanResult> mLastScanResults = new ArrayList<>(); 106 // Global scan listener for listening to all scan requests. 107 private class GlobalScanListener implements WifiScanner.ScanListener { 108 @Override onSuccess()109 public void onSuccess() { 110 // Ignore. These will be processed from the scan request listener. 111 } 112 113 @Override onFailure(int reason, String description)114 public void onFailure(int reason, String description) { 115 // Ignore. These will be processed from the scan request listener. 116 } 117 118 @Override onResults(WifiScanner.ScanData[] scanDatas)119 public void onResults(WifiScanner.ScanData[] scanDatas) { 120 if (mVerboseLoggingEnabled) { 121 Log.d(TAG, "Scan results received"); 122 } 123 // For single scans, the array size should always be 1. 124 if (scanDatas.length != 1) { 125 Log.wtf(TAG, "Found more than 1 batch of scan results, Failing..."); 126 sendScanResultBroadcast(false); 127 return; 128 } 129 WifiScanner.ScanData scanData = scanDatas[0]; 130 ScanResult[] scanResults = scanData.getResults(); 131 if (mVerboseLoggingEnabled) { 132 Log.d(TAG, "Received " + scanResults.length + " scan results"); 133 } 134 // Only process full band scan results. 135 if (scanData.getBandScanned() == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) { 136 // Store the last scan results & send out the scan completion broadcast. 137 mLastScanResults.clear(); 138 mLastScanResults.addAll(Arrays.asList(scanResults)); 139 sendScanResultBroadcast(true); 140 } 141 } 142 143 @Override onFullResult(ScanResult fullScanResult)144 public void onFullResult(ScanResult fullScanResult) { 145 // Ignore for single scans. 146 } 147 148 @Override onPeriodChanged(int periodInMs)149 public void onPeriodChanged(int periodInMs) { 150 // Ignore for single scans. 151 } 152 }; 153 154 // Common scan listener for scan requests initiated by this class. 155 private class ScanRequestProxyScanListener implements WifiScanner.ScanListener { 156 @Override onSuccess()157 public void onSuccess() { 158 // Scan request succeeded, wait for results to report to external clients. 159 if (mVerboseLoggingEnabled) { 160 Log.d(TAG, "Scan request succeeded"); 161 } 162 } 163 164 @Override onFailure(int reason, String description)165 public void onFailure(int reason, String description) { 166 Log.e(TAG, "Scan failure received. reason: " + reason + ",description: " + description); 167 sendScanResultBroadcast(false); 168 } 169 170 @Override onResults(WifiScanner.ScanData[] scanDatas)171 public void onResults(WifiScanner.ScanData[] scanDatas) { 172 // Ignore. These will be processed from the global listener. 173 } 174 175 @Override onFullResult(ScanResult fullScanResult)176 public void onFullResult(ScanResult fullScanResult) { 177 // Ignore for single scans. 178 } 179 180 @Override onPeriodChanged(int periodInMs)181 public void onPeriodChanged(int periodInMs) { 182 // Ignore for single scans. 183 } 184 }; 185 186 /** 187 * Observer for scan throttle enable settings changes. 188 * This is enabled by default. Will be toggled off via adb command or a developer settings 189 * toggle by the user to disable all scan throttling. 190 */ 191 private class ThrottleEnabledSettingObserver extends ContentObserver { 192 private boolean mThrottleEnabled = true; 193 ThrottleEnabledSettingObserver(Handler handler)194 ThrottleEnabledSettingObserver(Handler handler) { 195 super(handler); 196 } 197 198 /** 199 * Register for any changes to the scan throttle setting. 200 */ initialize()201 public void initialize() { 202 mFrameworkFacade.registerContentObserver(mContext, 203 Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_THROTTLE_ENABLED), 204 true, this); 205 mThrottleEnabled = getValue(); 206 if (mVerboseLoggingEnabled) { 207 Log.v(TAG, "Scan throttle enabled " + mThrottleEnabled); 208 } 209 } 210 211 /** 212 * Check if throttling is enabled or not. 213 * 214 * @return true if throttling is enabled, false otherwise. 215 */ isEnabled()216 public boolean isEnabled() { 217 return mThrottleEnabled; 218 } 219 220 @Override onChange(boolean selfChange)221 public void onChange(boolean selfChange) { 222 super.onChange(selfChange); 223 mThrottleEnabled = getValue(); 224 Log.i(TAG, "Scan throttle enabled " + mThrottleEnabled); 225 } 226 getValue()227 private boolean getValue() { 228 return mFrameworkFacade.getIntegerSetting(mContext, 229 Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1; 230 } 231 } 232 ScanRequestProxy(Context context, AppOpsManager appOpsManager, ActivityManager activityManager, WifiInjector wifiInjector, WifiConfigManager configManager, WifiPermissionsUtil wifiPermissionUtil, WifiMetrics wifiMetrics, Clock clock, FrameworkFacade frameworkFacade, Handler handler)233 ScanRequestProxy(Context context, AppOpsManager appOpsManager, ActivityManager activityManager, 234 WifiInjector wifiInjector, WifiConfigManager configManager, 235 WifiPermissionsUtil wifiPermissionUtil, WifiMetrics wifiMetrics, Clock clock, 236 FrameworkFacade frameworkFacade, Handler handler) { 237 mContext = context; 238 mAppOps = appOpsManager; 239 mActivityManager = activityManager; 240 mWifiInjector = wifiInjector; 241 mWifiConfigManager = configManager; 242 mWifiPermissionsUtil = wifiPermissionUtil; 243 mWifiMetrics = wifiMetrics; 244 mClock = clock; 245 mFrameworkFacade = frameworkFacade; 246 mThrottleEnabledSettingObserver = new ThrottleEnabledSettingObserver(handler); 247 } 248 249 /** 250 * Enable verbose logging. 251 */ enableVerboseLogging(int verbose)252 public void enableVerboseLogging(int verbose) { 253 mVerboseLoggingEnabled = (verbose > 0); 254 } 255 256 /** 257 * Helper method to populate WifiScanner handle. This is done lazily because 258 * WifiScanningService is started after WifiService. 259 */ retrieveWifiScannerIfNecessary()260 private boolean retrieveWifiScannerIfNecessary() { 261 if (mWifiScanner == null) { 262 mWifiScanner = mWifiInjector.getWifiScanner(); 263 // Start listening for throttle settings change after we retrieve scanner instance. 264 mThrottleEnabledSettingObserver.initialize(); 265 // Register the global scan listener. 266 if (mWifiScanner != null) { 267 mWifiScanner.registerScanListener(new GlobalScanListener()); 268 } 269 } 270 return mWifiScanner != null; 271 } 272 273 /** 274 * Method that lets public apps know that scans are available. 275 * 276 * @param context Context to use for the notification 277 * @param available boolean indicating if scanning is available 278 */ sendScanAvailableBroadcast(Context context, boolean available)279 private void sendScanAvailableBroadcast(Context context, boolean available) { 280 Log.d(TAG, "Sending scan available broadcast: " + available); 281 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); 282 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 283 if (available) { 284 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_ENABLED); 285 } else { 286 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED); 287 } 288 context.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 289 } 290 enableScanningInternal(boolean enable)291 private void enableScanningInternal(boolean enable) { 292 if (!retrieveWifiScannerIfNecessary()) { 293 Log.e(TAG, "Failed to retrieve wifiscanner"); 294 return; 295 } 296 mWifiScanner.setScanningEnabled(enable); 297 sendScanAvailableBroadcast(mContext, enable); 298 clearScanResults(); 299 Log.i(TAG, "Scanning is " + (enable ? "enabled" : "disabled")); 300 } 301 302 /** 303 * Enable/disable scanning. 304 * 305 * @param enable true to enable, false to disable. 306 * @param enableScanningForHiddenNetworks true to enable scanning for hidden networks, 307 * false to disable. 308 */ enableScanning(boolean enable, boolean enableScanningForHiddenNetworks)309 public void enableScanning(boolean enable, boolean enableScanningForHiddenNetworks) { 310 if (enable) { 311 enableScanningInternal(true); 312 mScanningForHiddenNetworksEnabled = enableScanningForHiddenNetworks; 313 Log.i(TAG, "Scanning for hidden networks is " 314 + (enableScanningForHiddenNetworks ? "enabled" : "disabled")); 315 } else { 316 enableScanningInternal(false); 317 } 318 mScanningEnabled = enable; 319 } 320 321 322 /** 323 * Helper method to send the scan request status broadcast. 324 */ sendScanResultBroadcast(boolean scanSucceeded)325 private void sendScanResultBroadcast(boolean scanSucceeded) { 326 Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 327 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 328 intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded); 329 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 330 } 331 332 /** 333 * Helper method to send the scan request failure broadcast to specified package. 334 */ sendScanResultFailureBroadcastToPackage(String packageName)335 private void sendScanResultFailureBroadcastToPackage(String packageName) { 336 Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 337 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 338 intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); 339 intent.setPackage(packageName); 340 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 341 } 342 trimPastScanRequestTimesForForegroundApp( List<Long> scanRequestTimestamps, long currentTimeMillis)343 private void trimPastScanRequestTimesForForegroundApp( 344 List<Long> scanRequestTimestamps, long currentTimeMillis) { 345 Iterator<Long> timestampsIter = scanRequestTimestamps.iterator(); 346 while (timestampsIter.hasNext()) { 347 Long scanRequestTimeMillis = timestampsIter.next(); 348 if ((currentTimeMillis - scanRequestTimeMillis) 349 > SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS) { 350 timestampsIter.remove(); 351 } else { 352 // This list is sorted by timestamps, so we can skip any more checks 353 break; 354 } 355 } 356 } 357 getOrCreateScanRequestTimestampsForForegroundApp( int callingUid, String packageName)358 private LinkedList<Long> getOrCreateScanRequestTimestampsForForegroundApp( 359 int callingUid, String packageName) { 360 Pair<Integer, String> uidAndPackageNamePair = Pair.create(callingUid, packageName); 361 LinkedList<Long> scanRequestTimestamps = 362 mLastScanTimestampsForFgApps.get(uidAndPackageNamePair); 363 if (scanRequestTimestamps == null) { 364 scanRequestTimestamps = new LinkedList<>(); 365 mLastScanTimestampsForFgApps.put(uidAndPackageNamePair, scanRequestTimestamps); 366 } 367 return scanRequestTimestamps; 368 } 369 370 /** 371 * Checks if the scan request from the app (specified by packageName) needs 372 * to be throttled. 373 * The throttle limit allows a max of {@link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS} 374 * in {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS} window. 375 */ shouldScanRequestBeThrottledForForegroundApp( int callingUid, String packageName)376 private boolean shouldScanRequestBeThrottledForForegroundApp( 377 int callingUid, String packageName) { 378 LinkedList<Long> scanRequestTimestamps = 379 getOrCreateScanRequestTimestampsForForegroundApp(callingUid, packageName); 380 long currentTimeMillis = mClock.getElapsedSinceBootMillis(); 381 // First evict old entries from the list. 382 trimPastScanRequestTimesForForegroundApp(scanRequestTimestamps, currentTimeMillis); 383 if (scanRequestTimestamps.size() >= SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS) { 384 return true; 385 } 386 // Proceed with the scan request and record the time. 387 scanRequestTimestamps.addLast(currentTimeMillis); 388 return false; 389 } 390 391 /** 392 * Checks if the scan request from a background app needs to be throttled. 393 */ shouldScanRequestBeThrottledForBackgroundApp()394 private boolean shouldScanRequestBeThrottledForBackgroundApp() { 395 long lastScanMs = mLastScanTimestampForBgApps; 396 long elapsedRealtime = mClock.getElapsedSinceBootMillis(); 397 if (lastScanMs != 0 398 && (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS) { 399 return true; 400 } 401 // Proceed with the scan request and record the time. 402 mLastScanTimestampForBgApps = elapsedRealtime; 403 return false; 404 } 405 406 /** 407 * Check if the request comes from background app. 408 */ isRequestFromBackground(int callingUid, String packageName)409 private boolean isRequestFromBackground(int callingUid, String packageName) { 410 mAppOps.checkPackage(callingUid, packageName); 411 try { 412 return mActivityManager.getPackageImportance(packageName) 413 > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 414 } catch (SecurityException e) { 415 Log.e(TAG, "Failed to check the app state", e); 416 return true; 417 } 418 } 419 420 /** 421 * Checks if the scan request from the app (specified by callingUid & packageName) needs 422 * to be throttled. 423 */ shouldScanRequestBeThrottledForApp(int callingUid, String packageName)424 private boolean shouldScanRequestBeThrottledForApp(int callingUid, String packageName) { 425 boolean isThrottled; 426 if (isRequestFromBackground(callingUid, packageName)) { 427 isThrottled = shouldScanRequestBeThrottledForBackgroundApp(); 428 if (isThrottled) { 429 if (mVerboseLoggingEnabled) { 430 Log.v(TAG, "Background scan app request [" + callingUid + ", " 431 + packageName + "]"); 432 } 433 mWifiMetrics.incrementExternalBackgroundAppOneshotScanRequestsThrottledCount(); 434 } 435 } else { 436 isThrottled = shouldScanRequestBeThrottledForForegroundApp(callingUid, packageName); 437 if (isThrottled) { 438 if (mVerboseLoggingEnabled) { 439 Log.v(TAG, "Foreground scan app request [" + callingUid + ", " 440 + packageName + "]"); 441 } 442 mWifiMetrics.incrementExternalForegroundAppOneshotScanRequestsThrottledCount(); 443 } 444 } 445 mWifiMetrics.incrementExternalAppOneshotScanRequestsCount(); 446 return isThrottled; 447 } 448 449 /** 450 * Initiate a wifi scan. 451 * 452 * @param callingUid The uid initiating the wifi scan. Blame will be given to this uid. 453 * @return true if the scan request was placed or a scan is already ongoing, false otherwise. 454 */ startScan(int callingUid, String packageName)455 public boolean startScan(int callingUid, String packageName) { 456 if (!retrieveWifiScannerIfNecessary()) { 457 Log.e(TAG, "Failed to retrieve wifiscanner"); 458 sendScanResultFailureBroadcastToPackage(packageName); 459 return false; 460 } 461 boolean fromSettingsOrSetupWizard = 462 mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid) 463 || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid); 464 // Check and throttle scan request unless, 465 // a) App has either NETWORK_SETTINGS or NETWORK_SETUP_WIZARD permission. 466 // b) Throttling has been disabled by user. 467 if (!fromSettingsOrSetupWizard && mThrottleEnabledSettingObserver.isEnabled() 468 && shouldScanRequestBeThrottledForApp(callingUid, packageName)) { 469 Log.i(TAG, "Scan request from " + packageName + " throttled"); 470 sendScanResultFailureBroadcastToPackage(packageName); 471 return false; 472 } 473 // Create a worksource using the caller's UID. 474 WorkSource workSource = new WorkSource(callingUid, packageName); 475 476 // Create the scan settings. 477 WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings(); 478 // Scan requests from apps with network settings will be of high accuracy type. 479 if (fromSettingsOrSetupWizard) { 480 settings.type = WifiScanner.TYPE_HIGH_ACCURACY; 481 } 482 // always do full scans 483 settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS; 484 settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN 485 | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 486 if (mScanningForHiddenNetworksEnabled) { 487 // retrieve the list of hidden network SSIDs from saved network to scan for, if enabled. 488 List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList = 489 new ArrayList<>(mWifiConfigManager.retrieveHiddenNetworkList()); 490 // retrieve the list of hidden network SSIDs from Network suggestion to scan for. 491 hiddenNetworkList.addAll( 492 mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList()); 493 settings.hiddenNetworks = hiddenNetworkList.toArray( 494 new WifiScanner.ScanSettings.HiddenNetwork[0]); 495 } 496 mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource); 497 return true; 498 } 499 500 /** 501 * Return the results of the most recent access point scan, in the form of 502 * a list of {@link ScanResult} objects. 503 * @return the list of results 504 */ getScanResults()505 public List<ScanResult> getScanResults() { 506 return mLastScanResults; 507 } 508 509 /** 510 * Clear the stored scan results. 511 */ clearScanResults()512 private void clearScanResults() { 513 mLastScanResults.clear(); 514 mLastScanTimestampForBgApps = 0; 515 mLastScanTimestampsForFgApps.clear(); 516 } 517 518 /** 519 * Clear any scan timestamps being stored for the app. 520 * 521 * @param uid Uid of the package. 522 * @param packageName Name of the package. 523 */ clearScanRequestTimestampsForApp(@onNull String packageName, int uid)524 public void clearScanRequestTimestampsForApp(@NonNull String packageName, int uid) { 525 if (mVerboseLoggingEnabled) { 526 Log.v(TAG, "Clearing scan request timestamps for uid=" + uid + ", packageName=" 527 + packageName); 528 } 529 mLastScanTimestampsForFgApps.remove(Pair.create(uid, packageName)); 530 } 531 } 532