ScanRequestProxy: Handle scan requests & results.
Implementation of the scan request & results processing.
Bug: 70359905
Test: Unit tests.
Change-Id: I0b88334b61ce88fa1a3117d8755cceddd1a72bc3
diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java
index 2c60925..5ed5988 100644
--- a/service/java/com/android/server/wifi/ScanRequestProxy.java
+++ b/service/java/com/android/server/wifi/ScanRequestProxy.java
@@ -23,9 +23,11 @@
import android.net.wifi.WifiScanner;
import android.os.Binder;
import android.os.UserHandle;
+import android.os.WorkSource;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import javax.annotation.concurrent.NotThreadSafe;
@@ -61,6 +63,53 @@
private boolean mScanningForHiddenNetworksEnabled = false;
// Scan results cached from the last full single scan request.
private final List<ScanResult> mLastScanResults = new ArrayList<>();
+ // Common scan listener for scan requests.
+ private final WifiScanner.ScanListener mScanListener = new 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");
+ sendScanResultBroadcast(false);
+ }
+
+ @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.e(TAG, "Found more than 1 batch of scan results, Ignoring...");
+ sendScanResultBroadcast(false);
+ }
+ WifiScanner.ScanData scanData = scanDatas[0];
+ ScanResult[] scanResults = scanData.getResults();
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Received " + scanResults.length + " scan results");
+ }
+ // Store the last scan results & send out the scan completion broadcast.
+ mLastScanResults.clear();
+ mLastScanResults.addAll(Arrays.asList(scanResults));
+ sendScanResultBroadcast(true);
+ }
+
+ @Override
+ public void onFullResult(ScanResult fullScanResult) {
+ // Ignore for single scans.
+ }
+
+ @Override
+ public void onPeriodChanged(int periodInMs) {
+ // Ignore for single scans.
+ }
+ };
ScanRequestProxy(Context context, WifiInjector wifiInjector, WifiConfigManager configManager) {
mContext = context;
@@ -123,15 +172,26 @@
public boolean startScan(int callingUid) {
if (!retrieveWifiScannerIfNecessary()) {
Log.e(TAG, "Failed to retrieve wifiscanner");
+ sendScanResultBroadcast(false);
return false;
}
+ // Create a worksource using the caller's UID.
+ WorkSource workSource = new WorkSource(callingUid);
- // Retrieve the list of hidden network SSIDs to scan for, if enabled.
+ // Create the scan settings.
+ WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+ // always do full scans
+ settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
+ settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
+ | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
if (mScanningForHiddenNetworksEnabled) {
+ // retrieve the list of hidden network SSIDs to scan for, if enabled.
List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList =
mWifiConfigManager.retrieveHiddenNetworkList();
+ settings.hiddenNetworks = hiddenNetworkList.toArray(
+ new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
}
- // TODO: Implementation
+ mWifiScanner.startScan(settings, mScanListener, workSource);
return true;
}
@@ -143,4 +203,11 @@
public List<ScanResult> getScanResults() {
return mLastScanResults;
}
+
+ /**
+ * Clear the stored scan results.
+ */
+ public void clearScanResults() {
+ mLastScanResults.clear();
+ }
}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
index 5ab57cc..f1f5abb 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
@@ -20,16 +20,24 @@
import static org.mockito.Mockito.*;
import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
+import android.os.UserHandle;
+import android.os.WorkSource;
import android.test.suitebuilder.annotation.SmallTest;
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;
import java.util.ArrayList;
+import java.util.List;
/**
* Unit tests for {@link com.android.server.wifi.ScanRequestProxy}.
@@ -37,11 +45,26 @@
@SmallTest
public class ScanRequestProxyTest {
private static final int TEST_UID = 5;
+ private static final List<WifiScanner.ScanSettings.HiddenNetwork> TEST_HIDDEN_NETWORKS_LIST =
+ new ArrayList<WifiScanner.ScanSettings.HiddenNetwork>() {{
+ add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_1"));
+ add(new WifiScanner.ScanSettings.HiddenNetwork("test_ssid_2"));
+
+ }};
@Mock private Context mContext;
@Mock private WifiInjector mWifiInjector;
@Mock private WifiConfigManager mWifiConfigManager;
@Mock private WifiScanner mWifiScanner;
+ private ArgumentCaptor<WorkSource> mWorkSourceArgumentCaptor =
+ ArgumentCaptor.forClass(WorkSource.class);
+ private ArgumentCaptor<WifiScanner.ScanSettings> mScanSettingsArgumentCaptor =
+ ArgumentCaptor.forClass(WifiScanner.ScanSettings.class);
+ private ArgumentCaptor<WifiScanner.ScanListener> mScanListenerArgumentCaptor =
+ ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
+ private WifiScanner.ScanData[] mTestScanDatas1;
+ private WifiScanner.ScanData[] mTestScanDatas2;
+ private InOrder mInOrder;
private ScanRequestProxy mScanRequestProxy;
@@ -50,7 +73,16 @@
MockitoAnnotations.initMocks(this);
when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
- when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(new ArrayList());
+ when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(TEST_HIDDEN_NETWORKS_LIST);
+ doNothing().when(mWifiScanner).startScan(
+ mScanSettingsArgumentCaptor.capture(),
+ mScanListenerArgumentCaptor.capture(),
+ mWorkSourceArgumentCaptor.capture());
+
+ mInOrder = inOrder(mWifiScanner, mWifiConfigManager, mContext);
+ mTestScanDatas1 = ScanTestUtil.createScanDatas(new int[][]{ { 2417, 2427, 5180, 5170 } });
+ mTestScanDatas2 = ScanTestUtil.createScanDatas(new int[][]{ { 2412, 2422, 5200, 5210 } });
+
mScanRequestProxy = new ScanRequestProxy(mContext, mWifiInjector, mWifiConfigManager);
}
@@ -66,14 +98,23 @@
public void testStartScanFailWithoutScanner() {
when(mWifiInjector.getWifiScanner()).thenReturn(null);
assertFalse(mScanRequestProxy.startScan(TEST_UID));
+ validateScanResultsAvailableBroadcastSent(false);
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
}
/**
* Verify scan request will forwarded to wifiscanner if wifiscanner is present.
*/
@Test
- public void testStartScanSuccessWithScanner() {
+ public void testStartScanSuccess() {
assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID)));
+ validateScanSettings(mScanSettingsArgumentCaptor.getValue(), false);
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
}
/**
@@ -83,7 +124,13 @@
public void testStartScanWithHiddenNetworkScanningDisabled() {
mScanRequestProxy.enableScanningForHiddenNetworks(false);
assertTrue(mScanRequestProxy.startScan(TEST_UID));
- verify(mWifiConfigManager, never()).retrieveHiddenNetworkList();
+ mInOrder.verify(mWifiConfigManager, never()).retrieveHiddenNetworkList();
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID)));
+ validateScanSettings(mScanSettingsArgumentCaptor.getValue(), false);
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
}
/**
@@ -93,6 +140,175 @@
public void testStartScanWithHiddenNetworkScanningEnabled() {
mScanRequestProxy.enableScanningForHiddenNetworks(true);
assertTrue(mScanRequestProxy.startScan(TEST_UID));
- verify(mWifiConfigManager).retrieveHiddenNetworkList();
+ mInOrder.verify(mWifiConfigManager).retrieveHiddenNetworkList();
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+
+ assertTrue(mWorkSourceArgumentCaptor.getValue().equals(new WorkSource(TEST_UID)));
+ validateScanSettings(mScanSettingsArgumentCaptor.getValue(), true);
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Verify a successful scan request and processing of scan results.
+ */
+ @Test
+ public void testScanSuccess() {
+ // Make a scan request.
+ testStartScanSuccess();
+
+ // Verify the scan results processing.
+ mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
+ validateScanResultsAvailableBroadcastSent(true);
+
+ // Validate the scan results in the cache.
+ ScanTestUtil.assertScanResultsEquals(
+ mTestScanDatas1[0].getResults(),
+ mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Verify a successful scan request and processing of scan failure.
+ */
+ @Test
+ public void testScanFailure() {
+ // Make a scan request.
+ testStartScanSuccess();
+
+ // Verify the scan failure processing.
+ mScanListenerArgumentCaptor.getValue().onFailure(0, "failed");
+ validateScanResultsAvailableBroadcastSent(false);
+
+ // Ensure scan results in the cache is empty.
+ assertTrue(mScanRequestProxy.getScanResults().isEmpty());
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Verify processing of successive successful scans.
+ */
+ @Test
+ public void testScanSuccessOverwritesPreviousResults() {
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+ // Verify the scan results processing for request 1.
+ mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
+ validateScanResultsAvailableBroadcastSent(true);
+ // Validate the scan results in the cache.
+ ScanTestUtil.assertScanResultsEquals(
+ mTestScanDatas1[0].getResults(),
+ mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+
+ // Make scan request 2.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+ // Verify the scan results processing for request 2.
+ mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas2);
+ validateScanResultsAvailableBroadcastSent(true);
+ // Validate the scan results in the cache.
+ ScanTestUtil.assertScanResultsEquals(
+ mTestScanDatas2[0].getResults(),
+ mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Verify processing of a successful scan followed by a failure.
+ */
+ @Test
+ public void testScanFailureDoesNotOverwritePreviousResults() {
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+ // Verify the scan results processing for request 1.
+ mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
+ validateScanResultsAvailableBroadcastSent(true);
+ // Validate the scan results in the cache.
+ ScanTestUtil.assertScanResultsEquals(
+ mTestScanDatas1[0].getResults(),
+ mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+
+ // Make scan request 2.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+ // Verify the scan failure processing.
+ mScanListenerArgumentCaptor.getValue().onFailure(0, "failed");
+ validateScanResultsAvailableBroadcastSent(false);
+ // Validate the scan results from a previous successful scan in the cache.
+ ScanTestUtil.assertScanResultsEquals(
+ mTestScanDatas1[0].getResults(),
+ mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ /**
+ * Verify that clear scan results invocation clears all stored scan results.
+ */
+ @Test
+ public void testClearScanResults() {
+ // Make scan request 1.
+ assertTrue(mScanRequestProxy.startScan(TEST_UID));
+ mInOrder.verify(mWifiScanner).startScan(any(), any(), any());
+ // Verify the scan results processing for request 1.
+ mScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas1);
+ validateScanResultsAvailableBroadcastSent(true);
+ // Validate the scan results in the cache.
+ ScanTestUtil.assertScanResultsEquals(
+ mTestScanDatas1[0].getResults(),
+ mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+
+ mScanRequestProxy.clearScanResults();
+ assertTrue(mScanRequestProxy.getScanResults().isEmpty());
+ verifyNoMoreInteractions(mWifiScanner, mWifiConfigManager, mContext);
+ }
+
+ private void validateScanSettings(WifiScanner.ScanSettings scanSettings,
+ boolean expectHiddenNetworks) {
+ assertNotNull(scanSettings);
+ assertEquals(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, scanSettings.band);
+ assertEquals(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
+ | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, scanSettings.reportEvents);
+ if (expectHiddenNetworks) {
+ assertNotNull(scanSettings.hiddenNetworks);
+ assertEquals(TEST_HIDDEN_NETWORKS_LIST.size(), scanSettings.hiddenNetworks.length);
+ for (int i = 0; i < scanSettings.hiddenNetworks.length; i++) {
+ validateHiddenNetworkInList(scanSettings.hiddenNetworks[i],
+ TEST_HIDDEN_NETWORKS_LIST);
+ }
+ } else {
+ assertNull(scanSettings.hiddenNetworks);
+ }
+ }
+
+ private void validateHiddenNetworkInList(
+ WifiScanner.ScanSettings.HiddenNetwork expectedHiddenNetwork,
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList) {
+ for (WifiScanner.ScanSettings.HiddenNetwork hiddenNetwork : hiddenNetworkList) {
+ if (hiddenNetwork.ssid.equals(expectedHiddenNetwork.ssid)) {
+ return;
+ }
+ }
+ fail();
+ }
+
+ private void validateScanResultsAvailableBroadcastSent(boolean expectScanSuceeded) {
+ 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);
+ assertEquals(expectScanSuceeded, scanSucceeded);
}
}