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 17 package com.android.server.wifi.scanner; 18 19 import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; 20 import static com.android.server.wifi.ScanTestUtil.assertNativeScanSettingsEquals; 21 import static com.android.server.wifi.ScanTestUtil.channelsToSpec; 22 import static com.android.server.wifi.ScanTestUtil.createRequest; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.mockito.Mockito.validateMockitoUsage; 28 29 import android.net.wifi.WifiScanner; 30 import android.net.wifi.WifiScanner.ScanSettings; 31 import android.support.test.filters.SmallTest; 32 import android.util.ArraySet; 33 34 import com.android.server.wifi.WifiNative; 35 import com.android.server.wifi.WifiNative.BucketSettings; 36 import com.android.server.wifi.scanner.KnownBandsChannelHelper.KnownBandsChannelCollection; 37 38 import org.junit.After; 39 import org.junit.Before; 40 import org.junit.Test; 41 42 import java.lang.reflect.Field; 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.Collections; 46 import java.util.Set; 47 48 /** 49 * Unit tests for {@link com.android.server.wifi.scanner.BackgroundScanScheduler}. 50 */ 51 @SmallTest 52 public class BackgroundScanSchedulerTest { 53 54 private static final int DEFAULT_MAX_BUCKETS = 9; 55 private static final int DEFAULT_MAX_CHANNELS_PER_BUCKET = 23; 56 private static final int DEFAULT_MAX_BATCH = 11; 57 private static final int DEFAULT_MAX_AP_PER_SCAN = 33; 58 59 private KnownBandsChannelHelper mChannelHelper; 60 private BackgroundScanScheduler mScheduler; 61 62 @Before 63 public void setUp() throws Exception { 64 mChannelHelper = new PresetKnownBandsChannelHelper( 65 new int[]{2400, 2450}, 66 new int[]{5150, 5175}, 67 new int[]{5600, 5650, 5660}); 68 mScheduler = new BackgroundScanScheduler(mChannelHelper); 69 mScheduler.setMaxBuckets(DEFAULT_MAX_BUCKETS); 70 mScheduler.setMaxChannelsPerBucket(DEFAULT_MAX_CHANNELS_PER_BUCKET); 71 mScheduler.setMaxBatch(DEFAULT_MAX_BATCH); 72 mScheduler.setMaxApPerScan(DEFAULT_MAX_AP_PER_SCAN); 73 } 74 75 @After 76 public void cleanup() { 77 validateMockitoUsage(); 78 } 79 80 @Test 81 public void noRequest() { 82 Collection<ScanSettings> requests = Collections.emptyList(); 83 84 mScheduler.updateSchedule(requests); 85 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 86 87 assertEquals(30000, schedule.base_period_ms); 88 assertBuckets(schedule, 0); 89 } 90 91 @Test 92 public void singleRequest() { 93 Collection<ScanSettings> requests = Collections.singleton(createRequest( 94 WifiScanner.WIFI_BAND_BOTH, 30000, 0, 20, 95 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 96 )); 97 98 mScheduler.updateSchedule(requests); 99 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 100 101 assertEquals(30000, schedule.base_period_ms); 102 assertBuckets(schedule, 1); 103 for (ScanSettings request : requests) { 104 assertSettingsSatisfied(schedule, request, false, true); 105 } 106 } 107 108 @Test 109 public void singleRequestWithoutPredefinedBucket() { 110 Collection<ScanSettings> requests = Collections.singleton(createRequest( 111 WifiScanner.WIFI_BAND_BOTH, 7500, 0, 20, 112 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 113 )); 114 115 mScheduler.updateSchedule(requests); 116 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 117 118 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 119 assertBuckets(schedule, 1); 120 for (ScanSettings request : requests) { 121 assertSettingsSatisfied(schedule, request, false, true); 122 } 123 } 124 125 @Test 126 public void fewRequests() { 127 Collection<ScanSettings> requests = new ArrayList<>(); 128 requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 30000, 0, 20, 129 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 130 requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 14000, 0, 20, 131 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 132 133 mScheduler.updateSchedule(requests); 134 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 135 136 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 137 assertBuckets(schedule, 2); 138 for (ScanSettings request : requests) { 139 assertSettingsSatisfied(schedule, request, false, true); 140 } 141 } 142 143 @Test 144 public void manyRequests() { 145 Collection<ScanSettings> requests = new ArrayList<>(); 146 requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 30000, 0, 20, 147 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 148 requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 15000, 0, 20, 149 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 150 requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10000, 0, 20, 151 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 152 153 mScheduler.updateSchedule(requests); 154 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 155 156 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 157 assertBuckets(schedule, 2); 158 for (ScanSettings request : requests) { 159 assertSettingsSatisfied(schedule, request, false, false); 160 } 161 } 162 163 @Test 164 public void requestsWithNoPeriodCommonDenominator() { 165 ArrayList<ScanSettings> requests = new ArrayList<>(); 166 requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 299999, 0, 20, 167 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 168 requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10500, 0, 20, 169 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 170 171 mScheduler.updateSchedule(requests); 172 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 173 174 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 175 assertBuckets(schedule, 2); 176 for (ScanSettings request : requests) { 177 assertSettingsSatisfied(schedule, request, false, true); 178 } 179 } 180 181 @Test 182 public void manyRequestsDifferentReportScans() { 183 Collection<ScanSettings> requests = new ArrayList<>(); 184 requests.add(createRequest(channelsToSpec(5175), 60000, 0, 20, 185 WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); 186 requests.add(createRequest(channelsToSpec(2400), 60000, 0, 20, 187 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 188 requests.add(createRequest(channelsToSpec(2450), 60000, 0, 20, 189 WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 190 requests.add(createRequest(channelsToSpec(5150), 60000, 0, 20, 191 WifiScanner.REPORT_EVENT_NO_BATCH)); 192 193 mScheduler.updateSchedule(requests); 194 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 195 196 assertEquals("base_period_ms", 60000, schedule.base_period_ms); 197 assertBuckets(schedule, 1); 198 for (ScanSettings request : requests) { 199 assertSettingsSatisfied(schedule, request, false, true); 200 } 201 } 202 203 @Test 204 public void exceedMaxBatch() { 205 Collection<ScanSettings> requests = new ArrayList<>(); 206 requests.add(createRequest(channelsToSpec(5175), 30000, 10, 20, 207 WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); 208 209 mScheduler.setMaxBatch(5); 210 mScheduler.updateSchedule(requests); 211 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 212 213 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 214 assertBuckets(schedule, 1); 215 for (ScanSettings request : requests) { 216 assertSettingsSatisfied(schedule, request, false, true); 217 } 218 assertEquals("maxScansToCache", 5, schedule.report_threshold_num_scans); 219 } 220 221 @Test 222 public void defaultMaxBatch() { 223 Collection<ScanSettings> requests = new ArrayList<>(); 224 requests.add(createRequest(channelsToSpec(5175), 60000, 0, 20, 225 WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); 226 227 mScheduler.setMaxBatch(6); 228 mScheduler.updateSchedule(requests); 229 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 230 231 assertEquals("base_period_ms", 60000, schedule.base_period_ms); 232 assertBuckets(schedule, 1); 233 for (ScanSettings request : requests) { 234 assertSettingsSatisfied(schedule, request, false, true); 235 } 236 assertEquals("maxScansToCache", 6, schedule.report_threshold_num_scans); 237 } 238 239 @Test 240 public void exceedMaxAps() { 241 Collection<ScanSettings> requests = new ArrayList<>(); 242 requests.add(createRequest(channelsToSpec(5175), 30000, 10, 20, 243 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 244 245 mScheduler.setMaxApPerScan(5); 246 mScheduler.updateSchedule(requests); 247 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 248 249 assertEquals("maxScansToCache", 5, schedule.max_ap_per_scan); 250 } 251 252 @Test 253 public void defaultMaxAps() { 254 Collection<ScanSettings> requests = new ArrayList<>(); 255 requests.add(createRequest(channelsToSpec(5175), 30000, 10, 0, 256 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 257 258 mScheduler.setMaxApPerScan(8); 259 mScheduler.updateSchedule(requests); 260 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 261 262 assertEquals("maxApsPerScan", 8, schedule.max_ap_per_scan); 263 } 264 265 @Test 266 public void optimalScheduleExceedsNumberOfAvailableBuckets() { 267 ArrayList<ScanSettings> requests = new ArrayList<>(); 268 requests.add(createRequest(channelsToSpec(2400), 30000, 0, 20, 269 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 270 requests.add(createRequest(channelsToSpec(2450), 10000, 0, 20, 271 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 272 requests.add(createRequest(channelsToSpec(5150), 120000, 0, 20, 273 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 274 275 mScheduler.setMaxBuckets(2); 276 mScheduler.updateSchedule(requests); 277 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 278 279 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 280 assertBuckets(schedule, 2); 281 for (ScanSettings request : requests) { 282 assertSettingsSatisfied(schedule, request, true, true); 283 } 284 } 285 286 @Test 287 public void optimalScheduleExceedsNumberOfAvailableBuckets2() { 288 ArrayList<ScanSettings> requests = new ArrayList<>(); 289 requests.add(createRequest(channelsToSpec(2400), 30000, 0, 20, 290 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 291 requests.add(createRequest(channelsToSpec(2450), 60000, 0, 20, 292 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 293 requests.add(createRequest(channelsToSpec(5150), 3840000, 0, 20, 294 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 295 296 mScheduler.setMaxBuckets(2); 297 mScheduler.updateSchedule(requests); 298 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 299 300 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 301 assertBuckets(schedule, 2); 302 for (ScanSettings request : requests) { 303 assertSettingsSatisfied(schedule, request, true, true); 304 } 305 } 306 307 /** 308 * Ensure that a channel request is placed in the bucket closest to the original 309 * period and not the bucket it is initially placed in. Here the 5 min period is 310 * initially placed in the 240s bucket, but that bucket is eliminated because it 311 * would be a 7th bucket. This test ensures that the request is placed in the 480s 312 * bucket and not the 120s bucket. 313 */ 314 @Test 315 public void optimalScheduleExceedsNumberOfAvailableBucketsClosestToOriginal() { 316 ArrayList<ScanSettings> requests = new ArrayList<>(); 317 requests.add(createRequest(channelsToSpec(2400), 30 * 1000, 0, 20, 318 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 319 requests.add(createRequest(channelsToSpec(2450), 120 * 1000, 0, 20, 320 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 321 requests.add(createRequest(channelsToSpec(5150), 480 * 1000, 0, 20, 322 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 323 requests.add(createRequest(channelsToSpec(5175), 10 * 1000, 0, 20, 324 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 325 requests.add(createRequest(channelsToSpec(5600), 60 * 1000, 0, 20, 326 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 327 requests.add(createRequest(channelsToSpec(5650), 1920 * 1000, 0, 20, 328 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 329 330 requests.add(createRequest(channelsToSpec(5660), 300 * 1000, 0, 20, // 5 min 331 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 332 333 mScheduler.setMaxBuckets(6); 334 mScheduler.updateSchedule(requests); 335 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 336 337 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 338 assertBuckets(schedule, 6); 339 for (ScanSettings request : requests) { 340 assertSettingsSatisfied(schedule, request, true, true); 341 } 342 } 343 344 @Test 345 public void optimalScheduleExceedsMaxChannelsOnSingleBand() { 346 ArrayList<ScanSettings> requests = new ArrayList<>(); 347 requests.add(createRequest(channelsToSpec(2400, 2450), 30000, 0, 20, 348 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 349 350 mScheduler.setMaxBuckets(2); 351 mScheduler.setMaxChannelsPerBucket(1); 352 mScheduler.updateSchedule(requests); 353 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 354 355 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 356 assertBuckets(schedule, 2); 357 for (ScanSettings request : requests) { 358 assertSettingsSatisfied(schedule, request, true, true); 359 } 360 } 361 362 @Test 363 public void optimalScheduleExceedsMaxChannelsOnMultipleBands() { 364 ArrayList<ScanSettings> requests = new ArrayList<>(); 365 requests.add(createRequest(channelsToSpec(2400, 2450, 5150), 30000, 0, 20, 366 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 367 368 mScheduler.setMaxBuckets(2); 369 mScheduler.setMaxChannelsPerBucket(2); 370 mScheduler.updateSchedule(requests); 371 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 372 373 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 374 assertBuckets(schedule, 2); 375 for (ScanSettings request : requests) { 376 assertSettingsSatisfied(schedule, request, true, true); 377 } 378 } 379 380 @Test 381 public void optimalScheduleExceedsMaxChannelsOnMultipleBandsFromMultipleRequests() { 382 ArrayList<ScanSettings> requests = new ArrayList<>(); 383 requests.add(createRequest(channelsToSpec(2400, 2450), 30000, 0, 20, 384 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 385 requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ, 30000, 0, 20, 386 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 387 388 mScheduler.setMaxBuckets(2); 389 mScheduler.setMaxChannelsPerBucket(2); 390 mScheduler.updateSchedule(requests); 391 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 392 393 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 394 assertBuckets(schedule, 2); 395 for (ScanSettings request : requests) { 396 assertSettingsSatisfied(schedule, request, true, true); 397 } 398 } 399 400 @Test 401 public void exactRequests() { 402 scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 30000, 0, 403 20, WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); 404 scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_5_GHZ, 60000, 3, 405 13, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 406 scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10000, 2, 407 10, WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); 408 scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 25000, 0, 409 10, WifiScanner.REPORT_EVENT_NO_BATCH)); 410 scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 25000, 3, 411 0, WifiScanner.REPORT_EVENT_NO_BATCH)); 412 scheduleAndTestExactRequest(createRequest(channelsToSpec(2400, 5175, 5650) , 25000, 3, 413 0, WifiScanner.REPORT_EVENT_NO_BATCH)); 414 } 415 416 @Test 417 public void singleExponentialBackOffRequest() { 418 Collection<ScanSettings> requests = Collections.singleton(createRequest( 419 WifiScanner.TYPE_LOW_LATENCY, WifiScanner.WIFI_BAND_BOTH, 30000, 160000, 2, 0, 20, 420 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN 421 )); 422 423 mScheduler.updateSchedule(requests); 424 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 425 426 assertEquals(30000, schedule.base_period_ms); 427 assertBuckets(schedule, 1); 428 for (ScanSettings request : requests) { 429 assertSettingsSatisfied(schedule, request, false, true); 430 } 431 } 432 433 @Test 434 public void exponentialBackOffAndRegularRequests() { 435 Collection<ScanSettings> requests = new ArrayList<>(); 436 requests.add(createRequest(WifiScanner.TYPE_LOW_LATENCY, WifiScanner.WIFI_BAND_BOTH, 30000, 437 200000, 1, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 438 requests.add(createRequest(channelsToSpec(5175), 30000, 0, 20, 439 WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); 440 441 mScheduler.updateSchedule(requests); 442 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 443 444 assertEquals("base_period_ms", 30000, schedule.base_period_ms); 445 assertBuckets(schedule, 2); 446 for (ScanSettings request : requests) { 447 assertSettingsSatisfied(schedule, request, false, true); 448 } 449 } 450 451 /** 452 * Add 2 background scan requests with different time intervals, but one of the setting channels 453 * is totally contained in the other setting. Ensure that the requests are collapsed into a 454 * common bucket with the lower time period setting. 455 */ 456 @Test 457 public void optimalScheduleFullyCollapsesDuplicateChannelsInBand() { 458 ArrayList<ScanSettings> requests = new ArrayList<>(); 459 requests.add(createRequest(channelsToSpec(2400, 2450), 240000, 0, 20, 460 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 461 requests.add(createRequest(WifiScanner.WIFI_BAND_24_GHZ, 10000, 0, 20, 462 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 463 464 mScheduler.setMaxBuckets(2); 465 mScheduler.setMaxChannelsPerBucket(2); 466 mScheduler.updateSchedule(requests); 467 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 468 469 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 470 assertBuckets(schedule, 1); 471 for (ScanSettings request : requests) { 472 assertSettingsSatisfied(schedule, request, false, false); 473 } 474 475 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(0))); 476 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(1))); 477 478 KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); 479 collection.addBand(WifiScanner.WIFI_BAND_24_GHZ); 480 Set<Integer> expectedBucketChannelSet = collection.getAllChannels(); 481 assertBucketChannels(schedule.buckets[0], expectedBucketChannelSet); 482 } 483 484 /** 485 * Add 2 background scan requests with different time intervals, but one of the setting channels 486 * is totally contained in the other setting. Ensure that the requests are collapsed into a 487 * common bucket with the lower time period setting. 488 */ 489 @Test 490 public void optimalScheduleFullyCollapsesDuplicateChannels() { 491 ArrayList<ScanSettings> requests = new ArrayList<>(); 492 requests.add(createRequest(channelsToSpec(2400, 2450), 240000, 0, 20, 493 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 494 requests.add(createRequest(channelsToSpec(2400, 2450), 10000, 0, 20, 495 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 496 497 mScheduler.setMaxBuckets(2); 498 mScheduler.setMaxChannelsPerBucket(2); 499 mScheduler.updateSchedule(requests); 500 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 501 502 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 503 assertBuckets(schedule, 1); 504 for (ScanSettings request : requests) { 505 assertSettingsSatisfied(schedule, request, false, false); 506 } 507 508 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(0))); 509 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(1))); 510 511 Set<Integer> expectedBucketChannelSet = new ArraySet<>(); 512 expectedBucketChannelSet.add(2400); 513 expectedBucketChannelSet.add(2450); 514 assertBucketChannels(schedule.buckets[0], expectedBucketChannelSet); 515 } 516 517 /** 518 * Add 2 background scan requests with different time intervals, but one of the setting channels 519 * is partially contained in the other setting. Ensure that the requests are partially split 520 * across the lower time period bucket. 521 */ 522 @Test 523 public void optimalSchedulePartiallyCollapsesDuplicateChannels() { 524 ArrayList<ScanSettings> requests = new ArrayList<>(); 525 requests.add(createRequest(channelsToSpec(2400, 2450), 10000, 0, 20, 526 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 527 requests.add(createRequest(channelsToSpec(2400, 2450, 5175), 240000, 0, 20, 528 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 529 530 mScheduler.setMaxBuckets(2); 531 mScheduler.setMaxChannelsPerBucket(2); 532 mScheduler.updateSchedule(requests); 533 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 534 535 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 536 assertBuckets(schedule, 2); 537 for (ScanSettings request : requests) { 538 assertSettingsSatisfied(schedule, request, false, false); 539 } 540 541 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(0))); 542 assertEquals("scheduled bucket", 1, mScheduler.getScheduledBucket(requests.get(1))); 543 544 Set<Integer> expectedBucketChannelSet = new ArraySet<>(); 545 expectedBucketChannelSet.add(2400); 546 expectedBucketChannelSet.add(2450); 547 assertBucketChannels(schedule.buckets[0], expectedBucketChannelSet); 548 549 expectedBucketChannelSet.clear(); 550 expectedBucketChannelSet.add(5175); 551 assertBucketChannels(schedule.buckets[1], expectedBucketChannelSet); 552 } 553 554 /** 555 * Add 2 background scan requests with different time intervals, but one of the setting channels 556 * is partially contained in the 2 other settings. Ensure that the requests are partially split 557 * across the lower time period buckets. 558 */ 559 @Test 560 public void optimalSchedulePartiallyCollapsesDuplicateChannelsAcrossMultipleBuckets() { 561 ArrayList<ScanSettings> requests = new ArrayList<>(); 562 requests.add(createRequest(channelsToSpec(2400, 2450), 10000, 0, 20, 563 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 564 requests.add(createRequest(channelsToSpec(2400, 2450, 5175), 30000, 0, 20, 565 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 566 requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, 240000, 0, 20, 567 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 568 569 mScheduler.setMaxBuckets(3); 570 mScheduler.updateSchedule(requests); 571 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 572 573 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 574 assertBuckets(schedule, 3); 575 for (ScanSettings request : requests) { 576 assertSettingsSatisfied(schedule, request, false, false); 577 } 578 579 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(0))); 580 assertEquals("scheduled bucket", 1, mScheduler.getScheduledBucket(requests.get(1))); 581 assertEquals("scheduled bucket", 2, mScheduler.getScheduledBucket(requests.get(2))); 582 583 Set<Integer> expectedBucketChannelSet = new ArraySet<>(); 584 expectedBucketChannelSet.add(2400); 585 expectedBucketChannelSet.add(2450); 586 assertBucketChannels(schedule.buckets[0], expectedBucketChannelSet); 587 588 expectedBucketChannelSet.clear(); 589 expectedBucketChannelSet.add(5175); 590 assertBucketChannels(schedule.buckets[1], expectedBucketChannelSet); 591 592 KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); 593 collection.addBand(WifiScanner.WIFI_BAND_BOTH_WITH_DFS); 594 expectedBucketChannelSet = collection.getAllChannels(); 595 expectedBucketChannelSet.remove(5175); 596 expectedBucketChannelSet.remove(2400); 597 expectedBucketChannelSet.remove(2450); 598 assertBucketChannels(schedule.buckets[2], expectedBucketChannelSet); 599 } 600 601 /** 602 * Add 2 background scan requests with different time intervals, but one of the setting channels 603 * is partially contained in the 2 other settings. Ensure that the requests are partially split 604 * across the lower time period buckets and the last bucket is split into 2 because the 605 * channel list does not fit into a single bucket. 606 */ 607 @Test 608 public void optimalSchedulePartiallyCollapsesDuplicateChannelsWithSplitBuckets() { 609 ArrayList<ScanSettings> requests = new ArrayList<>(); 610 requests.add(createRequest(channelsToSpec(2400, 2450), 10000, 0, 20, 611 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 612 requests.add(createRequest(channelsToSpec(2400, 2450, 5175), 30000, 0, 20, 613 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 614 requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, 240000, 0, 20, 615 WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); 616 617 mScheduler.setMaxBuckets(5); 618 mScheduler.setMaxChannelsPerBucket(2); 619 mScheduler.updateSchedule(requests); 620 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 621 622 assertEquals("base_period_ms", 10000, schedule.base_period_ms); 623 assertBuckets(schedule, 4); 624 for (ScanSettings request : requests) { 625 assertSettingsSatisfied(schedule, request, false, false); 626 } 627 628 assertEquals("scheduled bucket", 0, mScheduler.getScheduledBucket(requests.get(0))); 629 assertEquals("scheduled bucket", 1, mScheduler.getScheduledBucket(requests.get(1))); 630 assertEquals("scheduled bucket", 2, mScheduler.getScheduledBucket(requests.get(2))); 631 632 Set<Integer> expectedBucketChannelSet = new ArraySet<>(); 633 expectedBucketChannelSet.add(2400); 634 expectedBucketChannelSet.add(2450); 635 assertBucketChannels(schedule.buckets[0], expectedBucketChannelSet); 636 637 expectedBucketChannelSet.clear(); 638 expectedBucketChannelSet.add(5175); 639 assertBucketChannels(schedule.buckets[1], expectedBucketChannelSet); 640 641 KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); 642 collection.addBand(WifiScanner.WIFI_BAND_BOTH_WITH_DFS); 643 expectedBucketChannelSet = collection.getAllChannels(); 644 expectedBucketChannelSet.remove(5175); 645 expectedBucketChannelSet.remove(2400); 646 expectedBucketChannelSet.remove(2450); 647 // Check if the combined channel set matches what we expect 648 Set<Integer> combinedBucketChannelSet = getAllChannels(schedule.buckets[2]); 649 combinedBucketChannelSet.addAll(getAllChannels(schedule.buckets[3])); 650 assertChannels(combinedBucketChannelSet, expectedBucketChannelSet); 651 } 652 653 protected Set<Integer> getAllChannels(BucketSettings bucket) { 654 KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); 655 collection.addChannels(bucket); 656 return collection.getAllChannels(); 657 } 658 659 protected Set<Integer> getAllChannels(WifiScanner.ScanSettings settings) { 660 KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); 661 collection.addChannels(settings); 662 return collection.getAllChannels(); 663 } 664 665 public void scheduleAndTestExactRequest(ScanSettings settings) { 666 Collection<ScanSettings> requests = new ArrayList<>(); 667 requests.add(settings); 668 669 mScheduler.updateSchedule(requests); 670 WifiNative.ScanSettings schedule = mScheduler.getSchedule(); 671 672 int expectedPeriod = computeExpectedPeriod(settings.periodInMs); 673 NativeScanSettingsBuilder expectedBuilder = new NativeScanSettingsBuilder() 674 .withBasePeriod(expectedPeriod) 675 .withMaxApPerScan(settings.numBssidsPerScan == 0 676 ? DEFAULT_MAX_AP_PER_SCAN 677 : settings.numBssidsPerScan) 678 .withMaxScansToCache(settings.maxScansToCache == 0 679 ? DEFAULT_MAX_BATCH 680 : settings.maxScansToCache); 681 682 if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { 683 expectedBuilder.addBucketWithChannels(expectedPeriod, settings.reportEvents, 684 settings.channels); 685 } else { 686 expectedBuilder.addBucketWithBand(expectedPeriod, settings.reportEvents, settings.band); 687 } 688 assertNativeScanSettingsEquals(expectedBuilder.build(), schedule); 689 } 690 691 private void assertBuckets(WifiNative.ScanSettings schedule, int numBuckets) { 692 assertEquals("num_buckets", numBuckets, schedule.num_buckets); 693 assertNotNull("buckets was null", schedule.buckets); 694 assertEquals("num_buckets and actual buckets", schedule.num_buckets, 695 schedule.buckets.length); 696 for (int i = 0; i < numBuckets; i++) { 697 assertNotNull("bucket[" + i + "] was null", schedule.buckets[i]); 698 if (schedule.buckets[i].band == WifiScanner.WIFI_BAND_UNSPECIFIED) { 699 assertTrue("num channels <= 0", schedule.buckets[i].num_channels > 0); 700 assertTrue("bucket channels > max channels", 701 schedule.buckets[i].num_channels <= mScheduler.getMaxChannelsPerBucket()); 702 assertNotNull("Channels was null", schedule.buckets[i].channels); 703 for (int c = 0; c < schedule.buckets[i].num_channels; c++) { 704 assertNotNull("Channel was null", schedule.buckets[i].channels[c]); 705 } 706 } else { 707 assertTrue("Invalid band: " + schedule.buckets[i].band, 708 schedule.buckets[i].band > WifiScanner.WIFI_BAND_UNSPECIFIED 709 && schedule.buckets[i].band <= WifiScanner.WIFI_BAND_BOTH_WITH_DFS); 710 } 711 } 712 } 713 714 private void assertSettingsSatisfied(WifiNative.ScanSettings schedule, 715 ScanSettings settings, boolean bucketsLimited, boolean exactPeriod) { 716 assertTrue("bssids per scan: " + schedule.max_ap_per_scan + " /<= " 717 + settings.numBssidsPerScan, 718 schedule.max_ap_per_scan <= settings.numBssidsPerScan); 719 720 if (settings.maxScansToCache > 0) { 721 assertTrue("scans to cache: " + schedule.report_threshold_num_scans + " /<= " 722 + settings.maxScansToCache, 723 schedule.report_threshold_num_scans <= settings.maxScansToCache); 724 } 725 726 Set<Integer> channelSet = getAllChannels(settings); 727 728 StringBuilder ignoreString = new StringBuilder(); 729 730 KnownBandsChannelCollection scheduleChannels = mChannelHelper.createChannelCollection(); 731 for (int b = 0; b < schedule.num_buckets; b++) { 732 BucketSettings bucket = schedule.buckets[b]; 733 if ((settings.reportEvents & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { 734 if ((bucket.report_events & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) == 0) { 735 ignoreString 736 .append(" ") 737 .append(getAllChannels(bucket)) 738 .append("=after_each_scan:") 739 .append(bucket.report_events & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) 740 .append("!=") 741 .append(settings.reportEvents 742 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); 743 continue; 744 } 745 } 746 if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 747 if ((bucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) == 0) { 748 ignoreString 749 .append(" ") 750 .append(getAllChannels(bucket)) 751 .append("=full_result:") 752 .append(bucket.report_events 753 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) 754 .append("!=") 755 .append(settings.reportEvents 756 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT); 757 continue; 758 } 759 } 760 if ((settings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 761 if ((bucket.report_events & WifiScanner.REPORT_EVENT_NO_BATCH) != 0) { 762 ignoreString 763 .append(" ") 764 .append(getAllChannels(bucket)) 765 .append("=no_batch:") 766 .append(bucket.report_events & WifiScanner.REPORT_EVENT_NO_BATCH) 767 .append("!=") 768 .append(settings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH); 769 continue; 770 } 771 } 772 int expectedPeriod; 773 774 if (settings.maxPeriodInMs != 0 && settings.periodInMs != settings.maxPeriodInMs) { 775 // exponential back off scan 776 expectedPeriod = settings.periodInMs; 777 } else { 778 if (bucketsLimited) { 779 expectedPeriod = computeExpectedPeriod(settings.periodInMs, schedule); 780 } else { 781 expectedPeriod = computeExpectedPeriod(settings.periodInMs); 782 } 783 } 784 785 if (exactPeriod) { 786 if (bucket.period_ms != expectedPeriod) { 787 ignoreString 788 .append(" ") 789 .append(getAllChannels(bucket)) 790 .append("=period:") 791 .append(bucket.period_ms) 792 .append("!=") 793 .append(settings.periodInMs); 794 continue; 795 } 796 } else { 797 if (bucket.period_ms > expectedPeriod) { 798 ignoreString 799 .append(" ") 800 .append(getAllChannels(bucket)) 801 .append("=period:") 802 .append(bucket.period_ms) 803 .append(">") 804 .append(settings.periodInMs); 805 continue; 806 } 807 } 808 scheduleChannels.addChannels(bucket); 809 } 810 811 assertTrue("expected that " + scheduleChannels.getAllChannels() + " contained " 812 + channelSet + ", Channel ignore reasons:" + ignoreString.toString(), 813 scheduleChannels.getAllChannels().containsAll(channelSet)); 814 } 815 816 private void assertBucketChannels(BucketSettings bucket, Set<Integer> expectedChannelSet) { 817 Set<Integer> bucketChannelSet = getAllChannels(bucket); 818 assertChannels(bucketChannelSet, expectedChannelSet); 819 } 820 821 private void assertChannels(Set<Integer> channelSet, Set<Integer> expectedChannelSet) { 822 assertTrue("expected that " + channelSet + " contained " 823 + expectedChannelSet, channelSet.containsAll(expectedChannelSet)); 824 } 825 826 private static int[] getPredefinedBuckets() { 827 try { 828 Field f = BackgroundScanScheduler.class.getDeclaredField("PREDEFINED_BUCKET_PERIODS"); 829 f.setAccessible(true); 830 return (int[]) f.get(null); 831 } catch (Exception e) { 832 throw new RuntimeException("Could not get predefined buckets", e); 833 } 834 } 835 private static final int[] PREDEFINED_BUCKET_PERIODS = getPredefinedBuckets(); 836 837 // find closest bucket period to the requested period 838 private static int computeExpectedPeriod(int requestedPeriod) { 839 int period = 0; 840 int minDiff = Integer.MAX_VALUE; 841 for (int bucketPeriod : PREDEFINED_BUCKET_PERIODS) { 842 int diff = Math.abs(bucketPeriod - requestedPeriod); 843 if (diff < minDiff) { 844 minDiff = diff; 845 period = bucketPeriod; 846 } 847 } 848 return period; 849 } 850 851 // find closest bucket period to the requested period that exists in the schedule 852 private static int computeExpectedPeriod(int requestedPeriod, 853 WifiNative.ScanSettings schedule) { 854 int period = 0; 855 int minDiff = Integer.MAX_VALUE; 856 for (int i = 0; i < schedule.num_buckets; ++i) { 857 int bucketPeriod = schedule.buckets[i].period_ms; 858 int diff = Math.abs(bucketPeriod - requestedPeriod); 859 if (diff < minDiff) { 860 minDiff = diff; 861 period = bucketPeriod; 862 } 863 } 864 return period; 865 } 866 } 867