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