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