SupplicantPnoScannerTest.java revision 1bf983a4211f547593a60523e43112ecdb5c8997
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.scanner;
18
19import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder;
20import static com.android.server.wifi.ScanTestUtil.assertScanDataEquals;
21
22import static org.junit.Assert.*;
23import static org.mockito.Mockito.*;
24
25import android.content.Context;
26import android.net.wifi.WifiConfiguration;
27import android.net.wifi.WifiScanner;
28import android.os.SystemClock;
29import android.test.suitebuilder.annotation.SmallTest;
30
31import com.android.internal.R;
32import com.android.server.wifi.Clock;
33import com.android.server.wifi.MockAlarmManager;
34import com.android.server.wifi.MockLooper;
35import com.android.server.wifi.MockResources;
36import com.android.server.wifi.MockWifiMonitor;
37import com.android.server.wifi.ScanResults;
38import com.android.server.wifi.WifiMonitor;
39import com.android.server.wifi.WifiNative;
40import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
41
42import org.junit.Before;
43import org.junit.Test;
44import org.mockito.InOrder;
45import org.mockito.Mock;
46import org.mockito.MockitoAnnotations;
47
48import java.util.Arrays;
49import java.util.HashSet;
50import java.util.Set;
51
52
53/**
54 * Unit tests for {@link com.android.server.wifi.scanner.SupplicantWifiScannerImpl.setPnoList}.
55 */
56@SmallTest
57public class SupplicantPnoScannerTest {
58
59    @Mock Context mContext;
60    MockAlarmManager mAlarmManager;
61    MockWifiMonitor mWifiMonitor;
62    MockLooper mLooper;
63    @Mock WifiNative mWifiNative;
64    MockResources mResources;
65    @Mock Clock mClock;
66    SupplicantWifiScannerImpl mScanner;
67
68    @Before
69    public void setup() throws Exception {
70        MockitoAnnotations.initMocks(this);
71
72        mLooper = new MockLooper();
73        mAlarmManager = new MockAlarmManager();
74        mWifiMonitor = new MockWifiMonitor();
75        mResources = new MockResources();
76
77        when(mWifiNative.getInterfaceName()).thenReturn("a_test_interface_name");
78        when(mContext.getSystemService(Context.ALARM_SERVICE))
79                .thenReturn(mAlarmManager.getAlarmManager());
80        when(mContext.getResources()).thenReturn(mResources);
81        when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
82    }
83
84    /**
85     * Verify that the HW disconnected PNO scan triggers a supplicant PNO scan and invokes the
86     * OnPnoNetworkFound callback when the scan results are received.
87     */
88    @Test
89    public void startHwDisconnectedPnoScan() {
90        createScannerWithHwPnoScanSupport();
91
92        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
93        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
94        ScanResults scanResults = createDummyScanResults();
95
96        InOrder order = inOrder(pnoEventHandler, mWifiNative);
97        // Start PNO scan
98        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
99        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
100        verifyNoMoreInteractions(pnoEventHandler);
101    }
102
103    /**
104     * Verify that we pause & resume HW PNO scan when a single scan is scheduled and invokes the
105     * OnPnoNetworkFound callback when the scan results are received.
106     */
107    @Test
108    public void pauseResumeHwDisconnectedPnoScanForSingleScan() {
109        createScannerWithHwPnoScanSupport();
110
111        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
112        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
113        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
114        WifiNative.ScanSettings settings = createDummyScanSettings();
115        ScanResults scanResults = createDummyScanResults();
116
117        InOrder order = inOrder(eventHandler, mWifiNative);
118        // Start PNO scan
119        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
120        // Start single scan
121        assertTrue(mScanner.startSingleScan(settings, eventHandler));
122        // Verify that the PNO scan was paused and single scan runs successfully
123        expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
124                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(),
125                scanResults);
126        verifyNoMoreInteractions(eventHandler);
127
128        order = inOrder(pnoEventHandler, mWifiNative);
129        // Resume PNO scan after the single scan results are received and PNO monitor debounce
130        // alarm fires.
131        assertTrue("dispatch pno monitor alarm",
132                mAlarmManager.dispatch(
133                        SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
134        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
135        // Now verify that PNO scan is resumed successfully
136        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
137        verifyNoMoreInteractions(pnoEventHandler);
138    }
139
140    /**
141     * Verify that the SW disconnected PNO scan triggers a background scan and invokes the
142     * background scan callbacks when scan results are received.
143     */
144    @Test
145    public void startSwDisconnectedPnoScan() {
146        createScannerWithSwPnoScanSupport();
147        doSuccessfulSwPnoScanTest(false);
148    }
149
150    /**
151     * Verify that the HW connected PNO scan triggers a background scan and invokes the
152     * background scan callbacks when scan results are received.
153     */
154    @Test
155    public void startHwConnectedPnoScan() {
156        createScannerWithHwPnoScanSupport();
157        doSuccessfulSwPnoScanTest(true);
158    }
159
160    /**
161     * Verify that the SW connected PNO scan triggers a background scan and invokes the
162     * background scan callbacks when scan results are received.
163     */
164    @Test
165    public void startSwConnectedPnoScan() {
166        createScannerWithSwPnoScanSupport();
167        doSuccessfulSwPnoScanTest(true);
168    }
169
170    /**
171     * Verify that the HW PNO delayed failure cleans up the scan settings cleanly.
172     * 1. Start Hw PNO.
173     * 2. Start Single Scan which should pause PNO scan.
174     * 3. Fail the PNO scan resume and verify that the OnPnoScanFailed callback is invoked.
175     * 4. Now restart a new PNO scan to ensure that the failure was cleanly handled.
176     */
177    @Test
178    public void delayedHwDisconnectedPnoScanFailure() {
179        createScannerWithHwPnoScanSupport();
180
181        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
182        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
183        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
184        WifiNative.ScanSettings settings = createDummyScanSettings();
185        ScanResults scanResults = createDummyScanResults();
186
187        InOrder order = inOrder(eventHandler, mWifiNative);
188        // Start PNO scan
189        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
190        // Start single scan
191        assertTrue(mScanner.startSingleScan(settings, eventHandler));
192        // Verify that the PNO scan was paused and single scan runs successfully
193        expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
194                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(),
195                scanResults);
196        verifyNoMoreInteractions(eventHandler);
197
198        // Fail the PNO resume and check that the OnPnoScanFailed callback is invoked.
199        order = inOrder(pnoEventHandler, mWifiNative);
200        when(mWifiNative.setPnoScan(true)).thenReturn(false);
201        assertTrue("dispatch pno monitor alarm",
202                mAlarmManager.dispatch(
203                        SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
204        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
205        order.verify(pnoEventHandler).onPnoScanFailed();
206        verifyNoMoreInteractions(pnoEventHandler);
207
208        // Add a new PNO scan request
209        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
210        assertTrue("dispatch pno monitor alarm",
211                mAlarmManager.dispatch(
212                        SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
213        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
214        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
215        verifyNoMoreInteractions(pnoEventHandler);
216    }
217
218    /**
219     * Verify that the HW PNO scan stop failure still resets the PNO scan state.
220     * 1. Start Hw PNO.
221     * 2. Stop Hw PNO scan which raises a stop command to WifiNative which is failed.
222     * 3. Now restart a new PNO scan to ensure that the failure was cleanly handled.
223     */
224    @Test
225    public void ignoreHwDisconnectedPnoScanStopFailure() {
226        createScannerWithHwPnoScanSupport();
227
228        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
229        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
230
231        // Start PNO scan
232        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
233
234        // Fail the PNO stop.
235        when(mWifiNative.setPnoScan(false)).thenReturn(false);
236        assertTrue(mScanner.resetHwPnoList());
237        assertTrue("dispatch pno monitor alarm",
238                mAlarmManager.dispatch(
239                        SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
240        mLooper.dispatchAll();
241        verify(mWifiNative).setPnoScan(false);
242
243        // Add a new PNO scan request and ensure it runs successfully.
244        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
245        assertTrue("dispatch pno monitor alarm",
246                mAlarmManager.dispatch(
247                        SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
248        mLooper.dispatchAll();
249        InOrder order = inOrder(pnoEventHandler, mWifiNative);
250        ScanResults scanResults = createDummyScanResults();
251        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
252        verifyNoMoreInteractions(pnoEventHandler);
253    }
254
255    /**
256     * Verify that the HW PNO scan is forcefully stopped (bypass debounce logic) and restarted when
257     * settings change.
258     * 1. Start Hw PNO.
259     * 2. Stop Hw PNO .
260     * 3. Now restart a new PNO scan with different settings.
261     * 4. Ensure that the stop was issued before we start again.
262     */
263    @Test
264    public void forceRestartHwDisconnectedPnoScanWhenSettingsChange() {
265        createScannerWithHwPnoScanSupport();
266
267        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
268        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
269        InOrder order = inOrder(pnoEventHandler, mWifiNative);
270
271        // Start PNO scan
272        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
273        expectHwDisconnectedPnoScanStart(order, pnoSettings);
274
275        // Stop PNO now. This should trigger the debounce timer and not stop PNO.
276        assertTrue(mScanner.resetHwPnoList());
277        assertTrue(mAlarmManager.isPending(
278                SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
279        order.verify(mWifiNative, never()).setPnoScan(false);
280
281        // Now restart PNO scan with an extra network in settings.
282        pnoSettings.networkList =
283                Arrays.copyOf(pnoSettings.networkList, pnoSettings.networkList.length + 1);
284        pnoSettings.networkList[pnoSettings.networkList.length - 1] =
285                createDummyPnoNetwork("ssid_pno_new", 6, 6);
286        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
287
288        // This should bypass the debounce timer and stop PNO scan immediately and then start
289        // a new debounce timer for the start.
290        order.verify(mWifiNative).setPnoScan(false);
291
292        // Trigger the debounce timer and ensure we start PNO scan again.
293        mAlarmManager.dispatch(SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG);
294        mLooper.dispatchAll();
295        order.verify(mWifiNative).setPnoScan(true);
296    }
297
298    /**
299     * Verify that the HW PNO scan is not forcefully stopped (bypass debounce logic) when
300     * settings don't change.
301     * 1. Start Hw PNO.
302     * 2. Stop Hw PNO .
303     * 3. Now restart a new PNO scan with same settings.
304     * 4. Ensure that the stop was never issued.
305     */
306    @Test
307    public void noForceRestartHwDisconnectedPnoScanWhenNoSettingsChange() {
308        createScannerWithHwPnoScanSupport();
309
310        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
311        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
312        InOrder order = inOrder(pnoEventHandler, mWifiNative);
313
314        // Start PNO scan
315        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
316        expectHwDisconnectedPnoScanStart(order, pnoSettings);
317
318        // Stop PNO now. This should trigger the debounce timer and not stop PNO.
319        assertTrue(mScanner.resetHwPnoList());
320        assertTrue(mAlarmManager.isPending(
321                SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
322        order.verify(mWifiNative, never()).setPnoScan(false);
323
324        // Now restart PNO scan with the same settings.
325        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
326
327        // Trigger the debounce timer and ensure that we neither stop/start.
328        mLooper.dispatchAll();
329        order.verify(mWifiNative, never()).setPnoScan(anyBoolean());
330    }
331
332    private void doSuccessfulSwPnoScanTest(boolean isConnectedPno) {
333        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
334        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(isConnectedPno);
335        WifiNative.ScanEventHandler scanEventHandler = mock(WifiNative.ScanEventHandler.class);
336        WifiNative.ScanSettings scanSettings = createDummyScanSettings();
337        ScanResults scanResults = createDummyScanResults();
338
339        InOrder order = inOrder(scanEventHandler, mWifiNative);
340
341        // Start PNO scan
342        startSuccessfulPnoScan(scanSettings, pnoSettings, scanEventHandler, pnoEventHandler);
343
344        expectSuccessfulSwPnoScan(order, scanEventHandler, scanResults);
345
346        verifyNoMoreInteractions(pnoEventHandler);
347    }
348
349    private void createScannerWithHwPnoScanSupport() {
350        mResources.setBoolean(R.bool.config_wifi_background_scan_support, true);
351        mScanner =
352                new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock);
353    }
354
355    private void createScannerWithSwPnoScanSupport() {
356        mResources.setBoolean(R.bool.config_wifi_background_scan_support, false);
357        mScanner =
358                new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock);
359    }
360
361    private WifiNative.PnoNetwork createDummyPnoNetwork(String ssid, int networkId, int priority) {
362        WifiNative.PnoNetwork pnoNetwork = new WifiNative.PnoNetwork();
363        pnoNetwork.ssid = ssid;
364        pnoNetwork.networkId = networkId;
365        pnoNetwork.priority = priority;
366        return pnoNetwork;
367    }
368
369    private WifiNative.PnoSettings createDummyPnoSettings(boolean isConnected) {
370        WifiNative.PnoSettings pnoSettings = new WifiNative.PnoSettings();
371        pnoSettings.isConnected = isConnected;
372        pnoSettings.networkList = new WifiNative.PnoNetwork[2];
373        pnoSettings.networkList[0] = createDummyPnoNetwork("ssid_pno_1", 1, 1);
374        pnoSettings.networkList[1] = createDummyPnoNetwork("ssid_pno_2", 2, 2);
375        return pnoSettings;
376    }
377
378    private WifiNative.ScanSettings createDummyScanSettings() {
379        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
380                .withBasePeriod(10000)
381                .withMaxApPerScan(10)
382                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
383                        WifiScanner.WIFI_BAND_24_GHZ)
384                .build();
385        return settings;
386    }
387
388    private ScanResults createDummyScanResults() {
389        return ScanResults.create(0, 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450);
390    }
391
392    private void startSuccessfulPnoScan(WifiNative.ScanSettings scanSettings,
393            WifiNative.PnoSettings pnoSettings, WifiNative.ScanEventHandler scanEventHandler,
394            WifiNative.PnoEventHandler pnoEventHandler) {
395        reset(mWifiNative);
396        when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true);
397        when(mWifiNative.enableNetworkWithoutConnect(anyInt())).thenReturn(true);
398        // Scans succeed
399        when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
400        when(mWifiNative.setPnoScan(anyBoolean())).thenReturn(true);
401
402        if (mScanner.isHwPnoSupported(pnoSettings.isConnected)) {
403            // This should happen only for HW PNO scan
404            assertTrue(mScanner.setHwPnoList(pnoSettings, pnoEventHandler));
405        } else {
406            // This should happen only for SW PNO scan
407            assertTrue(mScanner.startBatchedScan(scanSettings, scanEventHandler));
408
409        }
410    }
411
412    private Set<Integer> expectedBandScanFreqs(int band) {
413        ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection();
414        collection.addBand(band);
415        return collection.getSupplicantScanFreqs();
416    }
417
418    /**
419     * Verify that the PNO scan was successfully started.
420     */
421    private void expectHwDisconnectedPnoScanStart(InOrder order,
422            WifiNative.PnoSettings pnoSettings) {
423        for (int i = 0; i < pnoSettings.networkList.length; i++) {
424            WifiNative.PnoNetwork network = pnoSettings.networkList[i];
425            order.verify(mWifiNative).setNetworkVariable(network.networkId,
426                    WifiConfiguration.priorityVarName, Integer.toString(network.priority));
427            order.verify(mWifiNative).enableNetworkWithoutConnect(network.networkId);
428        }
429        // Verify  HW PNO scan started
430        order.verify(mWifiNative).setPnoScan(true);
431    }
432
433    /**
434     *
435     * 1. Verify that the PNO scan was successfully started.
436     * 2. Send scan results and ensure that the |onPnoNetworkFound| callback was called.
437     */
438    private void expectSuccessfulHwDisconnectedPnoScan(InOrder order,
439            WifiNative.PnoSettings pnoSettings, WifiNative.PnoEventHandler eventHandler,
440            ScanResults scanResults) {
441        expectHwDisconnectedPnoScanStart(order, pnoSettings);
442
443        // Setup scan results
444        when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
445
446        // Notify scan has finished
447        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
448        assertEquals("dispatch message after results event", 1, mLooper.dispatchAll());
449
450        order.verify(eventHandler).onPnoNetworkFound(scanResults.getRawScanResults());
451    }
452
453    /**
454     * Verify that the single scan results were delivered and that the PNO scan was paused and
455     * resumed either side of it.
456     */
457    private void expectSuccessfulSingleScanWithHwPnoEnabled(InOrder order,
458            WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScanFreqs,
459            Set<Integer> expectedHiddenNetIds, ScanResults scanResults) {
460        // Pause PNO scan first
461        order.verify(mWifiNative).setPnoScan(false);
462
463        order.verify(mWifiNative).scan(eq(expectedScanFreqs), eq(expectedHiddenNetIds));
464
465        when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
466
467        // Notify scan has finished
468        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
469        assertEquals("dispatch message after results event", 1, mLooper.dispatchAll());
470
471        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
472        assertScanDataEquals(scanResults.getScanData(), mScanner.getLatestSingleScanResults());
473    }
474
475    /**
476     * Verify that the SW PNO scan was successfully started. This could either be disconnected
477     * or connected PNO.
478     * This is basically ensuring that the background scan runs successfully and returns the
479     * expected result.
480     */
481    private void expectSuccessfulSwPnoScan(InOrder order,
482            WifiNative.ScanEventHandler eventHandler, ScanResults scanResults) {
483
484        // Verify scan started
485        order.verify(mWifiNative).scan(any(Set.class), any(Set.class));
486
487        // Make sure that HW PNO scan was not started
488        verify(mWifiNative, never()).setPnoScan(anyBoolean());
489
490        // Setup scan results
491        when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
492
493        // Notify scan has finished
494        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
495        assertEquals("dispatch message after results event", 1, mLooper.dispatchAll());
496
497        // Verify background scan results delivered
498        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
499        WifiScanner.ScanData[] scanData = mScanner.getLatestBatchedScanResults(true);
500        WifiScanner.ScanData lastScanData = scanData[scanData.length -1];
501        assertScanDataEquals(scanResults.getScanData(), lastScanData);
502    }
503}
504