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;
18
19import static org.junit.Assert.*;
20import static org.junit.Assume.*;
21import static org.mockito.Mockito.*;
22
23import android.net.wifi.ScanResult;
24import android.net.wifi.WifiScanner;
25import android.net.wifi.WifiScanner.ScanData;
26import android.net.wifi.WifiSsid;
27
28import org.hamcrest.Description;
29import org.hamcrest.Matcher;
30import org.hamcrest.TypeSafeDiagnosingMatcher;
31
32import java.util.Arrays;
33import java.util.HashSet;
34import java.util.Set;
35
36/**
37 * Utilities for testing Wifi Scanning
38 */
39public class ScanTestUtil {
40
41    public static void setupMockChannels(WifiNative wifiNative, int[] channels24, int[] channels5,
42            int[] channelsDfs) throws Exception {
43        when(wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ))
44                .thenReturn(channels24);
45        when(wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ))
46                .thenReturn(channels5);
47        when(wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY))
48                .thenReturn(channelsDfs);
49    }
50
51    public static WifiScanner.ScanSettings createRequest(WifiScanner.ChannelSpec[] channels,
52            int period, int batch, int bssidsPerScan, int reportEvents) {
53        WifiScanner.ScanSettings request = new WifiScanner.ScanSettings();
54        request.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
55        request.channels = channels;
56        request.periodInMs = period;
57        request.numBssidsPerScan = bssidsPerScan;
58        request.maxScansToCache = batch;
59        request.reportEvents = reportEvents;
60        return request;
61    }
62
63    public static WifiScanner.ScanSettings createRequest(int type, int band, int period, int batch,
64            int bssidsPerScan, int reportEvents) {
65        return createRequest(WifiScanner.TYPE_HIGH_ACCURACY, band, period, 0, 0,
66                batch, bssidsPerScan, reportEvents);
67    }
68
69    public static WifiScanner.ScanSettings createRequest(int band, int period, int batch,
70            int bssidsPerScan, int reportEvents) {
71        return createRequest(WifiScanner.TYPE_HIGH_ACCURACY, band, period, 0, 0, batch,
72                bssidsPerScan, reportEvents);
73    }
74
75    /**
76     * Create an exponential back off scan request if maxPeriod != period && maxPeriod != 0.
77     */
78    public static WifiScanner.ScanSettings createRequest(int type, int band, int period,
79            int maxPeriod, int stepCount, int batch, int bssidsPerScan, int reportEvents) {
80        WifiScanner.ScanSettings request = new WifiScanner.ScanSettings();
81        request.type = type;
82        request.band = band;
83        request.channels = null;
84        request.periodInMs = period;
85        request.maxPeriodInMs = maxPeriod;
86        request.stepCount = stepCount;
87        request.numBssidsPerScan = bssidsPerScan;
88        request.maxScansToCache = batch;
89        request.reportEvents = reportEvents;
90        return request;
91    }
92
93    /**
94     * Builder to create WifiNative.ScanSettings objects for testing
95     */
96    public static class NativeScanSettingsBuilder {
97        private final WifiNative.ScanSettings mSettings = new WifiNative.ScanSettings();
98        public NativeScanSettingsBuilder() {
99            mSettings.scanType = WifiNative.SCAN_TYPE_LOW_LATENCY;
100            mSettings.buckets = new WifiNative.BucketSettings[0];
101            mSettings.num_buckets = 0;
102            mSettings.report_threshold_percent = 100;
103        }
104
105        public NativeScanSettingsBuilder withType(int type) {
106            mSettings.scanType = type;
107            return this;
108        }
109        public NativeScanSettingsBuilder withBasePeriod(int basePeriod) {
110            mSettings.base_period_ms = basePeriod;
111            return this;
112        }
113        public NativeScanSettingsBuilder withMaxApPerScan(int maxAp) {
114            mSettings.max_ap_per_scan = maxAp;
115            return this;
116        }
117        public NativeScanSettingsBuilder withMaxScansToCache(int maxScans) {
118            mSettings.report_threshold_num_scans = maxScans;
119            return this;
120        }
121        public NativeScanSettingsBuilder withMaxPercentToCache(int percent) {
122            mSettings.report_threshold_percent = percent;
123            return this;
124        }
125
126        /**
127         * Add the provided hidden network SSIDs to scan request.
128         * @param networkSSIDs List of hidden network SSIDs
129         * @return builder object
130         */
131        public NativeScanSettingsBuilder withHiddenNetworkSSIDs(String[] networkSSIDs) {
132            mSettings.hiddenNetworks = new WifiNative.HiddenNetwork[networkSSIDs.length];
133            for (int i = 0; i < networkSSIDs.length; i++) {
134                mSettings.hiddenNetworks[i] = new WifiNative.HiddenNetwork();
135                mSettings.hiddenNetworks[i].ssid = networkSSIDs[i];
136            }
137            return this;
138        }
139
140        public NativeScanSettingsBuilder addBucketWithBand(
141                int period, int reportEvents, int band) {
142            WifiNative.BucketSettings bucket = new WifiNative.BucketSettings();
143            bucket.bucket = mSettings.num_buckets;
144            bucket.band = band;
145            bucket.period_ms = period;
146            bucket.report_events = reportEvents;
147            return addBucket(bucket);
148        }
149
150        public NativeScanSettingsBuilder addBucketWithChannels(
151                int period, int reportEvents, WifiScanner.ChannelSpec... channels) {
152            int[] channelFreqs = new int[channels.length];
153            for (int i = 0; i < channels.length; ++i) {
154                channelFreqs[i] = channels[i].frequency;
155            }
156            return addBucketWithChannels(period, reportEvents, channelFreqs);
157        }
158
159        public NativeScanSettingsBuilder addBucketWithChannels(
160                int period, int reportEvents, int... channels) {
161            WifiNative.BucketSettings bucket = new WifiNative.BucketSettings();
162            bucket.bucket = mSettings.num_buckets;
163            bucket.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
164            bucket.num_channels = channels.length;
165            bucket.channels = channelsToNativeSettings(channels);
166            bucket.period_ms = period;
167            bucket.report_events = reportEvents;
168            return addBucket(bucket);
169        }
170
171        public NativeScanSettingsBuilder addBucket(WifiNative.BucketSettings bucket) {
172            mSettings.buckets = Arrays.copyOf(mSettings.buckets, mSettings.num_buckets + 1);
173            mSettings.buckets[mSettings.num_buckets] = bucket;
174            mSettings.num_buckets = mSettings.num_buckets + 1;
175            return this;
176        }
177
178        public WifiNative.ScanSettings build() {
179            return mSettings;
180        }
181
182    }
183
184    private static int getNativeScanType(int type) {
185        switch(type) {
186            case WifiScanner.TYPE_LOW_LATENCY:
187                return WifiNative.SCAN_TYPE_LOW_LATENCY;
188            case WifiScanner.TYPE_LOW_POWER:
189                return WifiNative.SCAN_TYPE_LOW_POWER;
190            case WifiScanner.TYPE_HIGH_ACCURACY:
191                return WifiNative.SCAN_TYPE_HIGH_ACCURACY;
192            default:
193                fail();
194                return -1;
195        }
196    }
197
198    /**
199     * Compute the expected native scan settings that are expected for the given
200     * WifiScanner.ScanSettings.
201     */
202    public static WifiNative.ScanSettings computeSingleScanNativeSettings(
203            WifiScanner.ScanSettings requestSettings) {
204        int reportEvents = requestSettings.reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
205        NativeScanSettingsBuilder builder = new NativeScanSettingsBuilder()
206                .withBasePeriod(0)
207                .withMaxApPerScan(0)
208                .withMaxPercentToCache(0)
209                .withMaxScansToCache(0)
210                .withType(getNativeScanType(requestSettings.type));
211        if (requestSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
212            builder.addBucketWithChannels(0, reportEvents, requestSettings.channels);
213        } else {
214            builder.addBucketWithBand(0, reportEvents, requestSettings.band);
215        }
216
217        return builder.build();
218    }
219
220    /**
221     * Compute the expected native scan settings that are expected for the given channels.
222     */
223    public static WifiNative.ScanSettings createSingleScanNativeSettingsForChannels(
224            int reportEvents, WifiScanner.ChannelSpec... channels) {
225        return createSingleScanNativeSettingsForChannels(
226            WifiNative.SCAN_TYPE_LOW_LATENCY, reportEvents, channels);
227    }
228
229    /**
230     * Compute the expected native scan settings that are expected for the given channels & type.
231     */
232    public static WifiNative.ScanSettings createSingleScanNativeSettingsForChannels(
233            int nativeScanType, int reportEvents, WifiScanner.ChannelSpec... channels) {
234        int actualReportEvents = reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
235        return new NativeScanSettingsBuilder()
236                .withBasePeriod(0)
237                .withMaxApPerScan(0)
238                .withMaxPercentToCache(0)
239                .withMaxScansToCache(0)
240                .addBucketWithChannels(0, actualReportEvents, channels)
241                .withType(nativeScanType)
242                .build();
243    }
244
245    public static Set<Integer> createFreqSet(int... elements) {
246        Set<Integer> set = new HashSet<>();
247        for (int e : elements) {
248            set.add(e);
249        }
250        return set;
251    }
252
253    public static ScanResult createScanResult(int freq) {
254        return new ScanResult(WifiSsid.createFromAsciiEncoded("AN SSID"), "00:00:00:00:00:00", 0L,
255                -1, null, "", 0, freq, 0);
256    }
257
258    private static ScanData createScanData(int[] freqs, int bucketsScanned) {
259        ScanResult[] results = new ScanResult[freqs.length];
260        for (int i = 0; i < freqs.length; ++i) {
261            results[i] = createScanResult(freqs[i]);
262        }
263        return new ScanData(0, 0, bucketsScanned, false, results);
264    }
265
266    public static ScanData[] createScanDatas(int[][] freqs, int[] bucketsScanned) {
267        assumeTrue(freqs.length == bucketsScanned.length);
268        ScanData[] data = new ScanData[freqs.length];
269        for (int i = 0; i < freqs.length; ++i) {
270            data[i] = createScanData(freqs[i], bucketsScanned[i]);
271        }
272        return data;
273    }
274
275    public static ScanData[] createScanDatas(int[][] freqs) {
276        return createScanDatas(freqs, new int[freqs.length] /* defaults all 0 */);
277    }
278
279    private static void assertScanResultEquals(
280            String prefix, ScanResult expected, ScanResult actual) {
281        assertEquals(prefix + "SSID", expected.SSID, actual.SSID);
282        assertEquals(prefix + "wifiSsid", expected.wifiSsid.toString(), actual.wifiSsid.toString());
283        assertEquals(prefix + "BSSID", expected.BSSID, actual.BSSID);
284        assertEquals(prefix + "capabilities", expected.capabilities, actual.capabilities);
285        assertEquals(prefix + "level", expected.level, actual.level);
286        assertEquals(prefix + "frequency", expected.frequency, actual.frequency);
287        assertEquals(prefix + "timestamp", expected.timestamp, actual.timestamp);
288        assertEquals(prefix + "seen", expected.seen, actual.seen);
289    }
290
291    private static void assertScanResultsEquals(String prefix, ScanResult[] expected,
292            ScanResult[] actual) {
293        assertNotNull(prefix + "expected ScanResults was null", expected);
294        assertNotNull(prefix + "actual ScanResults was null", actual);
295        assertEquals(prefix + "results.length", expected.length, actual.length);
296        for (int j = 0; j < expected.length; ++j) {
297            ScanResult expectedResult = expected[j];
298            ScanResult actualResult = actual[j];
299            assertScanResultEquals(prefix + "results[" + j + "]", actualResult, expectedResult);
300        }
301    }
302
303    /**
304     * Asserts if the provided scan results are the same.
305     */
306    public static void assertScanResultEquals(ScanResult expected, ScanResult actual) {
307        assertScanResultEquals("", expected, actual);
308    }
309
310    /**
311     * Asserts if the provided scan result arrays are the same.
312     */
313    public static void assertScanResultsEquals(ScanResult[] expected, ScanResult[] actual) {
314        assertScanResultsEquals("", expected, actual);
315    }
316
317    private static void assertScanDataEquals(String prefix, ScanData expected, ScanData actual) {
318        assertNotNull(prefix + "expected ScanData was null", expected);
319        assertNotNull(prefix + "actual ScanData was null", actual);
320        assertEquals(prefix + "id", expected.getId(), actual.getId());
321        assertEquals(prefix + "flags", expected.getFlags(), actual.getFlags());
322        assertEquals(prefix + "all channels", expected.isAllChannelsScanned(),
323                actual.isAllChannelsScanned());
324        assertScanResultsEquals(prefix, expected.getResults(), actual.getResults());
325    }
326
327    public static void assertScanDataEquals(ScanData expected, ScanData actual) {
328        assertScanDataEquals("", expected, actual);
329    }
330
331    public static void assertScanDatasEquals(String prefix, ScanData[] expected, ScanData[] actual) {
332        assertNotNull("expected " + prefix + "ScanData[] was null", expected);
333        assertNotNull("actaul " + prefix + "ScanData[] was null", actual);
334        assertEquals(prefix + "ScanData.length", expected.length, actual.length);
335        for (int i = 0; i < expected.length; ++i) {
336            assertScanDataEquals(prefix + "ScanData[" + i + "].", expected[i], actual[i]);
337        }
338    }
339
340    public static void assertScanDatasEquals(ScanData[] expected, ScanData[] actual) {
341        assertScanDatasEquals("", expected, actual);
342    }
343
344    public static WifiScanner.ChannelSpec[] channelsToSpec(int... channels) {
345        WifiScanner.ChannelSpec[] channelSpecs = new WifiScanner.ChannelSpec[channels.length];
346        for (int i = 0; i < channels.length; ++i) {
347            channelSpecs[i] = new WifiScanner.ChannelSpec(channels[i]);
348        }
349        return channelSpecs;
350    }
351
352    public static void assertNativeScanSettingsEquals(WifiNative.ScanSettings expected,
353            WifiNative.ScanSettings actual) {
354        assertEquals("scan type", expected.scanType, actual.scanType);
355        assertEquals("bssids per scan", expected.max_ap_per_scan, actual.max_ap_per_scan);
356        assertEquals("scans to cache", expected.report_threshold_num_scans,
357                actual.report_threshold_num_scans);
358        assertEquals("percent to cache", expected.report_threshold_percent,
359                actual.report_threshold_percent);
360        assertEquals("base period", expected.base_period_ms, actual.base_period_ms);
361
362        assertEquals("number of buckets", expected.num_buckets, actual.num_buckets);
363        assertNotNull("buckets was null", actual.buckets);
364        for (int i = 0; i < expected.buckets.length; ++i) {
365            assertNotNull("buckets[" + i + "] was null", actual.buckets[i]);
366            assertEquals("buckets[" + i + "].period",
367                    expected.buckets[i].period_ms, actual.buckets[i].period_ms);
368            assertEquals("buckets[" + i + "].reportEvents",
369                    expected.buckets[i].report_events, actual.buckets[i].report_events);
370
371            assertEquals("buckets[" + i + "].band",
372                    expected.buckets[i].band, actual.buckets[i].band);
373            if (expected.buckets[i].band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
374                Set<Integer> expectedChannels = new HashSet<>();
375                for (WifiNative.ChannelSettings channel : expected.buckets[i].channels) {
376                    expectedChannels.add(channel.frequency);
377                }
378                Set<Integer> actualChannels = new HashSet<>();
379                for (WifiNative.ChannelSettings channel : actual.buckets[i].channels) {
380                    actualChannels.add(channel.frequency);
381                }
382                assertEquals("channels", expectedChannels, actualChannels);
383            } else {
384                // since num_channels and channels are ignored when band is not
385                // WifiScanner.WIFI_BAND_UNSPECIFIED just assert that there are no channels
386                // the band equality was already checked above
387                assertEquals("buckets[" + i + "].num_channels not 0", 0,
388                        actual.buckets[i].num_channels);
389                assertTrue("buckets[" + i + "].channels not null or empty",
390                        actual.buckets[i].channels == null
391                        || actual.buckets[i].channels.length == 0);
392            }
393        }
394    }
395
396    /**
397     * Asserts if the provided pno settings are the same.
398     */
399    public static void assertNativePnoSettingsEquals(WifiNative.PnoSettings expected,
400            WifiNative.PnoSettings actual) {
401        assertNotNull("expected was null", expected);
402        assertNotNull("actaul was null", actual);
403        assertEquals("min5GHzRssi", expected.min5GHzRssi, actual.min5GHzRssi);
404        assertEquals("min24GHzRssi", expected.min24GHzRssi, actual.min24GHzRssi);
405        assertEquals("initialScoreMax", expected.initialScoreMax, actual.initialScoreMax);
406        assertEquals("currentConnectionBonus", expected.currentConnectionBonus,
407                actual.currentConnectionBonus);
408        assertEquals("sameNetworkBonus", expected.sameNetworkBonus, actual.sameNetworkBonus);
409        assertEquals("secureBonus", expected.secureBonus, actual.secureBonus);
410        assertEquals("band5GHzBonus", expected.band5GHzBonus, actual.band5GHzBonus);
411        assertEquals("isConnected", expected.isConnected, actual.isConnected);
412        assertNotNull("expected networkList was null", expected.networkList);
413        assertNotNull("actual networkList was null", actual.networkList);
414        assertEquals("networkList.length", expected.networkList.length, actual.networkList.length);
415        for (int i = 0; i < expected.networkList.length; i++) {
416            assertEquals("networkList[" + i + "].ssid",
417                    expected.networkList[i].ssid, actual.networkList[i].ssid);
418            assertEquals("networkList[" + i + "].flags",
419                    expected.networkList[i].flags, actual.networkList[i].flags);
420            assertEquals("networkList[" + i + "].auth_bit_field",
421                    expected.networkList[i].auth_bit_field, actual.networkList[i].auth_bit_field);
422        }
423    }
424
425    /**
426     * Convert a list of channel frequencies to an array of equivalent WifiNative.ChannelSettings
427     */
428    public static WifiNative.ChannelSettings[] channelsToNativeSettings(int... channels) {
429        WifiNative.ChannelSettings[] channelSpecs = new WifiNative.ChannelSettings[channels.length];
430        for (int i = 0; i < channels.length; ++i) {
431            channelSpecs[i] = new WifiNative.ChannelSettings();
432            channelSpecs[i].frequency = channels[i];
433        }
434        return channelSpecs;
435    }
436
437    /**
438     * Matcher to check that a BucketSettings has the given band
439     */
440    public static Matcher<WifiNative.BucketSettings> bandIs(final int expectedBand) {
441        return new TypeSafeDiagnosingMatcher<WifiNative.BucketSettings>() {
442            @Override
443            public boolean matchesSafely(WifiNative.BucketSettings bucketSettings,
444                    Description mismatchDescription) {
445                if (bucketSettings.band != expectedBand) {
446                    mismatchDescription
447                            .appendText("did not have expected band ").appendValue(expectedBand)
448                            .appendText(", was ").appendValue(bucketSettings.band);
449                    return false;
450                } else {
451                    return true;
452                }
453            }
454
455            @Override
456            public void describeTo(final Description description) {
457                description.appendText("bucket band is ").appendValue(expectedBand);
458            }
459        };
460    }
461
462    /**
463     * Matcher to check that a BucketSettings has exactly the given channels
464     */
465    public static Matcher<WifiNative.BucketSettings> channelsAre(final int... expectedChannels) {
466        return new TypeSafeDiagnosingMatcher<WifiNative.BucketSettings>() {
467            @Override
468            public boolean matchesSafely(WifiNative.BucketSettings bucketSettings,
469                    Description mismatchDescription) {
470                if (bucketSettings.band != WifiScanner.WIFI_BAND_UNSPECIFIED) {
471                    mismatchDescription.appendText("did not have expected unspecified band, was ")
472                            .appendValue(bucketSettings.band);
473                    return false;
474                } else if (bucketSettings.num_channels != expectedChannels.length) {
475                    mismatchDescription
476                            .appendText("did not have expected num_channels ")
477                            .appendValue(expectedChannels.length)
478                            .appendText(", was ").appendValue(bucketSettings.num_channels);
479                    return false;
480                } else if (bucketSettings.channels == null) {
481                    mismatchDescription.appendText("had null channels array");
482                    return false;
483                } else if (bucketSettings.channels.length != expectedChannels.length) {
484                    mismatchDescription
485                            .appendText("did not have channels array length matching excepted ")
486                            .appendValue(expectedChannels.length)
487                            .appendText(", was ").appendValue(bucketSettings.channels.length);
488                    return false;
489                } else {
490                    Set<Integer> foundChannelsSet = new HashSet<>();
491                    for (int i = 0; i < bucketSettings.channels.length; ++i) {
492                        foundChannelsSet.add(bucketSettings.channels[i].frequency);
493                    }
494                    Set<Integer> expectedChannelsSet = new HashSet<>();
495                    for (int i = 0; i < expectedChannels.length; ++i) {
496                        expectedChannelsSet.add(expectedChannels[i]);
497                    }
498
499                    if (!foundChannelsSet.containsAll(expectedChannelsSet)
500                            || foundChannelsSet.size() != expectedChannelsSet.size()) {
501                        Set<Integer> extraChannelsSet = new HashSet<>(foundChannelsSet);
502                        extraChannelsSet.removeAll(expectedChannelsSet);
503                        expectedChannelsSet.removeAll(foundChannelsSet);
504                        mismatchDescription
505                                .appendText("does not contain expected channels ")
506                                .appendValue(expectedChannelsSet);
507                        if (extraChannelsSet.size() > 0) {
508                            mismatchDescription
509                                    .appendText(", but contains extra channels ")
510                                    .appendValue(extraChannelsSet);
511                        }
512                        return false;
513                    } else {
514                        return true;
515                    }
516                }
517            }
518
519            @Override
520            public void describeTo(final Description description) {
521                description.appendText("bucket channels are ").appendValue(expectedChannels);
522            }
523        };
524    }
525}
526