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