/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.mockito.Mockito.*; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiSsid; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeDiagnosingMatcher; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * Utilities for testing Wifi Scanning */ public class ScanTestUtil { public static void setupMockChannels(WifiNative wifiNative, int[] channels24, int[] channels5, int[] channelsDfs) throws Exception { when(wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ)) .thenReturn(channels24); when(wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ)) .thenReturn(channels5); when(wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY)) .thenReturn(channelsDfs); } public static WifiScanner.ScanSettings createRequest(WifiScanner.ChannelSpec[] channels, int period, int batch, int bssidsPerScan, int reportEvents) { WifiScanner.ScanSettings request = new WifiScanner.ScanSettings(); request.band = WifiScanner.WIFI_BAND_UNSPECIFIED; request.channels = channels; request.periodInMs = period; request.numBssidsPerScan = bssidsPerScan; request.maxScansToCache = batch; request.reportEvents = reportEvents; return request; } public static WifiScanner.ScanSettings createRequest(int band, int period, int batch, int bssidsPerScan, int reportEvents) { return createRequest(band, period, 0, 0, batch, bssidsPerScan, reportEvents); } /** * Create an exponential back off scan request if maxPeriod != period && maxPeriod != 0. */ public static WifiScanner.ScanSettings createRequest(int band, int period, int maxPeriod, int stepCount, int batch, int bssidsPerScan, int reportEvents) { WifiScanner.ScanSettings request = new WifiScanner.ScanSettings(); request.band = band; request.channels = null; request.periodInMs = period; request.maxPeriodInMs = maxPeriod; request.stepCount = stepCount; request.numBssidsPerScan = bssidsPerScan; request.maxScansToCache = batch; request.reportEvents = reportEvents; return request; } /** * Builder to create WifiNative.ScanSettings objects for testing */ public static class NativeScanSettingsBuilder { private final WifiNative.ScanSettings mSettings = new WifiNative.ScanSettings(); public NativeScanSettingsBuilder() { mSettings.buckets = new WifiNative.BucketSettings[0]; mSettings.num_buckets = 0; mSettings.report_threshold_percent = 100; } public NativeScanSettingsBuilder withBasePeriod(int basePeriod) { mSettings.base_period_ms = basePeriod; return this; } public NativeScanSettingsBuilder withMaxApPerScan(int maxAp) { mSettings.max_ap_per_scan = maxAp; return this; } public NativeScanSettingsBuilder withMaxScansToCache(int maxScans) { mSettings.report_threshold_num_scans = maxScans; return this; } public NativeScanSettingsBuilder withMaxPercentToCache(int percent) { mSettings.report_threshold_percent = percent; return this; } /** * Add the provided hidden network SSIDs to scan request. * @param networkSSIDs List of hidden network SSIDs * @return builder object */ public NativeScanSettingsBuilder withHiddenNetworkSSIDs(String[] networkSSIDs) { mSettings.hiddenNetworks = new WifiNative.HiddenNetwork[networkSSIDs.length]; for (int i = 0; i < networkSSIDs.length; i++) { mSettings.hiddenNetworks[i] = new WifiNative.HiddenNetwork(); mSettings.hiddenNetworks[i].ssid = networkSSIDs[i]; } return this; } public NativeScanSettingsBuilder addBucketWithBand( int period, int reportEvents, int band) { WifiNative.BucketSettings bucket = new WifiNative.BucketSettings(); bucket.bucket = mSettings.num_buckets; bucket.band = band; bucket.period_ms = period; bucket.report_events = reportEvents; return addBucket(bucket); } public NativeScanSettingsBuilder addBucketWithChannels( int period, int reportEvents, WifiScanner.ChannelSpec... channels) { int[] channelFreqs = new int[channels.length]; for (int i = 0; i < channels.length; ++i) { channelFreqs[i] = channels[i].frequency; } return addBucketWithChannels(period, reportEvents, channelFreqs); } public NativeScanSettingsBuilder addBucketWithChannels( int period, int reportEvents, int... channels) { WifiNative.BucketSettings bucket = new WifiNative.BucketSettings(); bucket.bucket = mSettings.num_buckets; bucket.band = WifiScanner.WIFI_BAND_UNSPECIFIED; bucket.num_channels = channels.length; bucket.channels = channelsToNativeSettings(channels); bucket.period_ms = period; bucket.report_events = reportEvents; return addBucket(bucket); } public NativeScanSettingsBuilder addBucket(WifiNative.BucketSettings bucket) { mSettings.buckets = Arrays.copyOf(mSettings.buckets, mSettings.num_buckets + 1); mSettings.buckets[mSettings.num_buckets] = bucket; mSettings.num_buckets = mSettings.num_buckets + 1; return this; } public WifiNative.ScanSettings build() { return mSettings; } } /** * Compute the expected native scan settings that are expected for the given * WifiScanner.ScanSettings. */ public static WifiNative.ScanSettings computeSingleScanNativeSettings( WifiScanner.ScanSettings requestSettings) { int reportEvents = requestSettings.reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; NativeScanSettingsBuilder builder = new NativeScanSettingsBuilder() .withBasePeriod(0) .withMaxApPerScan(0) .withMaxPercentToCache(0) .withMaxScansToCache(0); if (requestSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { builder.addBucketWithChannels(0, reportEvents, requestSettings.channels); } else { builder.addBucketWithBand(0, reportEvents, requestSettings.band); } return builder.build(); } /** * Compute the expected native scan settings that are expected for the given channels. */ public static WifiNative.ScanSettings createSingleScanNativeSettingsForChannels( int reportEvents, WifiScanner.ChannelSpec... channels) { int actualReportEvents = reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; return new NativeScanSettingsBuilder() .withBasePeriod(0) .withMaxApPerScan(0) .withMaxPercentToCache(0) .withMaxScansToCache(0) .addBucketWithChannels(0, actualReportEvents, channels) .build(); } public static Set createFreqSet(int... elements) { Set set = new HashSet<>(); for (int e : elements) { set.add(e); } return set; } public static ScanResult createScanResult(int freq) { return new ScanResult(WifiSsid.createFromAsciiEncoded("AN SSID"), "00:00:00:00:00:00", 0L, -1, null, "", 0, freq, 0); } private static ScanData createScanData(int[] freqs, int bucketsScanned) { ScanResult[] results = new ScanResult[freqs.length]; for (int i = 0; i < freqs.length; ++i) { results[i] = createScanResult(freqs[i]); } return new ScanData(0, 0, bucketsScanned, false, results); } public static ScanData[] createScanDatas(int[][] freqs, int[] bucketsScanned) { assumeTrue(freqs.length == bucketsScanned.length); ScanData[] data = new ScanData[freqs.length]; for (int i = 0; i < freqs.length; ++i) { data[i] = createScanData(freqs[i], bucketsScanned[i]); } return data; } public static ScanData[] createScanDatas(int[][] freqs) { return createScanDatas(freqs, new int[freqs.length] /* defaults all 0 */); } private static void assertScanResultEquals( String prefix, ScanResult expected, ScanResult actual) { assertEquals(prefix + "SSID", expected.SSID, actual.SSID); assertEquals(prefix + "wifiSsid", expected.wifiSsid.toString(), actual.wifiSsid.toString()); assertEquals(prefix + "BSSID", expected.BSSID, actual.BSSID); assertEquals(prefix + "capabilities", expected.capabilities, actual.capabilities); assertEquals(prefix + "level", expected.level, actual.level); assertEquals(prefix + "frequency", expected.frequency, actual.frequency); assertEquals(prefix + "timestamp", expected.timestamp, actual.timestamp); assertEquals(prefix + "seen", expected.seen, actual.seen); } private static void assertScanResultsEquals(String prefix, ScanResult[] expected, ScanResult[] actual) { assertNotNull(prefix + "expected ScanResults was null", expected); assertNotNull(prefix + "actual ScanResults was null", actual); assertEquals(prefix + "results.length", expected.length, actual.length); for (int j = 0; j < expected.length; ++j) { ScanResult expectedResult = expected[j]; ScanResult actualResult = actual[j]; assertScanResultEquals(prefix + "results[" + j + "]", actualResult, expectedResult); } } /** * Asserts if the provided scan results are the same. */ public static void assertScanResultEquals(ScanResult expected, ScanResult actual) { assertScanResultEquals("", expected, actual); } /** * Asserts if the provided scan result arrays are the same. */ public static void assertScanResultsEquals(ScanResult[] expected, ScanResult[] actual) { assertScanResultsEquals("", expected, actual); } private static void assertScanDataEquals(String prefix, ScanData expected, ScanData actual) { assertNotNull(prefix + "expected ScanData was null", expected); assertNotNull(prefix + "actual ScanData was null", actual); assertEquals(prefix + "id", expected.getId(), actual.getId()); assertEquals(prefix + "flags", expected.getFlags(), actual.getFlags()); assertEquals(prefix + "all channels", expected.isAllChannelsScanned(), actual.isAllChannelsScanned()); assertScanResultsEquals(prefix, expected.getResults(), actual.getResults()); } public static void assertScanDataEquals(ScanData expected, ScanData actual) { assertScanDataEquals("", expected, actual); } public static void assertScanDatasEquals(String prefix, ScanData[] expected, ScanData[] actual) { assertNotNull("expected " + prefix + "ScanData[] was null", expected); assertNotNull("actaul " + prefix + "ScanData[] was null", actual); assertEquals(prefix + "ScanData.length", expected.length, actual.length); for (int i = 0; i < expected.length; ++i) { assertScanDataEquals(prefix + "ScanData[" + i + "].", expected[i], actual[i]); } } public static void assertScanDatasEquals(ScanData[] expected, ScanData[] actual) { assertScanDatasEquals("", expected, actual); } public static WifiScanner.ChannelSpec[] channelsToSpec(int... channels) { WifiScanner.ChannelSpec[] channelSpecs = new WifiScanner.ChannelSpec[channels.length]; for (int i = 0; i < channels.length; ++i) { channelSpecs[i] = new WifiScanner.ChannelSpec(channels[i]); } return channelSpecs; } public static void assertNativeScanSettingsEquals(WifiNative.ScanSettings expected, WifiNative.ScanSettings actual) { assertEquals("bssids per scan", expected.max_ap_per_scan, actual.max_ap_per_scan); assertEquals("scans to cache", expected.report_threshold_num_scans, actual.report_threshold_num_scans); assertEquals("percent to cache", expected.report_threshold_percent, actual.report_threshold_percent); assertEquals("base period", expected.base_period_ms, actual.base_period_ms); assertEquals("number of buckets", expected.num_buckets, actual.num_buckets); assertNotNull("buckets was null", actual.buckets); for (int i = 0; i < expected.buckets.length; ++i) { assertNotNull("buckets[" + i + "] was null", actual.buckets[i]); assertEquals("buckets[" + i + "].period", expected.buckets[i].period_ms, actual.buckets[i].period_ms); assertEquals("buckets[" + i + "].reportEvents", expected.buckets[i].report_events, actual.buckets[i].report_events); assertEquals("buckets[" + i + "].band", expected.buckets[i].band, actual.buckets[i].band); if (expected.buckets[i].band == WifiScanner.WIFI_BAND_UNSPECIFIED) { Set expectedChannels = new HashSet<>(); for (WifiNative.ChannelSettings channel : expected.buckets[i].channels) { expectedChannels.add(channel.frequency); } Set actualChannels = new HashSet<>(); for (WifiNative.ChannelSettings channel : actual.buckets[i].channels) { actualChannels.add(channel.frequency); } assertEquals("channels", expectedChannels, actualChannels); } else { // since num_channels and channels are ignored when band is not // WifiScanner.WIFI_BAND_UNSPECIFIED just assert that there are no channels // the band equality was already checked above assertEquals("buckets[" + i + "].num_channels not 0", 0, actual.buckets[i].num_channels); assertTrue("buckets[" + i + "].channels not null or empty", actual.buckets[i].channels == null || actual.buckets[i].channels.length == 0); } } } /** * Asserts if the provided pno settings are the same. */ public static void assertNativePnoSettingsEquals(WifiNative.PnoSettings expected, WifiNative.PnoSettings actual) { assertNotNull("expected was null", expected); assertNotNull("actaul was null", actual); assertEquals("min5GHzRssi", expected.min5GHzRssi, actual.min5GHzRssi); assertEquals("min24GHzRssi", expected.min24GHzRssi, actual.min24GHzRssi); assertEquals("initialScoreMax", expected.initialScoreMax, actual.initialScoreMax); assertEquals("currentConnectionBonus", expected.currentConnectionBonus, actual.currentConnectionBonus); assertEquals("sameNetworkBonus", expected.sameNetworkBonus, actual.sameNetworkBonus); assertEquals("secureBonus", expected.secureBonus, actual.secureBonus); assertEquals("band5GHzBonus", expected.band5GHzBonus, actual.band5GHzBonus); assertEquals("isConnected", expected.isConnected, actual.isConnected); assertNotNull("expected networkList was null", expected.networkList); assertNotNull("actual networkList was null", actual.networkList); assertEquals("networkList.length", expected.networkList.length, actual.networkList.length); for (int i = 0; i < expected.networkList.length; i++) { assertEquals("networkList[" + i + "].ssid", expected.networkList[i].ssid, actual.networkList[i].ssid); assertEquals("networkList[" + i + "].flags", expected.networkList[i].flags, actual.networkList[i].flags); assertEquals("networkList[" + i + "].auth_bit_field", expected.networkList[i].auth_bit_field, actual.networkList[i].auth_bit_field); } } /** * Convert a list of channel frequencies to an array of equivalent WifiNative.ChannelSettings */ public static WifiNative.ChannelSettings[] channelsToNativeSettings(int... channels) { WifiNative.ChannelSettings[] channelSpecs = new WifiNative.ChannelSettings[channels.length]; for (int i = 0; i < channels.length; ++i) { channelSpecs[i] = new WifiNative.ChannelSettings(); channelSpecs[i].frequency = channels[i]; } return channelSpecs; } /** * Matcher to check that a BucketSettings has the given band */ public static Matcher bandIs(final int expectedBand) { return new TypeSafeDiagnosingMatcher() { @Override public boolean matchesSafely(WifiNative.BucketSettings bucketSettings, Description mismatchDescription) { if (bucketSettings.band != expectedBand) { mismatchDescription .appendText("did not have expected band ").appendValue(expectedBand) .appendText(", was ").appendValue(bucketSettings.band); return false; } else { return true; } } @Override public void describeTo(final Description description) { description.appendText("bucket band is ").appendValue(expectedBand); } }; } /** * Matcher to check that a BucketSettings has exactly the given channels */ public static Matcher channelsAre(final int... expectedChannels) { return new TypeSafeDiagnosingMatcher() { @Override public boolean matchesSafely(WifiNative.BucketSettings bucketSettings, Description mismatchDescription) { if (bucketSettings.band != WifiScanner.WIFI_BAND_UNSPECIFIED) { mismatchDescription.appendText("did not have expected unspecified band, was ") .appendValue(bucketSettings.band); return false; } else if (bucketSettings.num_channels != expectedChannels.length) { mismatchDescription .appendText("did not have expected num_channels ") .appendValue(expectedChannels.length) .appendText(", was ").appendValue(bucketSettings.num_channels); return false; } else if (bucketSettings.channels == null) { mismatchDescription.appendText("had null channels array"); return false; } else if (bucketSettings.channels.length != expectedChannels.length) { mismatchDescription .appendText("did not have channels array length matching excepted ") .appendValue(expectedChannels.length) .appendText(", was ").appendValue(bucketSettings.channels.length); return false; } else { Set foundChannelsSet = new HashSet<>(); for (int i = 0; i < bucketSettings.channels.length; ++i) { foundChannelsSet.add(bucketSettings.channels[i].frequency); } Set expectedChannelsSet = new HashSet<>(); for (int i = 0; i < expectedChannels.length; ++i) { expectedChannelsSet.add(expectedChannels[i]); } if (!foundChannelsSet.containsAll(expectedChannelsSet) || foundChannelsSet.size() != expectedChannelsSet.size()) { Set extraChannelsSet = new HashSet<>(foundChannelsSet); extraChannelsSet.removeAll(expectedChannelsSet); expectedChannelsSet.removeAll(foundChannelsSet); mismatchDescription .appendText("does not contain expected channels ") .appendValue(expectedChannelsSet); if (extraChannelsSet.size() > 0) { mismatchDescription .appendText(", but contains extra channels ") .appendValue(extraChannelsSet); } return false; } else { return true; } } } @Override public void describeTo(final Description description) { description.appendText("bucket channels are ").appendValue(expectedChannels); } }; } }