diff options
-rw-r--r-- | service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java | 61 | ||||
-rw-r--r-- | tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java | 257 |
2 files changed, 271 insertions, 47 deletions
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index 49172c135..ee0a060cb 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -418,6 +418,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final IdleState mIdleState = new IdleState(); private final ScanningState mScanningState = new ScanningState(); + private WifiNative.ScanSettings mActiveScanSettings = null; private RequestList<ScanSettings> mActiveScans = new RequestList<>(); private RequestList<ScanSettings> mPendingScans = new RequestList<>(); @@ -546,12 +547,24 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY); WorkSource workSource = scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY); - if (validateAndAddToScanQueue(ci, handler, scanSettings, workSource)) { + if (validateScanRequest(ci, handler, scanSettings, workSource)) { + logScanRequest("addSingleScanRequest", ci, handler, workSource, + scanSettings, null); replySucceeded(msg); + + // If there is an active scan that will fulfill the scan request then + // mark this request as an active scan, otherwise mark it pending. // If were not currently scanning then try to start a scan. Otherwise // this scan will be scheduled when transitioning back to IdleState // after finishing the current scan. - if (getCurrentState() != mScanningState) { + if (getCurrentState() == mScanningState) { + if (activeScanSatisfies(scanSettings)) { + mActiveScans.addRequest(ci, handler, workSource, scanSettings); + } else { + mPendingScans.addRequest(ci, handler, workSource, scanSettings); + } + } else { + mPendingScans.addRequest(ci, handler, workSource, scanSettings); tryToStartNewScan(); } } else { @@ -597,6 +610,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public void exit() { + mActiveScanSettings = null; try { mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource); } catch (RemoteException e) { @@ -638,7 +652,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - boolean validateAndAddToScanQueue(ClientInfo ci, int handler, ScanSettings settings, + boolean validateScanRequest(ClientInfo ci, int handler, ScanSettings settings, WorkSource workSource) { if (ci == null) { Log.d(TAG, "Failing single scan request ClientInfo not found " + handler); @@ -650,8 +664,43 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { return false; } } - logScanRequest("addSingleScanRequest", ci, handler, workSource, settings, null); - mPendingScans.addRequest(ci, handler, workSource, settings); + return true; + } + + boolean activeScanSatisfies(ScanSettings settings) { + if (mActiveScanSettings == null) { + return false; + } + + // there is always one bucket for a single scan + WifiNative.BucketSettings activeBucket = mActiveScanSettings.buckets[0]; + + // validate that all requested channels are being scanned + ChannelCollection activeChannels = mChannelHelper.createChannelCollection(); + activeChannels.addChannels(activeBucket); + if (!activeChannels.containsSettings(settings)) { + return false; + } + + // if the request is for a full scan, but there is no ongoing full scan + if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 + && (activeBucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) + == 0) { + return false; + } + + if (settings.hiddenNetworkIds != null) { + Set<Integer> activeHiddenNetworkIds = new HashSet<>(); + for (int id : mActiveScanSettings.hiddenNetworkIds) { + activeHiddenNetworkIds.add(id); + } + for (int id : settings.hiddenNetworkIds) { + if (!activeHiddenNetworkIds.contains(id)) { + return false; + } + } + } + return true; } @@ -710,6 +759,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { settings.buckets = new WifiNative.BucketSettings[] {bucketSettings}; if (mScannerImpl.startSingleScan(settings, this)) { + // store the active scan settings + mActiveScanSettings = settings; // swap pending and active scan requests RequestList<ScanSettings> tmp = mActiveScans; mActiveScans = mPendingScans; diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java index 128815322..529b42988 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java @@ -137,13 +137,13 @@ public class WifiScanningServiceTest { return controlChannel; } - private Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler) { + private static Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler) { ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); order.verify(handler).handleMessage(messageCaptor.capture()); return messageCaptor.getValue(); } - private Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler, + private static Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler, final int what) { CapturingMatcher<Message> messageMatcher = new CapturingMatcher<Message>() { public boolean matches(Object argument) { @@ -155,14 +155,14 @@ public class WifiScanningServiceTest { return messageMatcher.getLastValue(); } - private void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId, + private static void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId, WifiScanner.ScanData... expected) { Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler, WifiScanner.CMD_SCAN_RESULT); assertScanResultsMessage(listenerId, expected, scanResultMessage); } - private void assertScanResultsMessage(int listenerId, WifiScanner.ScanData[] expected, + private static void assertScanResultsMessage(int listenerId, WifiScanner.ScanData[] expected, Message scanResultMessage) { assertEquals("what", WifiScanner.CMD_SCAN_RESULT, scanResultMessage.what); assertEquals("listenerId", listenerId, scanResultMessage.arg2); @@ -170,18 +170,19 @@ public class WifiScanningServiceTest { ((WifiScanner.ParcelableScanData) scanResultMessage.obj).getResults()); } - private void verifySingleScanCompletedRecieved(InOrder order, Handler handler, int listenerId) { + private static void verifySingleScanCompletedRecieved(InOrder order, Handler handler, + int listenerId) { Message completedMessage = verifyHandleMessageAndGetMessage(order, handler, WifiScanner.CMD_SINGLE_SCAN_COMPLETED); assertSingleScanCompletedMessage(listenerId, completedMessage); } - private void assertSingleScanCompletedMessage(int listenerId, Message completedMessage) { + private static void assertSingleScanCompletedMessage(int listenerId, Message completedMessage) { assertEquals("what", WifiScanner.CMD_SINGLE_SCAN_COMPLETED, completedMessage.what); assertEquals("listenerId", listenerId, completedMessage.arg2); } - private void sendBackgroundScanRequest(BidirectionalAsyncChannel controlChannel, + private static void sendBackgroundScanRequest(BidirectionalAsyncChannel controlChannel, int scanRequestId, WifiScanner.ScanSettings settings, WorkSource workSource) { Bundle scanParams = new Bundle(); scanParams.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); @@ -190,7 +191,7 @@ public class WifiScanningServiceTest { scanRequestId, scanParams)); } - private void sendSingleScanRequest(BidirectionalAsyncChannel controlChannel, + private static void sendSingleScanRequest(BidirectionalAsyncChannel controlChannel, int scanRequestId, WifiScanner.ScanSettings settings, WorkSource workSource) { Bundle scanParams = new Bundle(); scanParams.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); @@ -199,12 +200,12 @@ public class WifiScanningServiceTest { scanRequestId, scanParams)); } - private void verifySuccessfulResponse(InOrder order, Handler handler, int arg2) { + private static void verifySuccessfulResponse(InOrder order, Handler handler, int arg2) { Message response = verifyHandleMessageAndGetMessage(order, handler); assertSuccessfulResponse(arg2, response); } - private void assertSuccessfulResponse(int arg2, Message response) { + private static void assertSuccessfulResponse(int arg2, Message response) { if (response.what == WifiScanner.CMD_OP_FAILED) { WifiScanner.OperationResult result = (WifiScanner.OperationResult) response.obj; fail("response indicates failure, reason=" + result.reason @@ -215,13 +216,47 @@ public class WifiScanningServiceTest { } } - private void verifyFailedResponse(InOrder order, Handler handler, int arg2, + /** + * If multiple results are expected for a single hardware scan then the order that they are + * dispatched is dependant on the order which they are iterated through internally. This + * function validates that the order is either one way or the other. + */ + private static void verifyMultipleSingleScanResults(InOrder handlerOrder, Handler handler, + int requestId1, ScanResults results1, int requestId2, ScanResults results2) { + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + handlerOrder.verify(handler, times(4)) + .handleMessage(messageCaptor.capture()); + int firstListenerId = messageCaptor.getAllValues().get(0).arg2; + assertTrue(firstListenerId + " was neither " + requestId2 + " nor " + requestId1, + firstListenerId == requestId2 || firstListenerId == requestId1); + if (firstListenerId == requestId2) { + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(1)); + assertScanResultsMessage(requestId1, + new WifiScanner.ScanData[] {results1.getScanData()}, + messageCaptor.getAllValues().get(2)); + assertSingleScanCompletedMessage(requestId1, messageCaptor.getAllValues().get(3)); + } else { + assertScanResultsMessage(requestId1, + new WifiScanner.ScanData[] {results1.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertSingleScanCompletedMessage(requestId1, messageCaptor.getAllValues().get(1)); + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(2)); + assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(3)); + } + } + + private static void verifyFailedResponse(InOrder order, Handler handler, int arg2, int expectedErrorReason, String expectedErrorDescription) { Message response = verifyHandleMessageAndGetMessage(order, handler); assertFailedResponse(arg2, expectedErrorReason, expectedErrorDescription, response); } - private void assertFailedResponse(int arg2, int expectedErrorReason, + private static void assertFailedResponse(int arg2, int expectedErrorReason, String expectedErrorDescription, Message response) { if (response.what == WifiScanner.CMD_OP_SUCCEEDED) { fail("response indicates success"); @@ -629,8 +664,8 @@ public class WifiScanningServiceTest { } /** - * Send a single scan request and then a second one before the first completes. - * Verify that both are scheduled and succeed. + * Send a single scan request and then a second one not satisfied by the first before the first + * completes. Verify that both are scheduled and succeed. */ @Test public void sendSingleScanRequestWhilePreviousScanRunning() { @@ -695,8 +730,8 @@ public class WifiScanningServiceTest { /** - * Send a single scan request and then two more before the first completes. - * Verify that the first completes and the second two are merged. + * Send a single scan request and then two more before the first completes. Neither are + * satisfied by the first scan. Verify that the first completes and the second two are merged. */ @Test public void sendMultipleSingleScanRequestWhilePreviousScanRunning() throws RemoteException { @@ -779,32 +814,8 @@ public class WifiScanningServiceTest { mLooper.dispatchAll(); - // unfortunatally the order that these events are dispatched is dependant on the order which - // they are iterated through internally - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - handlerOrder.verify(handler, times(4)).handleMessage(messageCaptor.capture()); - int firstListenerId = messageCaptor.getAllValues().get(0).arg2; - assertTrue(firstListenerId + " was neither " + requestId2 + " nor " + requestId3, - firstListenerId == requestId2 || firstListenerId == requestId3); - if (firstListenerId == requestId2) { - assertScanResultsMessage(requestId2, - new WifiScanner.ScanData[] {results2.getScanData()}, - messageCaptor.getAllValues().get(0)); - assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(1)); - assertScanResultsMessage(requestId3, - new WifiScanner.ScanData[] {results3.getScanData()}, - messageCaptor.getAllValues().get(2)); - assertSingleScanCompletedMessage(requestId3, messageCaptor.getAllValues().get(3)); - } else { - assertScanResultsMessage(requestId3, - new WifiScanner.ScanData[] {results3.getScanData()}, - messageCaptor.getAllValues().get(0)); - assertSingleScanCompletedMessage(requestId3, messageCaptor.getAllValues().get(1)); - assertScanResultsMessage(requestId2, - new WifiScanner.ScanData[] {results2.getScanData()}, - messageCaptor.getAllValues().get(2)); - assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(3)); - } + verifyMultipleSingleScanResults(handlerOrder, handler, requestId2, results2, requestId3, + results3); assertEquals(mWifiMetrics.getOneshotScanCount(), 3); assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 3); @@ -821,6 +832,168 @@ public class WifiScanningServiceTest { "results=" + results3.getRawScanResults().length); } + + /** + * Send a single scan request and then a second one satisfied by the first before the first + * completes. Verify that only one scan is scheduled. + */ + @Test + public void sendSingleScanRequestWhilePreviousScanRunningAndMergeIntoFirstScan() { + // Split by frequency to make it easier to determine which results each request is expecting + ScanResults results24GHz = ScanResults.create(0, 2400, 2400, 2400, 2450); + ScanResults results5GHz = ScanResults.create(0, 5150, 5150, 5175); + ScanResults resultsBoth = ScanResults.merge(results24GHz, results5GHz); + + WifiScanner.ScanSettings requestSettings1 = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId1 = 12; + ScanResults results1 = resultsBoth; + + WifiScanner.ScanSettings requestSettings2 = createRequest(WifiScanner.WIFI_BAND_24_GHZ, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId2 = 13; + ScanResults results2 = results24GHz; + + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder handlerOrder = inOrder(handler); + InOrder nativeOrder = inOrder(mWifiScannerImpl); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings1)); + verifySuccessfulResponse(handlerOrder, handler, requestId1); + + // Queue scan 2 (will be folded into ongoing scan) + sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // dispatch scan 1 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(resultsBoth.getScanData()); + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyMultipleSingleScanResults(handlerOrder, handler, requestId1, results1, requestId2, + results2); + + assertEquals(mWifiMetrics.getOneshotScanCount(), 2); + assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 2); + } + + /** + * Send a single scan request and then two more before the first completes, one of which is + * satisfied by the first scan. Verify that the first two complete together the second scan is + * just for the other scan. + */ + @Test + public void sendMultipleSingleScanRequestWhilePreviousScanRunningAndMergeOneIntoFirstScan() + throws RemoteException { + // Split by frequency to make it easier to determine which results each request is expecting + ScanResults results2400 = ScanResults.create(0, 2400, 2400, 2400); + ScanResults results2450 = ScanResults.create(0, 2450); + ScanResults results1and3 = ScanResults.merge(results2400, results2450); + + WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400, 2450), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId1 = 12; + WorkSource workSource1 = new WorkSource(1121); + ScanResults results1 = results1and3; + + WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450, 5175), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId2 = 13; + WorkSource workSource2 = new WorkSource(Binder.getCallingUid()); // don't explicitly set + ScanResults results2 = ScanResults.create(0, 2450, 5175, 2450); + + WifiScanner.ScanSettings requestSettings3 = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId3 = 15; + WorkSource workSource3 = new WorkSource(2292); + ScanResults results3 = results2400; + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder handlerOrder = inOrder(handler); + InOrder nativeOrder = inOrder(mWifiScannerImpl); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId1, requestSettings1, workSource1); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler1 = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings1)); + verifySuccessfulResponse(handlerOrder, handler, requestId1); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource1)); + + + // Queue scan 2 (will not run because previous is in progress) + // uses uid of calling process + sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // Queue scan 3 (will be merged into the active scan) + sendSingleScanRequest(controlChannel, requestId3, requestSettings3, workSource3); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId3); + + // dispatch scan 1 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results1and3.getScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyMultipleSingleScanResults(handlerOrder, handler, requestId1, results1, requestId3, + results3); + // only the requests know at the beginning of the scan get blamed + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource1)); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource2)); + + // now that the first scan completed we expect the second and third ones to start + WifiNative.ScanEventHandler eventHandler2 = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings2)); + + // dispatch scan 2 and 3 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results2.getScanData()); + eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + + verifyScanResultsRecieved(handlerOrder, handler, requestId2, results2.getScanData()); + verifySingleScanCompletedRecieved(handlerOrder, handler, requestId2); + assertEquals(mWifiMetrics.getOneshotScanCount(), 3); + assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 3); + + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource2)); + + assertDumpContainsRequestLog("addSingleScanRequest", requestId1); + assertDumpContainsRequestLog("addSingleScanRequest", requestId2); + assertDumpContainsRequestLog("addSingleScanRequest", requestId3); + assertDumpContainsCallbackLog("singleScanResults", requestId1, + "results=" + results1.getRawScanResults().length); + assertDumpContainsCallbackLog("singleScanResults", requestId2, + "results=" + results2.getRawScanResults().length); + assertDumpContainsCallbackLog("singleScanResults", requestId3, + "results=" + results3.getRawScanResults().length); + } + private void doSuccessfulBackgroundScan(WifiScanner.ScanSettings requestSettings, WifiNative.ScanSettings nativeSettings) { startServiceAndLoadDriver(); |