Home | History | Annotate | Download | only in scanner
      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