659 lines
28 KiB
Java
659 lines
28 KiB
Java
|
/*
|
||
|
* Copyright (C) 2018 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package com.android.server.wifi;
|
||
|
|
||
|
import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_SCAN_THROTTLE_ENABLED;
|
||
|
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.app.ActivityManager;
|
||
|
import android.app.AppOpsManager;
|
||
|
import android.content.Context;
|
||
|
import android.content.Intent;
|
||
|
import android.net.wifi.IScanResultsCallback;
|
||
|
import android.net.wifi.ScanResult;
|
||
|
import android.net.wifi.WifiManager;
|
||
|
import android.net.wifi.WifiScanner;
|
||
|
import android.net.wifi.util.ScanResultUtil;
|
||
|
import android.os.Handler;
|
||
|
import android.os.RemoteCallbackList;
|
||
|
import android.os.RemoteException;
|
||
|
import android.os.UserHandle;
|
||
|
import android.os.WorkSource;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.ArrayMap;
|
||
|
import android.util.Log;
|
||
|
import android.util.Pair;
|
||
|
|
||
|
import com.android.internal.annotations.VisibleForTesting;
|
||
|
import com.android.modules.utils.HandlerExecutor;
|
||
|
import com.android.modules.utils.build.SdkLevel;
|
||
|
import com.android.server.wifi.util.WifiPermissionsUtil;
|
||
|
import com.android.wifi.resources.R;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Arrays;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.LinkedList;
|
||
|
import java.util.List;
|
||
|
import java.util.Map;
|
||
|
|
||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||
|
|
||
|
/**
|
||
|
* This class manages all scan requests originating from external apps using the
|
||
|
* {@link WifiManager#startScan()}.
|
||
|
*
|
||
|
* This class is responsible for:
|
||
|
* a) Enable/Disable scanning based on the request from {@link ActiveModeWarden}.
|
||
|
* a) Forwarding scan requests from {@link WifiManager#startScan()} to
|
||
|
* {@link WifiScanner#startScan(WifiScanner.ScanSettings, WifiScanner.ScanListener)}.
|
||
|
* Will essentially proxy scan requests from WifiService to WifiScanningService.
|
||
|
* b) Cache the results of these scan requests and return them when
|
||
|
* {@link WifiManager#getScanResults()} is invoked.
|
||
|
* c) Will send out the {@link WifiManager#SCAN_RESULTS_AVAILABLE_ACTION} broadcast when new
|
||
|
* scan results are available.
|
||
|
* d) Throttle scan requests from non-setting apps:
|
||
|
* a) Each foreground app can request a max of
|
||
|
* {@link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS} scan every
|
||
|
* {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS}.
|
||
|
* b) Background apps combined can request 1 scan every
|
||
|
* {@link #SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS}.
|
||
|
* Note: This class is not thread-safe. It needs to be invoked from the main Wifi thread only.
|
||
|
*/
|
||
|
@NotThreadSafe
|
||
|
public class ScanRequestProxy {
|
||
|
private static final String TAG = "WifiScanRequestProxy";
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public static final int SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS = 120 * 1000;
|
||
|
@VisibleForTesting
|
||
|
public static final int SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS = 4;
|
||
|
@VisibleForTesting
|
||
|
public static final int SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS = 30 * 60 * 1000;
|
||
|
|
||
|
private final Context mContext;
|
||
|
private final Handler mHandler;
|
||
|
private final AppOpsManager mAppOps;
|
||
|
private final ActivityManager mActivityManager;
|
||
|
private final WifiInjector mWifiInjector;
|
||
|
private final WifiConfigManager mWifiConfigManager;
|
||
|
private final WifiPermissionsUtil mWifiPermissionsUtil;
|
||
|
private final WifiMetrics mWifiMetrics;
|
||
|
private final Clock mClock;
|
||
|
private final WifiSettingsConfigStore mSettingsConfigStore;
|
||
|
private WifiScanner mWifiScanner;
|
||
|
|
||
|
// Verbose logging flag.
|
||
|
private boolean mVerboseLoggingEnabled = false;
|
||
|
private boolean mThrottleEnabled = true;
|
||
|
// Flag to decide if we need to scan or not.
|
||
|
private boolean mScanningEnabled = false;
|
||
|
// Flag to decide if we need to scan for hidden networks or not.
|
||
|
private boolean mScanningForHiddenNetworksEnabled = false;
|
||
|
// Timestamps for the last scan requested by any background app.
|
||
|
private long mLastScanTimestampForBgApps = 0;
|
||
|
// Timestamps for the list of last few scan requests by each foreground app.
|
||
|
// Keys in the map = Pair<Uid, PackageName> of the app.
|
||
|
// Values in the map = List of the last few scan request timestamps from the app.
|
||
|
private final ArrayMap<Pair<Integer, String>, LinkedList<Long>> mLastScanTimestampsForFgApps =
|
||
|
new ArrayMap();
|
||
|
// Scan results cached from the last full single scan request.
|
||
|
// Stored as a map of bssid -> ScanResult to allow other clients to perform ScanResult lookup
|
||
|
// for bssid more efficiently.
|
||
|
private final Map<String, ScanResult> mLastScanResultsMap = new HashMap<>();
|
||
|
// external ScanResultCallback tracker
|
||
|
private final RemoteCallbackList<IScanResultsCallback> mRegisteredScanResultsCallbacks;
|
||
|
// Global scan listener for listening to all scan requests.
|
||
|
private class GlobalScanListener implements WifiScanner.ScanListener {
|
||
|
@Override
|
||
|
public void onSuccess() {
|
||
|
// Ignore. These will be processed from the scan request listener.
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onFailure(int reason, String description) {
|
||
|
// Ignore. These will be processed from the scan request listener.
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onResults(WifiScanner.ScanData[] scanDatas) {
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.d(TAG, "Scan results received");
|
||
|
}
|
||
|
// For single scans, the array size should always be 1.
|
||
|
if (scanDatas.length != 1) {
|
||
|
Log.wtf(TAG, "Found more than 1 batch of scan results, Failing...");
|
||
|
sendScanResultBroadcast(false);
|
||
|
return;
|
||
|
}
|
||
|
WifiScanner.ScanData scanData = scanDatas[0];
|
||
|
ScanResult[] scanResults = scanData.getResults();
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.d(TAG, "Received " + scanResults.length + " scan results");
|
||
|
}
|
||
|
// Only process full band scan results.
|
||
|
if (WifiScanner.isFullBandScan(scanData.getScannedBandsInternal(), false)) {
|
||
|
// Store the last scan results & send out the scan completion broadcast.
|
||
|
mLastScanResultsMap.clear();
|
||
|
Arrays.stream(scanResults).forEach(s -> mLastScanResultsMap.put(s.BSSID, s));
|
||
|
sendScanResultBroadcast(true);
|
||
|
sendScanResultsAvailableToCallbacks();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onFullResult(ScanResult fullScanResult) {
|
||
|
// Ignore for single scans.
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onPeriodChanged(int periodInMs) {
|
||
|
// Ignore for single scans.
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Common scan listener for scan requests initiated by this class.
|
||
|
private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
|
||
|
@Override
|
||
|
public void onSuccess() {
|
||
|
// Scan request succeeded, wait for results to report to external clients.
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.d(TAG, "Scan request succeeded");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onFailure(int reason, String description) {
|
||
|
if (android.os.SystemProperties.getInt("persist.sys.cloud.wifi.enable", 1) != 1) {
|
||
|
Log.e(TAG, "Scan failure received. reason: " + reason + ",description: " + description);
|
||
|
sendScanResultBroadcast(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onResults(WifiScanner.ScanData[] scanDatas) {
|
||
|
// Ignore. These will be processed from the global listener.
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onFullResult(ScanResult fullScanResult) {
|
||
|
// Ignore for single scans.
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onPeriodChanged(int periodInMs) {
|
||
|
// Ignore for single scans.
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ScanRequestProxy(Context context, AppOpsManager appOpsManager, ActivityManager activityManager,
|
||
|
WifiInjector wifiInjector, WifiConfigManager configManager,
|
||
|
WifiPermissionsUtil wifiPermissionUtil, WifiMetrics wifiMetrics, Clock clock,
|
||
|
Handler handler, WifiSettingsConfigStore settingsConfigStore) {
|
||
|
mContext = context;
|
||
|
mHandler = handler;
|
||
|
mAppOps = appOpsManager;
|
||
|
mActivityManager = activityManager;
|
||
|
mWifiInjector = wifiInjector;
|
||
|
mWifiConfigManager = configManager;
|
||
|
mWifiPermissionsUtil = wifiPermissionUtil;
|
||
|
mWifiMetrics = wifiMetrics;
|
||
|
mClock = clock;
|
||
|
mSettingsConfigStore = settingsConfigStore;
|
||
|
mRegisteredScanResultsCallbacks = new RemoteCallbackList<>();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enable verbose logging.
|
||
|
*/
|
||
|
public void enableVerboseLogging(boolean verboseEnabled) {
|
||
|
mVerboseLoggingEnabled = verboseEnabled;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Helper method to populate WifiScanner handle. This is done lazily because
|
||
|
* WifiScanningService is started after WifiService.
|
||
|
*/
|
||
|
private boolean retrieveWifiScannerIfNecessary() {
|
||
|
if (mWifiScanner == null) {
|
||
|
mWifiScanner = mWifiInjector.getWifiScanner();
|
||
|
// Start listening for throttle settings change after we retrieve scanner instance.
|
||
|
mThrottleEnabled = mSettingsConfigStore.get(WIFI_SCAN_THROTTLE_ENABLED);
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.v(TAG, "Scan throttle enabled " + mThrottleEnabled);
|
||
|
}
|
||
|
// Register the global scan listener.
|
||
|
if (mWifiScanner != null) {
|
||
|
mWifiScanner.registerScanListener(
|
||
|
new HandlerExecutor(mHandler), new GlobalScanListener());
|
||
|
}
|
||
|
}
|
||
|
return mWifiScanner != null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Method that lets public apps know that scans are available.
|
||
|
*
|
||
|
* @param context Context to use for the notification
|
||
|
* @param available boolean indicating if scanning is available
|
||
|
*/
|
||
|
private void sendScanAvailableBroadcast(Context context, boolean available) {
|
||
|
Log.d(TAG, "Sending scan available broadcast: " + available);
|
||
|
final Intent intent = new Intent(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED);
|
||
|
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||
|
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, available);
|
||
|
context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
|
||
|
}
|
||
|
|
||
|
private void enableScanningInternal(boolean enable) {
|
||
|
if (!retrieveWifiScannerIfNecessary()) {
|
||
|
Log.e(TAG, "Failed to retrieve wifiscanner");
|
||
|
return;
|
||
|
}
|
||
|
mWifiScanner.setScanningEnabled(enable);
|
||
|
sendScanAvailableBroadcast(mContext, enable);
|
||
|
if (!enable) clearScanResults();
|
||
|
Log.i(TAG, "Scanning is " + (enable ? "enabled" : "disabled"));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enable/disable scanning.
|
||
|
*
|
||
|
* @param enable true to enable, false to disable.
|
||
|
* @param enableScanningForHiddenNetworks true to enable scanning for hidden networks,
|
||
|
* false to disable.
|
||
|
*/
|
||
|
public void enableScanning(boolean enable, boolean enableScanningForHiddenNetworks) {
|
||
|
if (enable) {
|
||
|
enableScanningInternal(true);
|
||
|
mScanningForHiddenNetworksEnabled = enableScanningForHiddenNetworks;
|
||
|
Log.i(TAG, "Scanning for hidden networks is "
|
||
|
+ (enableScanningForHiddenNetworks ? "enabled" : "disabled"));
|
||
|
} else {
|
||
|
enableScanningInternal(false);
|
||
|
}
|
||
|
mScanningEnabled = enable;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Helper method to send the scan request status broadcast.
|
||
|
*/
|
||
|
private void sendScanResultBroadcast(boolean scanSucceeded) {
|
||
|
Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
|
||
|
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||
|
intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
|
||
|
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Helper method to send the scan request failure broadcast to specified package.
|
||
|
*/
|
||
|
private void sendScanResultFailureBroadcastToPackage(String packageName) {
|
||
|
Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
|
||
|
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||
|
intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
|
||
|
intent.setPackage(packageName);
|
||
|
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
|
||
|
}
|
||
|
|
||
|
private void trimPastScanRequestTimesForForegroundApp(
|
||
|
List<Long> scanRequestTimestamps, long currentTimeMillis) {
|
||
|
Iterator<Long> timestampsIter = scanRequestTimestamps.iterator();
|
||
|
while (timestampsIter.hasNext()) {
|
||
|
Long scanRequestTimeMillis = timestampsIter.next();
|
||
|
if ((currentTimeMillis - scanRequestTimeMillis)
|
||
|
> SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS) {
|
||
|
timestampsIter.remove();
|
||
|
} else {
|
||
|
// This list is sorted by timestamps, so we can skip any more checks
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private LinkedList<Long> getOrCreateScanRequestTimestampsForForegroundApp(
|
||
|
int callingUid, String packageName) {
|
||
|
Pair<Integer, String> uidAndPackageNamePair = Pair.create(callingUid, packageName);
|
||
|
LinkedList<Long> scanRequestTimestamps =
|
||
|
mLastScanTimestampsForFgApps.get(uidAndPackageNamePair);
|
||
|
if (scanRequestTimestamps == null) {
|
||
|
scanRequestTimestamps = new LinkedList<>();
|
||
|
mLastScanTimestampsForFgApps.put(uidAndPackageNamePair, scanRequestTimestamps);
|
||
|
}
|
||
|
return scanRequestTimestamps;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if the scan request from the app (specified by packageName) needs
|
||
|
* to be throttled.
|
||
|
* The throttle limit allows a max of {@link #SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS}
|
||
|
* in {@link #SCAN_REQUEST_THROTTLE_TIME_WINDOW_FG_APPS_MS} window.
|
||
|
*/
|
||
|
private boolean shouldScanRequestBeThrottledForForegroundApp(
|
||
|
int callingUid, String packageName) {
|
||
|
if (isPackageNameInExceptionList(packageName, true)) {
|
||
|
return false;
|
||
|
}
|
||
|
LinkedList<Long> scanRequestTimestamps =
|
||
|
getOrCreateScanRequestTimestampsForForegroundApp(callingUid, packageName);
|
||
|
long currentTimeMillis = mClock.getElapsedSinceBootMillis();
|
||
|
// First evict old entries from the list.
|
||
|
trimPastScanRequestTimesForForegroundApp(scanRequestTimestamps, currentTimeMillis);
|
||
|
if (scanRequestTimestamps.size() >= SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS) {
|
||
|
return true;
|
||
|
}
|
||
|
// Proceed with the scan request and record the time.
|
||
|
scanRequestTimestamps.addLast(currentTimeMillis);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private boolean isPackageNameInExceptionList(String packageName, boolean isForeground) {
|
||
|
if (packageName == null) {
|
||
|
return false;
|
||
|
}
|
||
|
String[] exceptionList = mContext.getResources().getStringArray(isForeground
|
||
|
? R.array.config_wifiForegroundScanThrottleExceptionList
|
||
|
: R.array.config_wifiBackgroundScanThrottleExceptionList);
|
||
|
if (exceptionList == null) {
|
||
|
return false;
|
||
|
}
|
||
|
for (String name : exceptionList) {
|
||
|
if (TextUtils.equals(packageName, name)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if the scan request from a background app needs to be throttled.
|
||
|
*/
|
||
|
private boolean shouldScanRequestBeThrottledForBackgroundApp(String packageName) {
|
||
|
if (isPackageNameInExceptionList(packageName, false)) {
|
||
|
return false;
|
||
|
}
|
||
|
long lastScanMs = mLastScanTimestampForBgApps;
|
||
|
long elapsedRealtime = mClock.getElapsedSinceBootMillis();
|
||
|
if (lastScanMs != 0
|
||
|
&& (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS) {
|
||
|
return true;
|
||
|
}
|
||
|
// Proceed with the scan request and record the time.
|
||
|
mLastScanTimestampForBgApps = elapsedRealtime;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Safely retrieve package importance.
|
||
|
*/
|
||
|
private int getPackageImportance(int callingUid, String packageName) {
|
||
|
mAppOps.checkPackage(callingUid, packageName);
|
||
|
try {
|
||
|
return mActivityManager.getPackageImportance(packageName);
|
||
|
} catch (SecurityException e) {
|
||
|
Log.e(TAG, "Failed to check the app state", e);
|
||
|
return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if the scan request from the app (specified by callingUid & packageName) needs
|
||
|
* to be throttled.
|
||
|
*/
|
||
|
private boolean shouldScanRequestBeThrottledForApp(int callingUid, String packageName,
|
||
|
int packageImportance) {
|
||
|
boolean isThrottled;
|
||
|
if (packageImportance
|
||
|
> ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
|
||
|
isThrottled = shouldScanRequestBeThrottledForBackgroundApp(packageName);
|
||
|
if (isThrottled) {
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.v(TAG, "Background scan app request [" + callingUid + ", "
|
||
|
+ packageName + "]");
|
||
|
}
|
||
|
mWifiMetrics.incrementExternalBackgroundAppOneshotScanRequestsThrottledCount();
|
||
|
}
|
||
|
} else {
|
||
|
isThrottled = shouldScanRequestBeThrottledForForegroundApp(callingUid, packageName);
|
||
|
if (isThrottled) {
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.v(TAG, "Foreground scan app request [" + callingUid + ", "
|
||
|
+ packageName + "]");
|
||
|
}
|
||
|
mWifiMetrics.incrementExternalForegroundAppOneshotScanRequestsThrottledCount();
|
||
|
}
|
||
|
}
|
||
|
mWifiMetrics.incrementExternalAppOneshotScanRequestsCount();
|
||
|
return isThrottled;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initiate a wifi scan.
|
||
|
*
|
||
|
* @param callingUid The uid initiating the wifi scan. Blame will be given to this uid.
|
||
|
* @return true if the scan request was placed or a scan is already ongoing, false otherwise.
|
||
|
*/
|
||
|
public boolean startScan(int callingUid, String packageName) {
|
||
|
if (!mScanningEnabled || !retrieveWifiScannerIfNecessary()) {
|
||
|
Log.e(TAG, "Failed to retrieve wifiscanner");
|
||
|
sendScanResultFailureBroadcastToPackage(packageName);
|
||
|
return false;
|
||
|
}
|
||
|
boolean fromSettingsOrSetupWizard =
|
||
|
mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
|
||
|
|| mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
|
||
|
// Check and throttle scan request unless,
|
||
|
// a) App has either NETWORK_SETTINGS or NETWORK_SETUP_WIZARD permission.
|
||
|
// b) Throttling has been disabled by user.
|
||
|
int packageImportance = getPackageImportance(callingUid, packageName);
|
||
|
if (!fromSettingsOrSetupWizard && mThrottleEnabled
|
||
|
&& shouldScanRequestBeThrottledForApp(callingUid, packageName, packageImportance)) {
|
||
|
Log.i(TAG, "Scan request from " + packageName + " throttled");
|
||
|
sendScanResultFailureBroadcastToPackage(packageName);
|
||
|
return false;
|
||
|
}
|
||
|
// Create a worksource using the caller's UID.
|
||
|
WorkSource workSource = new WorkSource(callingUid, packageName);
|
||
|
mWifiMetrics.getScanMetrics().setWorkSource(workSource);
|
||
|
mWifiMetrics.getScanMetrics().setImportance(packageImportance);
|
||
|
|
||
|
// Create the scan settings.
|
||
|
WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
|
||
|
// Scan requests from apps with network settings will be of high accuracy type.
|
||
|
if (fromSettingsOrSetupWizard) {
|
||
|
settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
|
||
|
} else {
|
||
|
if (SdkLevel.isAtLeastS()) {
|
||
|
// since the scan request is from a normal app, do not scan all 6Ghz channels.
|
||
|
settings.set6GhzPscOnlyEnabled(true);
|
||
|
}
|
||
|
}
|
||
|
settings.band = WifiScanner.WIFI_BAND_ALL;
|
||
|
settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
|
||
|
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
|
||
|
if (mScanningForHiddenNetworksEnabled) {
|
||
|
settings.hiddenNetworks.clear();
|
||
|
// retrieve the list of hidden network SSIDs from saved network to scan for, if enabled.
|
||
|
settings.hiddenNetworks.addAll(mWifiConfigManager.retrieveHiddenNetworkList(false));
|
||
|
// retrieve the list of hidden network SSIDs from Network suggestion to scan for.
|
||
|
settings.hiddenNetworks.addAll(
|
||
|
mWifiInjector.getWifiNetworkSuggestionsManager()
|
||
|
.retrieveHiddenNetworkList(false));
|
||
|
}
|
||
|
mWifiScanner.startScan(settings, new HandlerExecutor(mHandler),
|
||
|
new ScanRequestProxyScanListener(), workSource);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the results of the most recent access point scan, in the form of
|
||
|
* a list of {@link ScanResult} objects.
|
||
|
* @return the list of results
|
||
|
*/
|
||
|
public List<ScanResult> getScanResults() {
|
||
|
// return a copy to prevent external modification
|
||
|
return new ArrayList<>(mLastScanResultsMap.values());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the ScanResult from the most recent access point scan for the provided bssid.
|
||
|
*
|
||
|
* @param bssid BSSID as string {@link ScanResult#BSSID}.
|
||
|
* @return ScanResult for the corresponding bssid if found, null otherwise.
|
||
|
*/
|
||
|
public @Nullable ScanResult getScanResult(@NonNull String bssid) {
|
||
|
ScanResult scanResult = mLastScanResultsMap.get(bssid);
|
||
|
if (scanResult == null) return null;
|
||
|
// return a copy to prevent external modification
|
||
|
return new ScanResult(scanResult);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Clear the stored scan results.
|
||
|
*/
|
||
|
private void clearScanResults() {
|
||
|
mLastScanResultsMap.clear();
|
||
|
mLastScanTimestampForBgApps = 0;
|
||
|
mLastScanTimestampsForFgApps.clear();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear any scan timestamps being stored for the app.
|
||
|
*
|
||
|
* @param uid Uid of the package.
|
||
|
* @param packageName Name of the package.
|
||
|
*/
|
||
|
public void clearScanRequestTimestampsForApp(@NonNull String packageName, int uid) {
|
||
|
if (mVerboseLoggingEnabled) {
|
||
|
Log.v(TAG, "Clearing scan request timestamps for uid=" + uid + ", packageName="
|
||
|
+ packageName);
|
||
|
}
|
||
|
mLastScanTimestampsForFgApps.remove(Pair.create(uid, packageName));
|
||
|
}
|
||
|
|
||
|
private void sendScanResultsAvailableToCallbacks() {
|
||
|
int itemCount = mRegisteredScanResultsCallbacks.beginBroadcast();
|
||
|
for (int i = 0; i < itemCount; i++) {
|
||
|
try {
|
||
|
mRegisteredScanResultsCallbacks.getBroadcastItem(i).onScanResultsAvailable();
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "onScanResultsAvailable: remote exception -- " + e);
|
||
|
}
|
||
|
}
|
||
|
mRegisteredScanResultsCallbacks.finishBroadcast();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Register a callback on scan event
|
||
|
* @param callback IScanResultListener instance to add.
|
||
|
* @return true if succeed otherwise false.
|
||
|
*/
|
||
|
public boolean registerScanResultsCallback(IScanResultsCallback callback) {
|
||
|
return mRegisteredScanResultsCallbacks.register(callback);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unregister a callback on scan event
|
||
|
* @param callback IScanResultListener instance to add.
|
||
|
*/
|
||
|
public void unregisterScanResultsCallback(IScanResultsCallback callback) {
|
||
|
mRegisteredScanResultsCallbacks.unregister(callback);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enable/disable wifi scan throttling from 3rd party apps.
|
||
|
*/
|
||
|
public void setScanThrottleEnabled(boolean enable) {
|
||
|
mThrottleEnabled = enable;
|
||
|
mSettingsConfigStore.put(WIFI_SCAN_THROTTLE_ENABLED, enable);
|
||
|
|
||
|
// reset internal counters when enabling/disabling throttling
|
||
|
mLastScanTimestampsForFgApps.clear();
|
||
|
mLastScanTimestampForBgApps = 0;
|
||
|
Log.i(TAG, "Scan throttle enabled " + mThrottleEnabled);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the persisted Wi-Fi scan throttle state, set by
|
||
|
* {@link #setScanThrottleEnabled(boolean)}.
|
||
|
*/
|
||
|
public boolean isScanThrottleEnabled() {
|
||
|
return mThrottleEnabled;
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are WPA2 personal only networks. */
|
||
|
public boolean isWpa2PersonalOnlyNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, r.getWifiSsid().toString())
|
||
|
&& ScanResultUtil.isScanResultForPskNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForSaeNetwork(r));
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are WPA3 only networks. */
|
||
|
public boolean isWpa3PersonalOnlyNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, r.getWifiSsid().toString())
|
||
|
&& ScanResultUtil.isScanResultForSaeNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForPskNetwork(r));
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are WPA2/WPA3 transition mode networks. */
|
||
|
public boolean isWpa2Wpa3PersonalTransitionNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, ScanResultUtil.createQuotedSsid(r.SSID))
|
||
|
&& ScanResultUtil.isScanResultForPskSaeTransitionNetwork(r));
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are OPEN only networks. */
|
||
|
public boolean isOpenOnlyNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, r.getWifiSsid().toString())
|
||
|
&& ScanResultUtil.isScanResultForOpenNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForOweNetwork(r));
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are OWE only networks. */
|
||
|
public boolean isOweOnlyNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, r.getWifiSsid().toString())
|
||
|
&& ScanResultUtil.isScanResultForOweNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForOweTransitionNetwork(r));
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are WPA2 Enterprise only networks. */
|
||
|
public boolean isWpa2EnterpriseOnlyNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, r.getWifiSsid().toString())
|
||
|
&& ScanResultUtil.isScanResultForEapNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r));
|
||
|
}
|
||
|
|
||
|
/** Indicate whether there are WPA3 Enterprise only networks. */
|
||
|
public boolean isWpa3EnterpriseOnlyNetworkInRange(String ssid) {
|
||
|
return mLastScanResultsMap.values().stream().anyMatch(r ->
|
||
|
TextUtils.equals(ssid, r.getWifiSsid().toString())
|
||
|
&& ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(r)
|
||
|
&& !ScanResultUtil.isScanResultForEapNetwork(r));
|
||
|
}
|
||
|
}
|