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