1/*
2 * Copyright (C) 2015 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;
21import static com.android.server.wifi.ScanTestUtil.createFreqSet;
22
23import static org.junit.Assert.*;
24import static org.mockito.Mockito.*;
25
26import android.app.test.TestAlarmManager;
27import android.content.Context;
28import android.net.wifi.ScanResult;
29import android.net.wifi.WifiScanner;
30import android.net.wifi.WifiScanner.ScanData;
31import android.net.wifi.WifiSsid;
32import android.os.SystemClock;
33import android.os.test.TestLooper;
34
35import com.android.server.wifi.Clock;
36import com.android.server.wifi.MockResources;
37import com.android.server.wifi.MockWifiMonitor;
38import com.android.server.wifi.ScanDetail;
39import com.android.server.wifi.ScanResults;
40import com.android.server.wifi.WifiMonitor;
41import com.android.server.wifi.WifiNative;
42import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
43
44import org.junit.Before;
45import org.junit.Test;
46import org.mockito.InOrder;
47import org.mockito.Mock;
48import org.mockito.MockitoAnnotations;
49
50import java.util.ArrayList;
51import java.util.Arrays;
52import java.util.Collections;
53import java.util.HashSet;
54import java.util.Set;
55
56/**
57 * Base unit tests that should pass for all implementations of
58 * {@link com.android.server.wifi.scanner.WifiScannerImpl}.
59 */
60public abstract class BaseWifiScannerImplTest {
61    @Mock Context mContext;
62    TestAlarmManager mAlarmManager;
63    MockWifiMonitor mWifiMonitor;
64    TestLooper mLooper;
65    @Mock WifiNative mWifiNative;
66    MockResources mResources;
67    @Mock Clock mClock;
68
69    /**
70     * mScanner implementation should be filled in by derived test class
71     */
72    WifiScannerImpl mScanner;
73
74    @Before
75    public void setUpBase() throws Exception {
76        MockitoAnnotations.initMocks(this);
77
78        mLooper = new TestLooper();
79        mAlarmManager = new TestAlarmManager();
80        mWifiMonitor = new MockWifiMonitor();
81        mResources = new MockResources();
82
83        when(mWifiNative.getInterfaceName()).thenReturn("a_test_interface_name");
84
85        when(mContext.getSystemService(Context.ALARM_SERVICE))
86                .thenReturn(mAlarmManager.getAlarmManager());
87
88        when(mContext.getResources()).thenReturn(mResources);
89        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
90    }
91
92    protected boolean isAllChannelsScanned(int band) {
93        ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection();
94        collection.addBand(band);
95        return collection.isAllChannels();
96    }
97
98    protected Set<Integer> expectedBandScanFreqs(int band) {
99        ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection();
100        collection.addBand(band);
101        return collection.getScanFreqs();
102    }
103
104    protected Set<Integer> expectedBandAndChannelScanFreqs(int band, int... channels) {
105        ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection();
106        collection.addBand(band);
107        for (int channel : channels) {
108            collection.addChannel(channel);
109        }
110        return collection.getScanFreqs();
111    }
112
113    @Test
114    public void singleScanSuccess() {
115        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
116                .withBasePeriod(10000) // ms
117                .withMaxApPerScan(10)
118                .addBucketWithBand(10000 /* ms */, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
119                        WifiScanner.WIFI_BAND_24_GHZ)
120                .build();
121
122        doSuccessfulSingleScanTest(settings, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ),
123                new HashSet<String>(),
124                ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
125                        2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), false);
126    }
127
128    @Test
129    public void singleScanSuccessWithChannels() {
130        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
131                .withBasePeriod(10000)
132                .withMaxApPerScan(10)
133                .addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5650)
134                .build();
135
136        doSuccessfulSingleScanTest(settings, createFreqSet(5650),
137                new HashSet<String>(),
138                ScanResults.create(0, 5650, 5650, 5650, 5650, 5650, 5650, 5650, 5650), false);
139    }
140
141    @Test
142    public void singleScanSuccessWithFullResults() {
143        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
144                .withBasePeriod(10000)
145                .withMaxApPerScan(10)
146                .addBucketWithBand(10000,
147                        WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
148                        | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
149                        WifiScanner.WIFI_BAND_24_GHZ)
150                .build();
151
152        doSuccessfulSingleScanTest(settings, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ),
153                new HashSet<String>(),
154                ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
155                        2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), true);
156    }
157
158    /**
159     * Tests whether the provided hidden networkId's in scan settings is correctly passed along
160     * when invoking native scan.
161     */
162    @Test
163    public void singleScanSuccessWithHiddenNetworkIds() {
164        String[] hiddenNetworkSSIDs = {"test_ssid_1", "test_ssid_2"};
165        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
166                .withBasePeriod(10000)
167                .withMaxApPerScan(10)
168                .withHiddenNetworkSSIDs(hiddenNetworkSSIDs)
169                .addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5650)
170                .build();
171
172        Set<String> hiddenNetworkSSIDSet = new HashSet<>();
173        for (int i = 0; i < hiddenNetworkSSIDs.length; i++) {
174            hiddenNetworkSSIDSet.add(hiddenNetworkSSIDs[i]);
175        }
176        doSuccessfulSingleScanTest(settings, createFreqSet(5650),
177                hiddenNetworkSSIDSet,
178                ScanResults.create(0, 5650, 5650, 5650, 5650, 5650, 5650, 5650, 5650), false);
179    }
180
181    /**
182     * Tests whether the provided hidden networkId's in scan settings is truncated to max size
183     * supported by wificond when invoking native scan.
184     */
185    @Test
186    public void singleScanSuccessWithTruncatedHiddenNetworkIds() {
187        String[] hiddenNetworkSSIDs = {
188                "test_ssid_0", "test_ssid_1", "test_ssid_2", "test_ssid_3", "test_ssid_4",
189                "test_ssid_5", "test_ssid_6", "test_ssid_7", "test_ssid_8", "test_ssid_9",
190                "test_ssid_10", "test_ssid_11", "test_ssid_12", "test_ssid_13", "test_ssid_14",
191                "test_ssid_15", "test_ssid_16", "test_ssid_17", "test_ssid_18", "test_ssid_19"
192        };
193        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
194                .withBasePeriod(10000)
195                .withMaxApPerScan(10)
196                .withHiddenNetworkSSIDs(hiddenNetworkSSIDs)
197                .addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5650)
198                .build();
199
200        Set<String> hiddenNetworkSSIDSet = new HashSet<>();
201        for (int i = 0; i < WificondScannerImpl.MAX_HIDDEN_NETWORK_IDS_PER_SCAN; i++) {
202            hiddenNetworkSSIDSet.add(hiddenNetworkSSIDs[i]);
203        }
204        doSuccessfulSingleScanTest(settings, createFreqSet(5650),
205                hiddenNetworkSSIDSet,
206                ScanResults.create(0, 5650, 5650, 5650, 5650, 5650, 5650, 5650, 5650), false);
207    }
208
209    @Test
210    public void overlappingSingleScanFails() {
211        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
212                .withBasePeriod(10000) // ms
213                .withMaxApPerScan(10)
214                .addBucketWithBand(10000 /* ms */, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
215                        WifiScanner.WIFI_BAND_24_GHZ)
216                .build();
217        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
218
219        WifiNative.ScanSettings settings2 = new NativeScanSettingsBuilder()
220                .withBasePeriod(10000) // ms
221                .withMaxApPerScan(10)
222                .addBucketWithBand(10000 /* ms */, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
223                        WifiScanner.WIFI_BAND_5_GHZ)
224                .build();
225        WifiNative.ScanEventHandler eventHandler2 = mock(WifiNative.ScanEventHandler.class);
226
227        // scan start succeeds
228        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
229
230        assertTrue(mScanner.startSingleScan(settings, eventHandler));
231        assertFalse("second scan while first scan running should fail immediately",
232                mScanner.startSingleScan(settings2, eventHandler2));
233    }
234
235    @Test
236    public void singleScanFailOnExecute() {
237        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
238                .withBasePeriod(10000)
239                .withMaxApPerScan(10)
240                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
241                        WifiScanner.WIFI_BAND_24_GHZ)
242                .build();
243
244        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
245
246        ScanResults results = ScanResults.create(0, 2400, 2450, 2450);
247
248        InOrder order = inOrder(eventHandler, mWifiNative);
249
250        // scan fails
251        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(false);
252
253        // start scan
254        assertTrue(mScanner.startSingleScan(settings, eventHandler));
255
256        mLooper.dispatchAll();
257        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_FAILED);
258
259        verifyNoMoreInteractions(eventHandler);
260    }
261
262    /**
263     * Test that a scan failure is reported if a scan times out
264     */
265    @Test
266    public void singleScanFailOnTimeout() {
267        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
268                .withBasePeriod(10000)
269                .withMaxApPerScan(10)
270                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
271                        WifiScanner.WIFI_BAND_24_GHZ)
272                .build();
273
274        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
275
276        ScanResults results = ScanResults.create(0, 2400, 2450, 2450);
277
278        InOrder order = inOrder(eventHandler, mWifiNative);
279
280        // scan succeeds
281        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
282
283        // start scan
284        assertTrue(mScanner.startSingleScan(settings, eventHandler));
285        mLooper.dispatchAll();
286
287        // Fire timeout
288        mAlarmManager.dispatch(WificondScannerImpl.TIMEOUT_ALARM_TAG);
289        mLooper.dispatchAll();
290
291        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_FAILED);
292
293        verifyNoMoreInteractions(eventHandler);
294    }
295
296    /**
297     * Test that a scan failure is reported if wificond sends a scan failed event
298     */
299    @Test
300    public void singleScanFailOnFailedEvent() {
301        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
302                .withBasePeriod(10000)
303                .withMaxApPerScan(10)
304                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
305                        WifiScanner.WIFI_BAND_24_GHZ)
306                .build();
307
308        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
309
310        ScanResults results = ScanResults.create(0, 2400, 2450, 2450);
311
312        InOrder order = inOrder(eventHandler, mWifiNative);
313
314        // scan succeeds
315        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
316
317        // start scan
318        assertTrue(mScanner.startSingleScan(settings, eventHandler));
319        mLooper.dispatchAll();
320
321        // Fire failed event
322        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_FAILED_EVENT);
323        mLooper.dispatchAll();
324
325        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_FAILED);
326
327        verifyNoMoreInteractions(eventHandler);
328    }
329
330    @Test
331    public void singleScanNullEventHandler() {
332        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
333                .withBasePeriod(10000)
334                .withMaxApPerScan(10)
335                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
336                        WifiScanner.WIFI_BAND_24_GHZ)
337                .build();
338        assertFalse(mScanner.startSingleScan(settings, null));
339    }
340
341    @Test
342    public void singleScanNullSettings() {
343        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
344
345        assertFalse(mScanner.startSingleScan(null, eventHandler));
346
347        verifyNoMoreInteractions(eventHandler);
348    }
349
350    @Test
351    public void multipleSingleScanSuccess() {
352        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
353                .withBasePeriod(10000)
354                .withMaxApPerScan(10)
355                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
356                        WifiScanner.WIFI_BAND_24_GHZ)
357                .build();
358        WifiNative.ScanSettings settings2 = new NativeScanSettingsBuilder()
359                .withBasePeriod(10000)
360                .withMaxApPerScan(10)
361                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
362                        WifiScanner.WIFI_BAND_BOTH_WITH_DFS)
363                .build();
364
365        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
366        InOrder order = inOrder(eventHandler, mWifiNative);
367
368        // scans succeed
369        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
370
371        // start first scan
372        assertTrue(mScanner.startSingleScan(settings, eventHandler));
373
374        expectSuccessfulSingleScan(order, eventHandler,
375                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ),
376                new HashSet<String>(),
377                ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
378                        2400, 2450, 2450), false);
379
380        // start second scan
381        assertTrue(mScanner.startSingleScan(settings2, eventHandler));
382
383        expectSuccessfulSingleScan(order, eventHandler,
384                expectedBandScanFreqs(WifiScanner.WIFI_BAND_BOTH_WITH_DFS),
385                new HashSet<String>(),
386                ScanResults.create(0, true,
387                        5150, 5175), false);
388
389        verifyNoMoreInteractions(eventHandler);
390    }
391
392    /**
393     * Validate that scan results that are returned from wificond, which are timestamped prior to
394     * the start of the scan, are ignored.
395     */
396    @Test
397    public void singleScanWhereSupplicantReturnsSomeOldResults() {
398        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
399                .withBasePeriod(10000)
400                .withMaxApPerScan(2)
401                .addBucketWithBand(10000,
402                        WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
403                        | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
404                        WifiScanner.WIFI_BAND_24_GHZ)
405                .build();
406
407        long approxScanStartUs = mClock.getElapsedSinceBootMillis() * 1000;
408        ArrayList<ScanDetail> rawResults = new ArrayList<>(Arrays.asList(
409                        new ScanDetail(WifiSsid.createFromAsciiEncoded("TEST AP 1"),
410                                "00:00:00:00:00:00", "", -70, 2450,
411                                approxScanStartUs + 2000 * 1000, 0),
412                        new ScanDetail(WifiSsid.createFromAsciiEncoded("TEST AP 2"),
413                                "AA:BB:CC:DD:EE:FF", "", -66, 2400,
414                                approxScanStartUs + 2500 * 1000, 0),
415                        new ScanDetail(WifiSsid.createFromAsciiEncoded("TEST AP 3"),
416                                "00:00:00:00:00:00", "", -80, 2450,
417                                approxScanStartUs - 2000 * 1000, 0), // old result will be filtered
418                        new ScanDetail(WifiSsid.createFromAsciiEncoded("TEST AP 4"),
419                                "AA:BB:CC:11:22:33", "", -65, 2450,
420                                approxScanStartUs + 4000 * 1000, 0)));
421
422        ArrayList<ScanResult> fullResults = new ArrayList<>();
423        for (ScanDetail detail : rawResults) {
424            if (detail.getScanResult().timestamp > approxScanStartUs) {
425                fullResults.add(detail.getScanResult());
426            }
427        }
428        ArrayList<ScanResult> scanDataResults = new ArrayList<>(fullResults);
429        Collections.sort(scanDataResults, ScanResults.SCAN_RESULT_RSSI_COMPARATOR);
430        ScanData scanData = new ScanData(0, 0, 0,
431                isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
432                scanDataResults.toArray(new ScanResult[scanDataResults.size()]));
433        Set<Integer> expectedScan = expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ);
434
435        // Actual test
436
437        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
438
439        InOrder order = inOrder(eventHandler, mWifiNative);
440
441        // scan succeeds
442        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
443
444        // start scan
445        assertTrue(mScanner.startSingleScan(settings, eventHandler));
446
447        order.verify(mWifiNative).scan(eq(expectedScan), any(Set.class));
448
449        when(mWifiNative.getScanResults()).thenReturn(rawResults);
450
451        // Notify scan has finished
452        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
453
454        mLooper.dispatchAll();
455
456        for (ScanResult result : fullResults) {
457            order.verify(eventHandler).onFullScanResult(eq(result), eq(0));
458        }
459
460        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
461        assertScanDataEquals(scanData, mScanner.getLatestSingleScanResults());
462
463        verifyNoMoreInteractions(eventHandler);
464    }
465
466    @Test
467    public void backgroundScanNullEventHandler() {
468        WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
469                .withBasePeriod(10000)
470                .withMaxApPerScan(10)
471                .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN,
472                        WifiScanner.WIFI_BAND_24_GHZ)
473                .build();
474        assertFalse(mScanner.startBatchedScan(settings, null));
475    }
476
477    @Test
478    public void backgroundScanNullSettings() {
479        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
480
481        assertFalse(mScanner.startBatchedScan(null, eventHandler));
482
483        verifyNoMoreInteractions(eventHandler);
484    }
485
486    protected void doSuccessfulSingleScanTest(WifiNative.ScanSettings settings,
487            Set<Integer> expectedScan, Set<String> expectedHiddenNetSSIDs, ScanResults results,
488            boolean expectFullResults) {
489        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
490
491        InOrder order = inOrder(eventHandler, mWifiNative);
492
493        // scan succeeds
494        when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
495
496        // start scan
497        assertTrue(mScanner.startSingleScan(settings, eventHandler));
498
499        expectSuccessfulSingleScan(order, eventHandler, expectedScan, expectedHiddenNetSSIDs,
500                results, expectFullResults);
501
502        verifyNoMoreInteractions(eventHandler);
503    }
504
505    protected void expectSuccessfulSingleScan(InOrder order,
506            WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScan,
507            Set<String> expectedHiddenNetSSIDs, ScanResults results, boolean expectFullResults) {
508        order.verify(mWifiNative).scan(eq(expectedScan), eq(expectedHiddenNetSSIDs));
509
510        when(mWifiNative.getScanResults()).thenReturn(results.getScanDetailArrayList());
511
512        // Notify scan has finished
513        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
514
515        mLooper.dispatchAll();
516
517        if (expectFullResults) {
518            for (ScanResult result : results.getRawScanResults()) {
519                order.verify(eventHandler).onFullScanResult(eq(result), eq(0));
520            }
521        }
522
523        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
524        assertScanDataEquals(results.getScanData(), mScanner.getLatestSingleScanResults());
525    }
526}
527