diff options
author | Roshan Pius <rpius@google.com> | 2018-01-29 22:57:09 -0800 |
---|---|---|
committer | Roshan Pius <rpius@google.com> | 2018-03-19 13:16:14 -0700 |
commit | 4f11976612567e57eefb0b58c7aef1059f52b45c (patch) | |
tree | 2c0acc1e787500d7b88035e6c4e40631d4c6a354 | |
parent | 6ef9e33f9bd16064df5f5696731f36f8fab286ae (diff) |
ScanRequestProxy: Add scan throttling
This is a port of the existing scan throttling for background apps in
WifiServiceImpl. We're now going to throttle all external scan requests,
except for requests coming in from apps holding NETWORK_SETTINGS
permission.
TODO: WifiServiceImpl.startScan() should return the status from
ScanRequestProxy.startScan().
Bug: 68987915
Test: Unit tests
Test: Scans from settings UI still works.
Change-Id: Ib388752df554de85ca5c29e424b344ef07a7d5b2
Merged-In: I78e4113317b9835dec6e816f2c5a0ba309d73061
5 files changed, 192 insertions, 214 deletions
diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java index 588ea6c99..39b9d8ae1 100644 --- a/service/java/com/android/server/wifi/ScanRequestProxy.java +++ b/service/java/com/android/server/wifi/ScanRequestProxy.java @@ -24,8 +24,10 @@ import android.net.wifi.WifiScanner; import android.os.Binder; import android.os.UserHandle; import android.os.WorkSource; +import android.util.ArrayMap; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.util.WifiPermissionsUtil; import java.util.ArrayList; @@ -47,17 +49,18 @@ import javax.annotation.concurrent.NotThreadSafe; * c) Will send out the {@link WifiManager#SCAN_RESULTS_AVAILABLE_ACTION} broadcast when new * scan results are available. * Note: This class is not thread-safe. It needs to be invoked from WifiStateMachine thread only. - * TODO (b/68987915): Port over scan throttling logic from WifiService for all apps. - * TODO: Port over idle mode handling from WifiService. */ @NotThreadSafe public class ScanRequestProxy { private static final String TAG = "WifiScanRequestProxy"; + @VisibleForTesting + public static final int SCAN_REQUEST_THROTTLE_INTERVAL_MS = 30 * 1000; private final Context mContext; private final WifiInjector mWifiInjector; private final WifiConfigManager mWifiConfigManager; private final WifiPermissionsUtil mWifiPermissionsUtil; + private final Clock mClock; private WifiScanner mWifiScanner; // Verbose logging flag. @@ -66,6 +69,8 @@ public class ScanRequestProxy { private boolean mScanningForHiddenNetworksEnabled = false; // Flag to indicate that we're waiting for scan results from an existing request. private boolean mIsScanProcessingComplete = true; + // Timestamps for the last scan requested by each app. + private final ArrayMap<String, Long> mLastScanTimestampsForApps = new ArrayMap(); // Scan results cached from the last full single scan request. private final List<ScanResult> mLastScanResults = new ArrayList<>(); // Common scan listener for scan requests. @@ -118,11 +123,12 @@ public class ScanRequestProxy { }; ScanRequestProxy(Context context, WifiInjector wifiInjector, WifiConfigManager configManager, - WifiPermissionsUtil wifiPermissionUtil) { + WifiPermissionsUtil wifiPermissionUtil, Clock clock) { mContext = context; mWifiInjector = wifiInjector; mWifiConfigManager = configManager; mWifiPermissionsUtil = wifiPermissionUtil; + mClock = clock; } /** @@ -184,15 +190,55 @@ public class ScanRequestProxy { } /** + * Helper method to send the scan request failure broadcast to specified package. + */ + private void sendScanResultFailureBroadcastToPackage(String packageName) { + // clear calling identity to send broadcast + long callingIdentity = Binder.clearCallingIdentity(); + try { + 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); + } finally { + // restore calling identity + Binder.restoreCallingIdentity(callingIdentity); + } + } + + /** + * Checks if the scan request from the app (specified by packageName) needs + * to be throttled. + */ + private boolean shouldScanRequestBeThrottledForApp(String packageName) { + long lastScanMs = mLastScanTimestampsForApps.getOrDefault(packageName, 0L); + long elapsedRealtime = mClock.getElapsedSinceBootMillis(); + if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < SCAN_REQUEST_THROTTLE_INTERVAL_MS) { + return true; + } + // Proceed with the scan request and record the time. + mLastScanTimestampsForApps.put(packageName, elapsedRealtime); + return false; + } + + /** * 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) { + public boolean startScan(int callingUid, String packageName) { if (!retrieveWifiScannerIfNecessary()) { Log.e(TAG, "Failed to retrieve wifiscanner"); - sendScanResultBroadcast(false); + sendScanResultFailureBroadcastToPackage(packageName); + return false; + } + boolean fromSettings = mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid); + // Check and throttle scan request from apps without NETWORK_SETTINGS permission. + if (!fromSettings && shouldScanRequestBeThrottledForApp(packageName)) { + Log.i(TAG, "Scan request from " + packageName + " throttled"); + sendScanResultFailureBroadcastToPackage(packageName); return false; } // Create a worksource using the caller's UID. @@ -201,7 +247,7 @@ public class ScanRequestProxy { // Create the scan settings. WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings(); // Scan requests from apps with network settings will be of high accuracy type. - if (mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) { + if (fromSettings) { settings.type = WifiScanner.TYPE_HIGH_ACCURACY; } // always do full scans @@ -234,5 +280,6 @@ public class ScanRequestProxy { */ public void clearScanResults() { mLastScanResults.clear(); + mLastScanTimestampsForApps.clear(); } } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 28e36169b..00d547abe 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -238,7 +238,7 @@ public class WifiInjector { mPasspointManager, mWifiConfigManager, mConnectivityLocalLog); mWifiMetrics.setPasspointManager(mPasspointManager); mScanRequestProxy = new ScanRequestProxy(mContext, this, mWifiConfigManager, - mWifiPermissionsUtil); + mWifiPermissionsUtil, mClock); // mWifiStateMachine has an implicit dependency on mJavaRuntime due to WifiDiagnostics. mJavaRuntime = Runtime.getRuntime(); mWifiStateMachine = new WifiStateMachine(mContext, mFrameworkFacade, diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 9d292e05a..a88b20d62 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -92,8 +92,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.Settings; -import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import android.util.MutableInt; import android.util.Slog; @@ -159,14 +157,12 @@ public class WifiServiceImpl extends IWifiManager.Stub { private final Context mContext; private final FrameworkFacade mFacade; private final Clock mClock; - private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>(); private final PowerManager mPowerManager; private final AppOpsManager mAppOps; private final UserManager mUserManager; private final ActivityManager mActivityManager; private final WifiCountryCode mCountryCode; - private long mBackgroundThrottleInterval; // Debug counter tracking scan requests sent by WifiManager private int scanRequestCounter = 0; @@ -181,9 +177,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { /* Backup/Restore Module */ private final WifiBackupRestore mWifiBackupRestore; - // Map of package name of background scan apps and last scan timestamp. - private final ArrayMap<String, Long> mLastScanTimestamps; - private WifiLog mLog; /** @@ -482,9 +475,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil(); mLog = mWifiInjector.makeLog(TAG); mFrameworkFacade = wifiInjector.getFrameworkFacade(); - mLastScanTimestamps = new ArrayMap<>(); - updateBackgroundThrottleInterval(); - updateBackgroundThrottlingWhitelist(); mIfaceIpModes = new ConcurrentHashMap<>(); mLocalOnlyHotspotRequests = new HashMap<>(); enableVerboseLoggingInternal(getVerboseLoggingLevel()); @@ -524,7 +514,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { (wifiEnabled ? "enabled" : "disabled")); registerForScanModeChange(); - registerForBackgroundThrottleChanges(); mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -629,18 +618,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { int callingUid = Binder.getCallingUid(); mLog.info("startScan uid=%").c(callingUid).flush(); - // Check and throttle background apps for wifi scan. - if (isRequestFromBackground(packageName)) { - long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L); - long elapsedRealtime = mClock.getElapsedSinceBootMillis(); - - if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) { - sendFailedScanBroadcast(); - return; - } - // Proceed with the scan request and record the time. - mLastScanTimestamps.put(packageName, elapsedRealtime); - } synchronized (this) { if (mInIdleMode) { // Need to send an immediate scan result broadcast in case the @@ -656,7 +633,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } boolean success = mWifiInjector.getWifiStateMachineHandler().runWithScissors(() -> { - if (!mScanRequestProxy.startScan(callingUid)) { + if (!mScanRequestProxy.startScan(callingUid, packageName)) { Log.e(TAG, "Failed to start scan"); } }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS); @@ -683,29 +660,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { } - // Check if the request comes from background. - private boolean isRequestFromBackground(String packageName) { - // Requests from system or wifi are not background. - if (Binder.getCallingUid() == Process.SYSTEM_UID - || Binder.getCallingUid() == Process.WIFI_UID) { - return false; - } - mAppOps.checkPackage(Binder.getCallingUid(), packageName); - if (mBackgroundThrottlePackageWhitelist.contains(packageName)) { - return false; - } - - // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming - // identify so the permission check can be done on system process where wifi runs in. - long callingIdentity = Binder.clearCallingIdentity(); - try { - return mActivityManager.getPackageImportance(packageName) - > BACKGROUND_IMPORTANCE_CUTOFF; - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - } - /** * WPS support in Client mode is deprecated. Return null. */ @@ -2414,51 +2368,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { } - // Monitors settings changes related to background wifi scan throttling. - private void registerForBackgroundThrottleChanges() { - mFrameworkFacade.registerContentObserver( - mContext, - Settings.Global.getUriFor( - Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS), - false, - new ContentObserver(null) { - @Override - public void onChange(boolean selfChange) { - updateBackgroundThrottleInterval(); - } - } - ); - mFrameworkFacade.registerContentObserver( - mContext, - Settings.Global.getUriFor( - Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST), - false, - new ContentObserver(null) { - @Override - public void onChange(boolean selfChange) { - updateBackgroundThrottlingWhitelist(); - } - } - ); - } - - private void updateBackgroundThrottleInterval() { - mBackgroundThrottleInterval = mFrameworkFacade.getLongSetting( - mContext, - Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS, - DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS); - } - - private void updateBackgroundThrottlingWhitelist() { - String setting = mFrameworkFacade.getStringSetting( - mContext, - Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST); - mBackgroundThrottlePackageWhitelist.clear(); - if (setting != null) { - mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(","))); - } - } - private void registerForBroadcasts() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_PRESENT); diff --git a/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java b/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java index 759fbe27b..aa7a821eb 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java @@ -47,6 +47,8 @@ import java.util.List; @SmallTest public class ScanRequestProxyTest { private static final int TEST_UID = 5; + private static final String TEST_PACKAGE_NAME_1 = "com.test.1"; + private static final String TEST_PACKAGE_NAME_2 = "com.test.2"; private static final List<WifiScanner.ScanSettings.HiddenNetwork> TEST_HIDDEN_NETWORKS_LIST = new ArrayList<WifiScanner.ScanSettings.HiddenNetwork>() {{ add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_1")); @@ -59,6 +61,7 @@ public class ScanRequestProxyTest { @Mock private WifiConfigManager mWifiConfigManager; @Mock private WifiScanner mWifiScanner; @Mock private WifiPermissionsUtil mWifiPermissionsUtil; + @Mock private Clock mClock; private ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor = ArgumentCaptor.forClass(WorkSource.class); private ArgumentCaptor<WifiScanner.ScanSettings> mScanSettingsArgumentCaptor = @@ -87,7 +90,8 @@ public class ScanRequestProxyTest { mTestScanDatas2 = ScanTestUtil.createScanDatas(new int[][]{ { 2412, 2422, 5200, 5210 } }); mScanRequestProxy = - new ScanRequestProxy(mContext, mWifiInjector, mWifiConfigManager, mWifiPermissionsUtil); + new ScanRequestProxy(mContext, mWifiInjector, mWifiConfigManager, + mWifiPermissionsUtil, mClock); } @After @@ -101,8 +105,8 @@ public class ScanRequestProxyTest { @Test public void testStartScanFailWithoutScanner() { when(mWifiInjector.getWifiScanner()).thenReturn(null); - assertFalse(mScanRequestProxy.startScan(TEST_UID)); - validateScanResultsAvailableBroadcastSent(false); + assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_1); verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext); } @@ -112,7 +116,7 @@ public class ScanRequestProxyTest { */ @Test public void testStartScanSuccess() { - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID))); @@ -127,7 +131,7 @@ public class ScanRequestProxyTest { @Test public void testStartScanSuccessFromAppWithNetworkSettings() { when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID)).thenReturn(true); - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID))); @@ -143,7 +147,7 @@ public class ScanRequestProxyTest { @Test public void testStartScanWithHiddenNetworkScanningDisabled() { mScanRequestProxy.enableScanningForHiddenNetworks(false); - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiConfigManager, never()).retrieveHiddenNetworkList(); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); @@ -159,7 +163,7 @@ public class ScanRequestProxyTest { @Test public void testStartScanWithHiddenNetworkScanningEnabled() { mScanRequestProxy.enableScanningForHiddenNetworks(true); - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiConfigManager).retrieveHiddenNetworkList(); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); @@ -213,7 +217,7 @@ public class ScanRequestProxyTest { @Test public void testScanSuccessOverwritesPreviousResults() { // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -224,7 +228,7 @@ public class ScanRequestProxyTest { mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new)); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 2. mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas2); @@ -243,7 +247,7 @@ public class ScanRequestProxyTest { @Test public void testScanFailureDoesNotOverwritePreviousResults() { // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -254,7 +258,7 @@ public class ScanRequestProxyTest { mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new)); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan failure processing. mScanListenerArgumentCaptor.getValue().onFailure(0, "failed"); @@ -277,12 +281,12 @@ public class ScanRequestProxyTest { WifiScanner.ScanListener listener1; WifiScanner.ScanListener listener2; // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); listener1 = mScanListenerArgumentCaptor.getValue(); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); // Ensure that we did send a second scan request to scanner. mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); listener2 = mScanListenerArgumentCaptor.getValue(); @@ -314,7 +318,7 @@ public class ScanRequestProxyTest { @Test public void testNewScanRequestAfterPreviousScanSucceeds() { // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send the scan results for request 1. mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -325,7 +329,7 @@ public class ScanRequestProxyTest { mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new)); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); // Ensure that we did send a second scan request to scanner. mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send the scan results for request 2. @@ -347,7 +351,7 @@ public class ScanRequestProxyTest { @Test public void testNewScanRequestAfterPreviousScanSucceedsWithInvalidScanDatas() { // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send scan success for request 1, but with invalid scan datas. @@ -358,7 +362,7 @@ public class ScanRequestProxyTest { assertTrue(mScanRequestProxy.getScanResults().isEmpty()); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); // Ensure that we did send a second scan request to scanner. mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send the scan results for request 2. @@ -380,7 +384,7 @@ public class ScanRequestProxyTest { @Test public void testNewScanRequestAfterPreviousScanFailure() { // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send scan failure for request 1. @@ -390,7 +394,7 @@ public class ScanRequestProxyTest { assertTrue(mScanRequestProxy.getScanResults().isEmpty()); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); // Ensure that we did send a second scan request to scanner. mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Now send the scan results for request 2. @@ -410,7 +414,7 @@ public class ScanRequestProxyTest { @Test public void testClearScanResults() { // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); // Verify the scan results processing for request 1. mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1); @@ -433,12 +437,12 @@ public class ScanRequestProxyTest { WifiScanner.ScanListener listener1; WifiScanner.ScanListener listener2; // Make scan request 1. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); listener1 = mScanListenerArgumentCaptor.getValue(); // Make scan request 2. - assertTrue(mScanRequestProxy.startScan(TEST_UID)); + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); // Ensure that we did send a second scan request to scanner. mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); listener2 = mScanListenerArgumentCaptor.getValue(); @@ -448,6 +452,96 @@ public class ScanRequestProxyTest { verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext); } + /** + * Ensure new scan requests from the same app are rejected if it's before + * {@link ScanRequestProxy#SCAN_REQUEST_THROTTLE_INTERVAL_MS} + */ + @Test + public void testSuccessiveScanRequestFromSameAppThrottled() { + long firstRequestMs = 782; + when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); + // Make scan request 1. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + long secondRequestMs = + firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_MS - 1; + when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs); + // Make scan request 2 from the same package name & ensure that it is throttled. + assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_1); + + verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext); + } + + /** + * Ensure new scan requests from the same app are not rejected if it's after + * {@link ScanRequestProxy#SCAN_REQUEST_THROTTLE_INTERVAL_MS} + */ + @Test + public void testSuccessiveScanRequestFromSameAppNotThrottled() { + long firstRequestMs = 782; + when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); + // Make scan request 1. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + long secondRequestMs = + firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_MS + 1; + when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs); + // Make scan request 2 from the same package name & ensure that it is not throttled. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext); + } + + /** + * Ensure new scan requests from the same app with NETWORK_SETTINGS permission are not + * throttled. + */ + @Test + public void testSuccessiveScanRequestFromSameAppWithNetworkSettingsPermissionNotThrottled() { + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID)).thenReturn(true); + + long firstRequestMs = 782; + when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); + // Make scan request 1. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + long secondRequestMs = + firstRequestMs + ScanRequestProxy.SCAN_REQUEST_THROTTLE_INTERVAL_MS - 1; + when(mClock.getElapsedSinceBootMillis()).thenReturn(secondRequestMs); + // Make scan request 2 from the same package name & ensure that it is not throttled. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext); + } + + /** + * Ensure new scan requests from different apps are not throttled. + */ + @Test + public void testSuccessiveScanRequestFromDifferentAppsNotThrottled() { + long firstRequestMs = 782; + when(mClock.getElapsedSinceBootMillis()).thenReturn(firstRequestMs); + // Make scan request 1. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + // Make scan request 2 from the same package name & ensure that it is throttled. + assertFalse(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1)); + validateScanResultsFailureBroadcastSent(TEST_PACKAGE_NAME_1); + + // Make scan request 3 from a different package name & ensure that it is not throttled. + assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_2)); + mInOrder.verify(mWifiScanner).startScan(any(), any(), any()); + + verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext); + } + private void validateScanSettings(WifiScanner.ScanSettings scanSettings, boolean expectHiddenNetworks) { validateScanSettings(scanSettings, expectHiddenNetworks, false); @@ -502,4 +596,21 @@ public class ScanRequestProxyTest { boolean scanSucceeded = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); assertEquals(expectScanSuceeded, scanSucceeded); } + + private void validateScanResultsFailureBroadcastSent(String expectedPackageName) { + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(UserHandle.class); + mInOrder.verify(mContext).sendBroadcastAsUser( + intentCaptor.capture(), userHandleCaptor.capture()); + + assertEquals(userHandleCaptor.getValue(), UserHandle.ALL); + + Intent intent = intentCaptor.getValue(); + assertEquals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION, intent.getAction()); + assertEquals(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, intent.getFlags()); + boolean scanSucceeded = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); + assertFalse(scanSucceeded); + String packageName = intent.getPackage(); + assertEquals(expectedPackageName, packageName); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index 6f99f5521..efd9b9fc0 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -89,7 +89,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserManager; import android.os.test.TestLooper; -import android.provider.Settings; import android.support.test.filters.SmallTest; import com.android.internal.os.PowerProfile; @@ -126,9 +125,7 @@ public class WifiServiceImplTest { private static final String TAG = "WifiServiceImplTest"; private static final String SCAN_PACKAGE_NAME = "scanPackage"; - private static final String WHITE_LIST_SCAN_PACKAGE_NAME = "whiteListScanPackage"; private static final int DEFAULT_VERBOSE_LOGGING = 0; - private static final long WIFI_BACKGROUND_SCAN_INTERVAL = 10000; private static final String ANDROID_SYSTEM_PACKAGE = "android"; private static final String TEST_PACKAGE_NAME = "TestPackage"; private static final String SYSUI_PACKAGE_NAME = "com.android.systemui"; @@ -286,15 +283,6 @@ public class WifiServiceImplTest { anyBoolean(), any()); when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager); when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); - when(mFrameworkFacade.getLongSetting( - eq(mContext), - eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS), - anyLong())) - .thenReturn(WIFI_BACKGROUND_SCAN_INTERVAL); - when(mFrameworkFacade.getStringSetting( - eq(mContext), - eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST))) - .thenReturn(WHITE_LIST_SCAN_PACKAGE_NAME); IPowerManager powerManagerService = mock(IPowerManager.class); mPowerManager = new PowerManager(mContext, powerManagerService, new Handler()); when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE); @@ -1029,80 +1017,6 @@ public class WifiServiceImplTest { } /** - * Ensure foreground apps can always do wifi scans. - */ - @Test - public void testWifiScanStartedForeground() { - setupWifiStateMachineHandlerForRunWithScissors(); - - when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); - mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy).startScan(Process.myUid()); - verifyCheckChangePermission(SCAN_PACKAGE_NAME); - } - - /** - * Ensure background apps get throttled when the previous scan is too close. - */ - @Test - public void testWifiScanBackgroundThrottled() { - setupWifiStateMachineHandlerForRunWithScissors(); - - when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); - long startMs = 1000; - when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs); - mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy).startScan(Process.myUid()); - - when(mClock.getElapsedSinceBootMillis()).thenReturn( - startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000); - mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy, times(1)).startScan(Process.myUid()); - } - - /** - * Ensure background apps can do wifi scan when the throttle interval reached. - */ - @Test - public void testWifiScanBackgroundNotThrottled() { - setupWifiStateMachineHandlerForRunWithScissors(); - - when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); - long startMs = 1000; - when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs); - mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy).startScan(Process.myUid()); - - when(mClock.getElapsedSinceBootMillis()).thenReturn( - startMs + WIFI_BACKGROUND_SCAN_INTERVAL + 1000); - mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy, times(2)).startScan(Process.myUid()); - } - - /** - * Ensure background apps can do wifi scan when the throttle interval reached. - */ - @Test - public void testWifiScanBackgroundWhiteListed() { - setupWifiStateMachineHandlerForRunWithScissors(); - - when(mActivityManager.getPackageImportance(WHITE_LIST_SCAN_PACKAGE_NAME)).thenReturn( - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); - long startMs = 1000; - when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs); - mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME); - verify(mScanRequestProxy).startScan(Process.myUid()); - - when(mClock.getElapsedSinceBootMillis()).thenReturn( - startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000); - mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME); - verify(mScanRequestProxy, times(2)).startScan(Process.myUid()); - } - - /** * Ensure that we handle scan request failure when posting the runnable to handler fails. */ @Ignore @@ -1111,11 +1025,8 @@ public class WifiServiceImplTest { setupWifiStateMachineHandlerForRunWithScissors(); doReturn(false).when(mHandlerSpyForWsmRunWithScissors) .runWithScissors(any(), anyLong()); - - when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy, never()).startScan(Process.myUid()); + verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME); } static final String TEST_SSID = "Sid's Place"; @@ -2614,14 +2525,14 @@ public class WifiServiceImplTest { // Send a scan request while the device is idle. mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); // No scans must be made yet as the device is idle. - verify(mScanRequestProxy, never()).startScan(Process.myUid()); + verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME); // Tell the wifi service that idle mode ended. when(mPowerManager.isDeviceIdleMode()).thenReturn(false); TestUtil.sendIdleModeChanged(mBroadcastReceiverCaptor.getValue(), mContext); // Must scan now. - verify(mScanRequestProxy, times(1)).startScan(Process.myUid()); + verify(mScanRequestProxy).startScan(Process.myUid(), TEST_PACKAGE_NAME); // The app ops check is executed with this package's identity (not the identity of the // original remote caller who requested the scan while idle). verify(mAppOpsManager).noteOp( @@ -2630,7 +2541,7 @@ public class WifiServiceImplTest { // Send another scan request. The device is not idle anymore, so it must be executed // immediately. mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); - verify(mScanRequestProxy, times(2)).startScan(Process.myUid()); + verify(mScanRequestProxy).startScan(Process.myUid(), SCAN_PACKAGE_NAME); } private class IdleModeIntentMatcher implements ArgumentMatcher<IntentFilter> { |