• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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