diff options
3 files changed, 403 insertions, 19 deletions
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 2da06fd4b..4c4008718 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -18,6 +18,7 @@ package com.android.server.wifi; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.AlarmManager; import android.app.AppOpsManager; import android.content.Context; import android.hardware.SystemSensorManager; @@ -548,7 +549,8 @@ public class WifiInjector { return new WifiNetworkFactory( mWifiCoreHandlerThread.getLooper(), mContext, nc, (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE), - wifiConnectivityManager); + (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE), + mClock, this, wifiConnectivityManager); } /** diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java index 936b85dbb..a75b80f8c 100644 --- a/service/java/com/android/server/wifi/WifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java @@ -16,16 +16,27 @@ package com.android.server.wifi; +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; + import android.app.ActivityManager; +import android.app.AlarmManager; import android.content.Context; import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiNetworkSpecifier; +import android.net.wifi.WifiScanner; +import android.os.Handler; import android.os.Looper; +import android.os.WorkSource; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.FileDescriptor; import java.io.PrintWriter; @@ -34,23 +45,107 @@ import java.io.PrintWriter; */ public class WifiNetworkFactory extends NetworkFactory { private static final String TAG = "WifiNetworkFactory"; + @VisibleForTesting private static final int SCORE_FILTER = 60; - private final WifiConnectivityManager mWifiConnectivityManager; + @VisibleForTesting + public static final int PERIODIC_SCAN_INTERVAL_MS = 10 * 1000; // 10 seconds + private final Context mContext; private final ActivityManager mActivityManager; + private final AlarmManager mAlarmManager; + private final Clock mClock; + private final Handler mHandler; + private final WifiInjector mWifiInjector; + private final WifiConnectivityManager mWifiConnectivityManager; + private final WifiScanner.ScanSettings mScanSettings; + private final NetworkFactoryScanListener mScanListener; + private final NetworkFactoryAlarmListener mPeriodicScanTimerListener; private int mGenericConnectionReqCount = 0; - NetworkRequest mActiveSpecificNetworkRequest; + private NetworkRequest mActiveSpecificNetworkRequest; + private WifiNetworkSpecifier mActiveSpecificNetworkRequestSpecifier; + private WifiScanner mWifiScanner; // Verbose logging flag. private boolean mVerboseLoggingEnabled = false; + private boolean mPeriodicScanTimerSet = false; + + // Scan listener for scan requests. + private class NetworkFactoryScanListener 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) { + Log.e(TAG, "Scan failure received. reason: " + reason + + ", description: " + description); + // TODO(b/113878056): Retry scan to workaround any transient scan failures. + scheduleNextPeriodicScan(); + } + + @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, Ignoring..."); + return; + } + WifiScanner.ScanData scanData = scanDatas[0]; + ScanResult[] scanResults = scanData.getResults(); + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Received " + scanResults.length + " scan results"); + } + // TODO(b/113878056): Find network match in scan results + + // Didn't find a match, schedule the next scan. + scheduleNextPeriodicScan(); + } + + @Override + public void onFullResult(ScanResult fullScanResult) { + // Ignore for single scans. + } + + @Override + public void onPeriodChanged(int periodInMs) { + // Ignore for single scans. + } + }; + + private class NetworkFactoryAlarmListener implements AlarmManager.OnAlarmListener { + @Override + public void onAlarm() { + // Trigger the next scan. + startScan(); + } + } public WifiNetworkFactory(Looper looper, Context context, NetworkCapabilities nc, - ActivityManager activityManager, + ActivityManager activityManager, AlarmManager alarmManager, + Clock clock, WifiInjector wifiInjector, WifiConnectivityManager connectivityManager) { super(looper, context, TAG, nc); mContext = context; mActivityManager = activityManager; + mAlarmManager = alarmManager; + mClock = clock; + mHandler = new Handler(looper); + mWifiInjector = wifiInjector; mWifiConnectivityManager = connectivityManager; + // Create the scan settings. + mScanSettings = new WifiScanner.ScanSettings(); + mScanSettings.type = WifiScanner.TYPE_HIGH_ACCURACY; + mScanSettings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS; + mScanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; + mScanListener = new NetworkFactoryScanListener(); + mPeriodicScanTimerListener = new NetworkFactoryAlarmListener(); setScoreFilter(SCORE_FILTER); } @@ -62,6 +157,11 @@ public class WifiNetworkFactory extends NetworkFactory { mVerboseLoggingEnabled = (verbose > 0); } + /** + * Check whether to accept the new network connection request. + * + * All the validation of the incoming request is done in this method. + */ @Override public boolean acceptRequest(NetworkRequest networkRequest, int score) { NetworkSpecifier ns = networkRequest.networkCapabilities.getNetworkSpecifier(); @@ -112,6 +212,12 @@ public class WifiNetworkFactory extends NetworkFactory { return true; } + /** + * Handle new network connection requests. + * + * The assumption here is that {@link #acceptRequest(NetworkRequest, int)} has already sanitized + * the incoming request. + */ @Override protected void needNetworkFor(NetworkRequest networkRequest, int score) { NetworkSpecifier ns = networkRequest.networkCapabilities.getNetworkSpecifier(); @@ -126,9 +232,20 @@ public class WifiNetworkFactory extends NetworkFactory { Log.e(TAG, "Invalid network specifier mentioned. Rejecting"); return; } + retrieveWifiScanner(); + // Store the active network request. mActiveSpecificNetworkRequest = new NetworkRequest(networkRequest); - // TODO (b/113878056): Complete handling. + WifiNetworkSpecifier wns = (WifiNetworkSpecifier) ns; + mActiveSpecificNetworkRequestSpecifier = new WifiNetworkSpecifier( + wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.wifiConfiguration, + wns.requestorUid); + + // Trigger periodic scans for finding a network in the request. + startPeriodicScans(); + // Disable Auto-join so that NetworkFactory can take control of the network selection. + // TODO(b/117979585): Defer turning off auto-join. + mWifiConnectivityManager.enable(false); } } @@ -156,7 +273,13 @@ public class WifiNetworkFactory extends NetworkFactory { } // Release the active network request. mActiveSpecificNetworkRequest = null; - // TODO (b/113878056): Complete handling. + mActiveSpecificNetworkRequestSpecifier = null; + // Cancel the periodic scans. + cancelPeriodicScans(); + // Re-enable Auto-join (if there is a generic request pending). + if (mGenericConnectionReqCount > 0) { + mWifiConnectivityManager.enable(true); + } } } @@ -201,5 +324,62 @@ public class WifiNetworkFactory extends NetworkFactory { return false; } } + + /** + * Helper method to populate WifiScanner handle. This is done lazily because + * WifiScanningService is started after WifiService. + */ + private void retrieveWifiScanner() { + if (mWifiScanner != null) return; + mWifiScanner = mWifiInjector.getWifiScanner(); + checkNotNull(mWifiScanner); + } + + private void startPeriodicScans() { + if (mActiveSpecificNetworkRequestSpecifier == null) { + Log.e(TAG, "Periodic scan triggered when there is no active network request. " + + "Ignoring..."); + return; + } + WifiNetworkSpecifier wns = mActiveSpecificNetworkRequestSpecifier; + WifiConfiguration wifiConfiguration = wns.wifiConfiguration; + if (wifiConfiguration.hiddenSSID) { + mScanSettings.hiddenNetworks = new WifiScanner.ScanSettings.HiddenNetwork[1]; + // Can't search for SSID pattern in hidden networks. + mScanSettings.hiddenNetworks[0] = + new WifiScanner.ScanSettings.HiddenNetwork( + addEnclosingQuotes(wns.ssidPatternMatcher.getPath())); + } + startScan(); + } + + private void cancelPeriodicScans() { + if (mPeriodicScanTimerSet) { + mAlarmManager.cancel(mPeriodicScanTimerListener); + mPeriodicScanTimerSet = false; + } + // Clear the hidden networks field after each request. + mScanSettings.hiddenNetworks = null; + } + + private void scheduleNextPeriodicScan() { + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + mClock.getElapsedSinceBootMillis() + PERIODIC_SCAN_INTERVAL_MS, + TAG, mPeriodicScanTimerListener, mHandler); + mPeriodicScanTimerSet = true; + } + + private void startScan() { + if (mActiveSpecificNetworkRequestSpecifier == null) { + Log.e(TAG, "Scan triggered when there is no active network request. Ignoring..."); + return; + } + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Starting the next scan for " + mActiveSpecificNetworkRequestSpecifier); + } + // Create a worksource using the caller's UID. + WorkSource workSource = new WorkSource(mActiveSpecificNetworkRequestSpecifier.requestorUid); + mWifiScanner.startScan(mScanSettings, mScanListener, workSource); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java index 1a7cce780..da4c32994 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java @@ -19,10 +19,15 @@ package com.android.server.wifi; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; +import static com.android.server.wifi.WifiNetworkFactory.PERIODIC_SCAN_INTERVAL_MS; +import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.AlarmManager.OnAlarmListener; import android.content.Context; import android.content.pm.PackageManager; import android.net.MacAddress; @@ -30,7 +35,11 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiNetworkSpecifier; +import android.net.wifi.WifiScanner; +import android.net.wifi.WifiScanner.ScanListener; +import android.net.wifi.WifiScanner.ScanSettings; import android.os.PatternMatcher; +import android.os.WorkSource; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; import android.util.Pair; @@ -38,6 +47,8 @@ import android.util.Pair; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -55,10 +66,19 @@ public class WifiNetworkFactoryTest { @Mock WifiConnectivityManager mWifiConnectivityManager; @Mock Context mContext; @Mock ActivityManager mActivityManager; + @Mock AlarmManager mAlarmManager; + @Mock Clock mClock; + @Mock WifiInjector mWifiInjector; + @Mock WifiScanner mWifiScanner; @Mock PackageManager mPackageManager; NetworkCapabilities mNetworkCapabilities; TestLooper mLooper; NetworkRequest mNetworkRequest; + WifiScanner.ScanData[] mTestScanDatas; + ArgumentCaptor<ScanSettings> mScanSettingsArgumentCaptor = + ArgumentCaptor.forClass(ScanSettings.class); + ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor = + ArgumentCaptor.forClass(WorkSource.class); private WifiNetworkFactory mWifiNetworkFactory; @@ -72,6 +92,7 @@ public class WifiNetworkFactoryTest { mLooper = new TestLooper(); mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + mTestScanDatas = ScanTestUtil.createScanDatas(new int[][]{ { 2417, 2427, 5180, 5170 } }); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getNameForUid(TEST_UID_1)).thenReturn(TEST_PACKAGE_NAME_1); @@ -80,9 +101,11 @@ public class WifiNetworkFactoryTest { .thenReturn(IMPORTANCE_FOREGROUND_SERVICE); when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_2)) .thenReturn(IMPORTANCE_FOREGROUND_SERVICE); + when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner); mWifiNetworkFactory = new WifiNetworkFactory(mLooper.getLooper(), mContext, - mNetworkCapabilities, mActivityManager, mWifiConnectivityManager); + mNetworkCapabilities, mActivityManager, mAlarmManager, mClock, mWifiInjector, + mWifiConnectivityManager); mNetworkRequest = new NetworkRequest.Builder() .setCapabilities(mNetworkCapabilities) @@ -149,7 +172,7 @@ public class WifiNetworkFactoryTest { when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1)) .thenReturn(IMPORTANCE_FOREGROUND_SERVICE + 1); - WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1); + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0)); @@ -164,7 +187,7 @@ public class WifiNetworkFactoryTest { when(mActivityManager.getPackageImportance(TEST_PACKAGE_NAME_1)) .thenReturn(IMPORTANCE_FOREGROUND); - WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1); + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0)); @@ -182,13 +205,13 @@ public class WifiNetworkFactoryTest { .thenReturn(IMPORTANCE_FOREGROUND); // Handle request 1. - WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1); + WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1); mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); // Make request 2 which will be accepted because a fg app request can // override a fg service request. - WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2); + WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2); assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0)); } @@ -205,13 +228,13 @@ public class WifiNetworkFactoryTest { .thenReturn(IMPORTANCE_FOREGROUND_SERVICE); // Handle request 1. - WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1); + WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1); mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); // Make request 2 which will be accepted because a fg service request can // override an existing fg service request. - WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2); + WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2); assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0)); } @@ -228,13 +251,13 @@ public class WifiNetworkFactoryTest { .thenReturn(IMPORTANCE_FOREGROUND); // Handle request 1. - WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1); + WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1); mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); // Make request 2 which will be accepted because a fg app request can // override an existing fg app request. - WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2); + WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2); assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0)); } @@ -251,23 +274,202 @@ public class WifiNetworkFactoryTest { .thenReturn(IMPORTANCE_FOREGROUND_SERVICE); // Handle request 1. - WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1); + WifiNetworkSpecifier specifier1 = createWifiNetworkSpecifier(TEST_UID_1, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1); mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); // Make request 2 which will be rejected because a fg service request cannot // override a fg app request. - WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2); + WifiNetworkSpecifier specifier2 = createWifiNetworkSpecifier(TEST_UID_2, false); mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2); assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest, 0)); } - private WifiNetworkSpecifier createWifiNetworkSpecifier(int uid) { + /** + * Verify handling of new network request with network specifier. + */ + @Test + public void testHandleNetworkRequestWithSpecifier() { + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false); + mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + + // Disable connectivity manager . + verify(mWifiConnectivityManager).enable(false); + verify(mWifiScanner).startScan(mScanSettingsArgumentCaptor.capture(), any(), + mWorkSourceArgumentCaptor.capture()); + + // Verify scan settings. + ScanSettings scanSettings = mScanSettingsArgumentCaptor.getValue(); + assertNotNull(scanSettings); + assertEquals(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, scanSettings.band); + assertEquals(WifiScanner.TYPE_HIGH_ACCURACY, scanSettings.type); + assertEquals(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, scanSettings.reportEvents); + assertNull(scanSettings.hiddenNetworks); + WorkSource workSource = mWorkSourceArgumentCaptor.getValue(); + assertNotNull(workSource); + assertEquals(TEST_UID_1, workSource.get(0)); + } + + /** + * Verify handling of new network request with network specifier for a hidden network. + */ + @Test + public void testHandleNetworkRequestWithSpecifierForHiddenNetwork() { + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, true); + mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + + // Disable connectivity manager . + verify(mWifiConnectivityManager).enable(false); + verify(mWifiScanner).startScan(mScanSettingsArgumentCaptor.capture(), any(), + mWorkSourceArgumentCaptor.capture()); + + // Verify scan settings. + ScanSettings scanSettings = mScanSettingsArgumentCaptor.getValue(); + assertNotNull(scanSettings); + assertEquals(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, scanSettings.band); + assertEquals(WifiScanner.TYPE_HIGH_ACCURACY, scanSettings.type); + assertEquals(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, scanSettings.reportEvents); + assertNotNull(scanSettings.hiddenNetworks); + assertNotNull(scanSettings.hiddenNetworks[0]); + assertEquals(scanSettings.hiddenNetworks[0].ssid, + addEnclosingQuotes(specifier.ssidPatternMatcher.getPath())); + WorkSource workSource = mWorkSourceArgumentCaptor.getValue(); + assertNotNull(workSource); + assertEquals(TEST_UID_1, workSource.get(0)); + } + + /** + * Verify handling of new network request with network specifier for a non-hidden network + * after processing a previous hidden network requst. + * Validates that the scan settings was properly reset between the 2 request + * {@link ScanSettings#hiddenNetworks} + */ + @Test + public void testHandleNetworkRequestWithSpecifierAfterPreviousHiddenNetworkRequest() { + testHandleNetworkRequestWithSpecifierForHiddenNetwork(); + mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); + reset(mWifiScanner, mWifiConnectivityManager); + testHandleNetworkRequestWithSpecifier(); + } + + /** + * Verify handling of release of the active network request with network specifier. + */ + @Test + public void testHandleNetworkReleaseWithSpecifier() { + // Make a generic request first to ensure that we re-enable auto-join after release. + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false); + mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); + + // Make the network request with specifier. + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + // Disable connectivity manager . + verify(mWifiConnectivityManager).enable(false); + verify(mWifiScanner).startScan(any(), any(), any()); + + // Release the network request. + mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); + // Re-enable connectivity manager . + verify(mWifiConnectivityManager).enable(true); + } + + /** + * Verify the periodic scan to find a network matching the network specifier. + * Simulates the case where the network is not found in any of the scan results. + */ + @Test + public void testPeriodicScanNetworkRequestWithSpecifier() { + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false); + mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + + verify(mWifiConnectivityManager).enable(false); + verifyPeriodicScans(0, + PERIODIC_SCAN_INTERVAL_MS, // 10s + PERIODIC_SCAN_INTERVAL_MS, // 10s + PERIODIC_SCAN_INTERVAL_MS, // 10s + PERIODIC_SCAN_INTERVAL_MS); // 10s + } + + /** + * Verify the periodic scan back off to find a network matching the network specifier + * is cancelled when the active network request is released. + */ + @Test + public void testPeriodicScanCancelOnReleaseNetworkRequestWithSpecifier() { + WifiNetworkSpecifier specifier = createWifiNetworkSpecifier(TEST_UID_1, false); + mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier); + mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0); + + verify(mWifiConnectivityManager).enable(false); + verifyPeriodicScans(0, + PERIODIC_SCAN_INTERVAL_MS, // 10s + PERIODIC_SCAN_INTERVAL_MS); // 10s + + mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); + // Cancel the alarm set for the next scan. + verify(mAlarmManager).cancel(any(OnAlarmListener.class)); + } + + // Simulates the periodic scans performed to find a matching network. + // a) Start scan + // b) Scan results received. + // c) Set alarm for next scan at the expected interval. + // d) Alarm fires, go to step a) again and repeat. + private void verifyPeriodicScans(long...expectedIntervalsInSeconds) { + when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); + + ArgumentCaptor<OnAlarmListener> alarmListenerArgumentCaptor = + ArgumentCaptor.forClass(OnAlarmListener.class); + OnAlarmListener alarmListener = null; + ArgumentCaptor<ScanListener> scanListenerArgumentCaptor = + ArgumentCaptor.forClass(ScanListener.class); + ScanListener scanListener = null; + + InOrder inOrder = inOrder(mWifiScanner, mAlarmManager); + + for (int i = 0; i < expectedIntervalsInSeconds.length - 1; i++) { + long expectedCurrentIntervalInMs = expectedIntervalsInSeconds[i]; + long expectedNextIntervalInMs = expectedIntervalsInSeconds[i + 1]; + + // First scan is immediately fired, so need for the alarm to fire. + if (expectedCurrentIntervalInMs != 0) { + // Fire the alarm and ensure that we started the next scan. + alarmListener.onAlarm(); + } + inOrder.verify(mWifiScanner).startScan( + any(), scanListenerArgumentCaptor.capture(), any()); + scanListener = scanListenerArgumentCaptor.getValue(); + assertNotNull(scanListener); + + // Now trigger the scan results callback and verify the alarm set for the next scan. + scanListener.onResults(mTestScanDatas); + + inOrder.verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(expectedNextIntervalInMs), any(), + alarmListenerArgumentCaptor.capture(), any()); + alarmListener = alarmListenerArgumentCaptor.getValue(); + assertNotNull(alarmListener); + } + + verifyNoMoreInteractions(mWifiScanner, mAlarmManager); + } + + private WifiNetworkSpecifier createWifiNetworkSpecifier(int uid, boolean isHidden) { PatternMatcher ssidPatternMatch = new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL); Pair<MacAddress, MacAddress> bssidPatternMatch = Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); - WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createPskNetwork(); + WifiConfiguration wifiConfiguration; + if (isHidden) { + wifiConfiguration = WifiConfigurationTestUtil.createPskHiddenNetwork(); + } else { + wifiConfiguration = WifiConfigurationTestUtil.createPskNetwork(); + } return new WifiNetworkSpecifier( ssidPatternMatch, bssidPatternMatch, wifiConfiguration, uid); } |