From 95984d1af44a00183a4b0e0ed61417583096ff90 Mon Sep 17 00:00:00 2001 From: Mitchell Wills Date: Thu, 17 Mar 2016 11:33:10 -0700 Subject: Merge MultiClientScheduler and WifiScanningScheduler And move them to the com.android.server.wifi.scanner package Change-Id: Iceac6823f118017f577e316d8eacc77c3445b2a7 --- .../android/server/wifi/MultiClientScheduler.java | 441 -------------- .../android/server/wifi/WifiScanningScheduler.java | 157 ----- .../server/wifi/WifiScanningServiceImpl.java | 8 +- .../wifi/scanner/BackgroundScanScheduler.java | 500 ++++++++++++++++ .../server/wifi/scanner/ScanScheduleUtil.java | 86 +++ .../wifi/MultiClientSchedulerFilterTest.java | 301 ---------- .../server/wifi/MultiClientSchedulerTest.java | 654 --------------------- .../server/wifi/WifiScanningServiceTest.java | 5 +- .../scanner/BackgroundScanSchedulerFilterTest.java | 301 ++++++++++ .../wifi/scanner/BackgroundScanSchedulerTest.java | 653 ++++++++++++++++++++ 10 files changed, 1548 insertions(+), 1558 deletions(-) delete mode 100644 service/java/com/android/server/wifi/MultiClientScheduler.java create mode 100644 service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java create mode 100644 service/java/com/android/server/wifi/scanner/ScanScheduleUtil.java delete mode 100644 tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerFilterTest.java delete mode 100644 tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java create mode 100644 tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerFilterTest.java create mode 100644 tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java diff --git a/service/java/com/android/server/wifi/MultiClientScheduler.java b/service/java/com/android/server/wifi/MultiClientScheduler.java deleted file mode 100644 index 31fbf64e4..000000000 --- a/service/java/com/android/server/wifi/MultiClientScheduler.java +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wifi; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiScanner; -import android.net.wifi.WifiScanner.ScanData; -import android.net.wifi.WifiScanner.ScanSettings; -import android.util.Rational; -import android.util.Slog; - -import com.android.server.wifi.scanner.ChannelHelper; -import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - -/** - *

This class takes a series of scan requests and formulates the best hardware level scanning - * schedule it can to try and satisfy requests. The hardware level accepts a series of buckets, - * where each bucket represents a set of channels and an interval to scan at. This - * scheduler operates as follows:

- * - *

Each new request is placed in the best predefined bucket. Once all requests have been added - * the last buckets (lower priority) are placed in the next best bucket until the number of buckets - * is less than the number supported by the hardware. - * - *

Finally, the scheduler creates a WifiNative.ScanSettings from the list of buckets which may be - * passed through the Wifi HAL.

- * - *

This class is not thread safe.

- */ -public class MultiClientScheduler extends WifiScanningScheduler { - - private static final String TAG = WifiScanningService.TAG; - private static final boolean DBG = false; - - /** - * Value that all scan periods must be an integer multiple of - */ - private static final int PERIOD_MIN_GCD_MS = 10000; - /** - * Default period to use if no buckets are being scheduled - */ - private static final int DEFAULT_PERIOD_MS = 40000; - /** - * Scan report threshold percentage to assign to the schedule by default - * @see com.android.server.wifi.WifiNative.ScanSettings#report_threshold_percent - */ - private static final int DEFAULT_REPORT_THRESHOLD_PERCENTAGE = 100; - - /** - * List of predefined periods (in ms) that buckets can be scheduled at. Ordered by preference - * if there are not enough buckets for all periods. All periods MUST be 2^N * PERIOD_MIN_GCD_MS. - * This requirement allows scans to be scheduled more efficiently because scan requests with - * intersecting channels will result in those channels being scanned exactly once at the smaller - * period and no unnecessary scan being scheduled. If this was not the case and two requests - * had channel 5 with periods of 15 seconds and 25 seconds then channel 5 would be scanned - * 296 (3600/15 + 3600/25 - 3500/75) times an hour instead of 240 times an hour (3600/15) if - * the 25s scan is rescheduled at 30s. This is less important with higher periods as it has - * significantly less impact. Ranking could be done by favoring shorter or longer; however, - * this would result in straying further from the requested period and possibly power - * implications if the scan is scheduled at a significantly lower period. - * - * For example if the hardware only supports 2 buckets and scans are requested with periods of - * 40s, 20s and 10s then the two buckets scheduled will have periods 40s and 20s and the 10s - * scan will be placed in the 20s bucket. - * - * If there are special scan requests such as exponential back off, we always dedicate a bucket - * for each type. Regular scan requests will be packed into the remaining buckets. - */ - private static final int[] PREDEFINED_BUCKET_PERIODS = { - 4 * PERIOD_MIN_GCD_MS, // 40s - 2 * PERIOD_MIN_GCD_MS, // 20s - 16 * PERIOD_MIN_GCD_MS, // 160s - 32 * PERIOD_MIN_GCD_MS, // 320s - 1 * PERIOD_MIN_GCD_MS, // 10s - 128 * PERIOD_MIN_GCD_MS, // 1280s - 64 * PERIOD_MIN_GCD_MS, // 640s - 256 * PERIOD_MIN_GCD_MS, // 2560s - -1, // place holder for exponential back off scan - }; - - private static final int EXPONENTIAL_BACK_OFF_BUCKET_IDX = - (PREDEFINED_BUCKET_PERIODS.length - 1); - private static final int NUM_OF_REGULAR_BUCKETS = - (PREDEFINED_BUCKET_PERIODS.length - 1); - - /** - * this class is an intermediate representation for scheduling - */ - private class Bucket { - public int period; - public final List settings = new ArrayList<>(); - - public Bucket(int period) { - this.period = period; - } - - /** - * convert ChannelSpec to native representation - */ - private WifiNative.ChannelSettings createChannelSettings(int frequency) { - WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings(); - channelSettings.frequency = frequency; - return channelSettings; - } - - /** - * convert the setting for this bucket to HAL representation - */ - public WifiNative.BucketSettings createBucketSettings(int bucketId, - int maxChannels) { - int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; - int maxPeriodInMs = 0; - int stepCount = 0; - int bucketIndex = 0; - - mChannelCollection.clear(); - - for (int i = 0; i < settings.size(); ++i) { - WifiScanner.ScanSettings setting = settings.get(i); - int requestedReportEvents = setting.reportEvents; - if ((requestedReportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { - reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH; - } - if ((requestedReportEvents & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { - reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; - } - if ((requestedReportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { - reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; - } - - mChannelCollection.addChannels(setting); - - // For the bucket allocated to exponential back off scan, the values of - // the exponential back off scan related parameters from the very first - // setting in the settings list will be used to configure this bucket. - // - if (i == 0 && setting.maxPeriodInMs != 0 - && setting.maxPeriodInMs != setting.periodInMs) { - // Align the starting period with one of the pre-defined regular - // scan periods. This will optimize the scan schedule when it has - // both exponential back off scan and regular scan(s). - bucketIndex = findBestRegularBucketIndex(setting.periodInMs, - NUM_OF_REGULAR_BUCKETS); - period = PREDEFINED_BUCKET_PERIODS[bucketIndex]; - maxPeriodInMs = (setting.maxPeriodInMs < period) - ? period - : setting.maxPeriodInMs; - stepCount = setting.stepCount; - } - - } - - WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); - bucketSettings.bucket = bucketId; - bucketSettings.report_events = reportEvents; - bucketSettings.period_ms = period; - bucketSettings.max_period_ms = maxPeriodInMs; - bucketSettings.step_count = stepCount; - mChannelCollection.fillBucketSettings(bucketSettings, maxChannels); - return bucketSettings; - } - } - - /** - * Maintains a list of buckets and the number that are active (non-null) - */ - private class BucketList { - private final Bucket[] mBuckets; - private int mActiveBucketCount = 0; - - public BucketList() { - mBuckets = new Bucket[PREDEFINED_BUCKET_PERIODS.length]; - } - - public void clearAll() { - Arrays.fill(mBuckets, null); - mActiveBucketCount = 0; - } - - public void clear(int index) { - if (mBuckets[index] != null) { - --mActiveBucketCount; - mBuckets[index] = null; - } - } - - public Bucket getOrCreate(int index) { - Bucket bucket = mBuckets[index]; - if (bucket == null) { - ++mActiveBucketCount; - bucket = mBuckets[index] = new Bucket(PREDEFINED_BUCKET_PERIODS[index]); - } - return bucket; - } - - public boolean isActive(int index) { - return mBuckets[index] != null; - } - - public Bucket get(int index) { - return mBuckets[index]; - } - - public int size() { - return mBuckets.length; - } - - public int getActiveCount() { - return mActiveBucketCount; - } - - public int getActiveRegularBucketCount() { - if (isActive(EXPONENTIAL_BACK_OFF_BUCKET_IDX)) { - return mActiveBucketCount - 1; - } else { - return mActiveBucketCount; - } - } - } - - private final BucketList mBuckets = new BucketList(); - private final ChannelHelper mChannelHelper; - private final ChannelCollection mChannelCollection; - private WifiNative.ScanSettings mSchedule; - - public MultiClientScheduler(ChannelHelper channelHelper) { - mChannelHelper = channelHelper; - mChannelCollection = mChannelHelper.createChannelCollection(); - mSchedule = createSchedule(Collections.emptyList()); - } - - @Override - public void updateSchedule(@NonNull Collection requests) { - // create initial schedule - mBuckets.clearAll(); - for (ScanSettings request : requests) { - addScanToBuckets(request); - } - - compactBuckets(getMaxBuckets()); - - mSchedule = createSchedule(requests); - } - - @Override - public @NonNull WifiNative.ScanSettings getSchedule() { - return mSchedule; - } - - @Override - public boolean shouldReportFullScanResultForSettings(@NonNull ScanResult result, - @NonNull ScanSettings settings) { - return mChannelHelper.settingsContainChannel(settings, result.frequency); - } - - @Override - public @Nullable ScanData[] filterResultsForSettings(@NonNull ScanData[] scanDatas, - @NonNull ScanSettings settings) { - ArrayList filteredScanDatas = new ArrayList<>(scanDatas.length); - ArrayList filteredResults = new ArrayList<>(); - for (ScanData scanData : scanDatas) { - filteredResults.clear(); - for (ScanResult scanResult : scanData.getResults()) { - if (mChannelHelper.settingsContainChannel(settings, scanResult.frequency)) { - filteredResults.add(scanResult); - } - if (settings.numBssidsPerScan > 0 - && filteredResults.size() >= settings.numBssidsPerScan) { - break; - } - } - // TODO correctly note if scan results may be incomplete - if (filteredResults.size() == scanData.getResults().length) { - filteredScanDatas.add(scanData); - } else if (filteredResults.size() > 0) { - filteredScanDatas.add(new WifiScanner.ScanData(scanData.getId(), - scanData.getFlags(), - filteredResults.toArray( - new ScanResult[filteredResults.size()]))); - } - } - if (filteredScanDatas.size() == 0) { - return null; - } else { - return filteredScanDatas.toArray(new ScanData[filteredScanDatas.size()]); - } - } - - // creates a schedule for the given buckets and requests - private WifiNative.ScanSettings createSchedule(Collection requests) { - WifiNative.ScanSettings schedule = new WifiNative.ScanSettings(); - schedule.num_buckets = mBuckets.getActiveCount(); - schedule.buckets = new WifiNative.BucketSettings[mBuckets.getActiveCount()]; - - // set all buckets in schedule - int bucketId = 0; - for (int i = 0; i < mBuckets.size(); ++i) { - if (mBuckets.isActive(i)) { - schedule.buckets[bucketId++] = - mBuckets.get(i).createBucketSettings(bucketId, getMaxChannels()); - } - } - - schedule.report_threshold_percent = DEFAULT_REPORT_THRESHOLD_PERCENTAGE; - - // update batching settings - schedule.max_ap_per_scan = 0; - schedule.report_threshold_num_scans = getMaxBatch(); - HashSet hiddenNetworkIdSet = new HashSet<>(); - for (ScanSettings settings : requests) { - // set APs per scan - if (settings.numBssidsPerScan > schedule.max_ap_per_scan) { - schedule.max_ap_per_scan = settings.numBssidsPerScan; - } - - // set batching - if (settings.maxScansToCache != 0 - && settings.maxScansToCache < schedule.report_threshold_num_scans) { - schedule.report_threshold_num_scans = settings.maxScansToCache; - } - - if (settings.hiddenNetworkIds != null) { - for (int i = 0; i < settings.hiddenNetworkIds.length; i++) { - hiddenNetworkIdSet.add(settings.hiddenNetworkIds[i]); - } - } - } - if (schedule.max_ap_per_scan == 0 || schedule.max_ap_per_scan > getMaxApPerScan()) { - schedule.max_ap_per_scan = getMaxApPerScan(); - } - if (hiddenNetworkIdSet.size() > 0) { - schedule.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()]; - int numHiddenNetworks = 0; - for (Integer hiddenNetworkId : hiddenNetworkIdSet) { - schedule.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId; - } - } - - // update base period as gcd of periods - if (schedule.num_buckets > 0) { - int gcd = schedule.buckets[0].period_ms; - for (int b = 1; b < schedule.num_buckets; b++) { - gcd = Rational.gcd(schedule.buckets[b].period_ms, gcd); - } - - if (gcd < PERIOD_MIN_GCD_MS) { - Slog.wtf(TAG, "found gcd less than min gcd"); - gcd = PERIOD_MIN_GCD_MS; - } - - schedule.base_period_ms = gcd; - } else { - schedule.base_period_ms = DEFAULT_PERIOD_MS; - } - - return schedule; - } - - public void addScanToBuckets(ScanSettings settings) { - int bucketIndex; - - if (settings.maxPeriodInMs != 0 - && settings.maxPeriodInMs != settings.periodInMs) { - // exponential back off scan has a dedicated bucket - bucketIndex = EXPONENTIAL_BACK_OFF_BUCKET_IDX; - } else { - bucketIndex = findBestRegularBucketIndex(settings.periodInMs, - NUM_OF_REGULAR_BUCKETS); - } - - mBuckets.getOrCreate(bucketIndex).settings.add(settings); - } - - /** - * find closest bucket period to the requested period in all predefined buckets - */ - private static int findBestRegularBucketIndex(int requestedPeriod, int maxNumBuckets) { - maxNumBuckets = Math.min(maxNumBuckets, NUM_OF_REGULAR_BUCKETS); - int index = -1; - int minDiff = Integer.MAX_VALUE; - for (int i = 0; i < maxNumBuckets; ++i) { - int diff = Math.abs(PREDEFINED_BUCKET_PERIODS[i] - requestedPeriod); - if (diff < minDiff) { - minDiff = diff; - index = i; - } - } - if (index == -1) { - Slog.wtf(TAG, "Could not find best bucket for period " + requestedPeriod + " in " - + maxNumBuckets + " buckets"); - } - return index; - } - - /** - * Reduce the number of required buckets by reassigning lower priority buckets to the next - * closest period bucket. - */ - private void compactBuckets(int maxBuckets) { - int maxRegularBuckets = maxBuckets; - - // reserve one bucket for exponential back off scan if there is - // such request(s) - if (mBuckets.isActive(EXPONENTIAL_BACK_OFF_BUCKET_IDX)) { - maxRegularBuckets--; - } - for (int i = NUM_OF_REGULAR_BUCKETS - 1; - i >= 0 && mBuckets.getActiveRegularBucketCount() > maxRegularBuckets; --i) { - if (mBuckets.isActive(i)) { - for (ScanSettings scanRequest : mBuckets.get(i).settings) { - int newBucketIndex = findBestRegularBucketIndex(scanRequest.periodInMs, i); - mBuckets.getOrCreate(newBucketIndex).settings.add(scanRequest); - } - mBuckets.clear(i); - } - } - } -} diff --git a/service/java/com/android/server/wifi/WifiScanningScheduler.java b/service/java/com/android/server/wifi/WifiScanningScheduler.java index 744afd55c..e69de29bb 100644 --- a/service/java/com/android/server/wifi/WifiScanningScheduler.java +++ b/service/java/com/android/server/wifi/WifiScanningScheduler.java @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wifi; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiScanner; - -import java.util.Collection; - -/** - * A class to handle scheduling wifi scan requests into a format for the wifi chipsets. - */ -public abstract class WifiScanningScheduler { - - static final int DEFAULT_MAX_BUCKETS = 8; - static final int DEFAULT_MAX_CHANNELS = 32; - // anecdotally, some chipsets will fail without explanation with a higher batch size, and - // there is apparently no way to retrieve the maximum batch size - static final int DEFAULT_MAX_SCANS_TO_BATCH = 10; - static final int DEFAULT_MAX_AP_PER_SCAN = 32; - - private int mMaxBuckets = DEFAULT_MAX_BUCKETS; - private int mMaxChannels = DEFAULT_MAX_CHANNELS; - private int mMaxBatch = DEFAULT_MAX_SCANS_TO_BATCH; - private int mMaxApPerScan = DEFAULT_MAX_AP_PER_SCAN; - - int getMaxBuckets() { - return mMaxBuckets; - } - - void setMaxBuckets(int maxBuckets) { - mMaxBuckets = maxBuckets; - } - - int getMaxChannels() { - return mMaxChannels; - } - - // TODO: find a way to get max channels - void setMaxChannels(int maxChannels) { - mMaxChannels = maxChannels; - } - - int getMaxBatch() { - return mMaxBatch; - } - - // TODO: find a way to get max batch size - void setMaxBatch(int maxBatch) { - mMaxBatch = maxBatch; - } - - int getMaxApPerScan() { - return mMaxApPerScan; - } - - void setMaxApPerScan(int maxApPerScan) { - mMaxApPerScan = maxApPerScan; - } - - /** - * Updates the schedule from the given set of requests. - */ - public abstract void updateSchedule(@NonNull Collection requests); - - /** - * Retrieves the current scanning schedule. - */ - public abstract @NonNull WifiNative.ScanSettings getSchedule(); - - /** - * Returns true if the given scan result should be reported to a listener with the given - * settings. - */ - public abstract boolean shouldReportFullScanResultForSettings( - @NonNull ScanResult result, @NonNull WifiScanner.ScanSettings settings); - - /** - * Returns a filtered version of the scan results from the chip that represents only the data - * requested in the settings. Will return null if the result should not be reported. - */ - public abstract @Nullable WifiScanner.ScanData[] filterResultsForSettings( - @NonNull WifiScanner.ScanData[] results, @NonNull WifiScanner.ScanSettings settings); - - /** - * Compares two ChannelSettings for equality. - */ - public static boolean channelEquals(@Nullable WifiNative.ChannelSettings channel1, - @Nullable WifiNative.ChannelSettings channel2) { - if (channel1 == null || channel2 == null) return false; - if (channel1 == channel2) return true; - - if (channel1.frequency != channel2.frequency) return false; - if (channel1.dwell_time_ms != channel2.dwell_time_ms) return false; - return channel1.passive == channel2.passive; - } - - /** - * Compares two BucketSettings for equality. - */ - public static boolean bucketEquals(@Nullable WifiNative.BucketSettings bucket1, - @Nullable WifiNative.BucketSettings bucket2) { - if (bucket1 == null || bucket2 == null) return false; - if (bucket1 == bucket2) return true; - - if (bucket1.bucket != bucket2.bucket) return false; - if (bucket1.band != bucket2.band) return false; - if (bucket1.period_ms != bucket2.period_ms) return false; - if (bucket1.report_events != bucket2.report_events) return false; - if (bucket1.num_channels != bucket2.num_channels) return false; - for (int c = 0; c < bucket1.num_channels; c++) { - if (!channelEquals(bucket1.channels[c], bucket2.channels[c])) { - return false; - } - } - - return true; - } - - /** - * Compares two ScanSettings for equality. - */ - public static boolean scheduleEquals(@Nullable WifiNative.ScanSettings schedule1, - @Nullable WifiNative.ScanSettings schedule2) { - if (schedule1 == null || schedule2 == null) return false; - if (schedule1 == schedule2) return true; - - if (schedule1.base_period_ms != schedule2.base_period_ms) return false; - if (schedule1.max_ap_per_scan != schedule2.max_ap_per_scan) return false; - if (schedule1.report_threshold_percent != schedule2.report_threshold_percent) return false; - if (schedule1.report_threshold_num_scans != schedule2.report_threshold_num_scans) return false; - if (schedule1.num_buckets != schedule2.num_buckets) return false; - for (int b = 0; b < schedule1.num_buckets; b++) { - if (!bucketEquals(schedule1.buckets[b], schedule2.buckets[b])) { - return false; - } - } - - return true; - } -} diff --git a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java index 58ddd579f..9b2e4b053 100644 --- a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java @@ -55,8 +55,10 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; +import com.android.server.wifi.scanner.BackgroundScanScheduler; import com.android.server.wifi.scanner.ChannelHelper; import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; +import com.android.server.wifi.scanner.ScanScheduleUtil; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -235,7 +237,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final ArrayMap mClients; private ChannelHelper mChannelHelper; - private WifiScanningScheduler mScheduler; + private BackgroundScanScheduler mScheduler; private WifiNative.ScanSettings mPreviousSchedule; private WifiBackgroundScanStateMachine mBackgroundScanStateMachine; @@ -740,7 +742,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mChannelHelper = mScannerImpl.getChannelHelper(); } - mScheduler = new MultiClientScheduler(mChannelHelper); + mScheduler = new BackgroundScanScheduler(mChannelHelper); WifiNative.ScanCapabilities capabilities = new WifiNative.ScanCapabilities(); @@ -1287,7 +1289,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mScheduler.updateSchedule(settings); WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - if (WifiScanningScheduler.scheduleEquals(mPreviousSchedule, schedule)) { + if (ScanScheduleUtil.scheduleEquals(mPreviousSchedule, schedule)) { if (DBG) Log.d(TAG, "schedule updated with no change"); return true; } diff --git a/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java b/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java new file mode 100644 index 000000000..1a6569fc8 --- /dev/null +++ b/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.scanner; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiScanner; +import android.net.wifi.WifiScanner.ScanData; +import android.net.wifi.WifiScanner.ScanSettings; +import android.util.Rational; +import android.util.Slog; + +import com.android.server.wifi.WifiNative; +import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +/** + *

This class takes a series of scan requests and formulates the best hardware level scanning + * schedule it can to try and satisfy requests. The hardware level accepts a series of buckets, + * where each bucket represents a set of channels and an interval to scan at. This + * scheduler operates as follows:

+ * + *

Each new request is placed in the best predefined bucket. Once all requests have been added + * the last buckets (lower priority) are placed in the next best bucket until the number of buckets + * is less than the number supported by the hardware. + * + *

Finally, the scheduler creates a WifiNative.ScanSettings from the list of buckets which may be + * passed through the Wifi HAL.

+ * + *

This class is not thread safe.

+ */ +public class BackgroundScanScheduler { + + private static final String TAG = "BackgroundScanScheduler"; + private static final boolean DBG = false; + + public static final int DEFAULT_MAX_BUCKETS = 8; + public static final int DEFAULT_MAX_CHANNELS = 32; + // anecdotally, some chipsets will fail without explanation with a higher batch size, and + // there is apparently no way to retrieve the maximum batch size + public static final int DEFAULT_MAX_SCANS_TO_BATCH = 10; + public static final int DEFAULT_MAX_AP_PER_SCAN = 32; + + /** + * Value that all scan periods must be an integer multiple of + */ + private static final int PERIOD_MIN_GCD_MS = 10000; + /** + * Default period to use if no buckets are being scheduled + */ + private static final int DEFAULT_PERIOD_MS = 40000; + /** + * Scan report threshold percentage to assign to the schedule by default + * @see com.android.server.wifi.WifiNative.ScanSettings#report_threshold_percent + */ + private static final int DEFAULT_REPORT_THRESHOLD_PERCENTAGE = 100; + + /** + * List of predefined periods (in ms) that buckets can be scheduled at. Ordered by preference + * if there are not enough buckets for all periods. All periods MUST be 2^N * PERIOD_MIN_GCD_MS. + * This requirement allows scans to be scheduled more efficiently because scan requests with + * intersecting channels will result in those channels being scanned exactly once at the smaller + * period and no unnecessary scan being scheduled. If this was not the case and two requests + * had channel 5 with periods of 15 seconds and 25 seconds then channel 5 would be scanned + * 296 (3600/15 + 3600/25 - 3500/75) times an hour instead of 240 times an hour (3600/15) if + * the 25s scan is rescheduled at 30s. This is less important with higher periods as it has + * significantly less impact. Ranking could be done by favoring shorter or longer; however, + * this would result in straying further from the requested period and possibly power + * implications if the scan is scheduled at a significantly lower period. + * + * For example if the hardware only supports 2 buckets and scans are requested with periods of + * 40s, 20s and 10s then the two buckets scheduled will have periods 40s and 20s and the 10s + * scan will be placed in the 20s bucket. + * + * If there are special scan requests such as exponential back off, we always dedicate a bucket + * for each type. Regular scan requests will be packed into the remaining buckets. + */ + private static final int[] PREDEFINED_BUCKET_PERIODS = { + 4 * PERIOD_MIN_GCD_MS, // 40s + 2 * PERIOD_MIN_GCD_MS, // 20s + 16 * PERIOD_MIN_GCD_MS, // 160s + 32 * PERIOD_MIN_GCD_MS, // 320s + 1 * PERIOD_MIN_GCD_MS, // 10s + 128 * PERIOD_MIN_GCD_MS, // 1280s + 64 * PERIOD_MIN_GCD_MS, // 640s + 256 * PERIOD_MIN_GCD_MS, // 2560s + -1, // place holder for exponential back off scan + }; + + private static final int EXPONENTIAL_BACK_OFF_BUCKET_IDX = + (PREDEFINED_BUCKET_PERIODS.length - 1); + private static final int NUM_OF_REGULAR_BUCKETS = + (PREDEFINED_BUCKET_PERIODS.length - 1); + + /** + * this class is an intermediate representation for scheduling + */ + private class Bucket { + public int period; + public final List settings = new ArrayList<>(); + + Bucket(int period) { + this.period = period; + } + + /** + * convert ChannelSpec to native representation + */ + private WifiNative.ChannelSettings createChannelSettings(int frequency) { + WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings(); + channelSettings.frequency = frequency; + return channelSettings; + } + + /** + * convert the setting for this bucket to HAL representation + */ + public WifiNative.BucketSettings createBucketSettings(int bucketId, + int maxChannels) { + int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; + int maxPeriodInMs = 0; + int stepCount = 0; + int bucketIndex = 0; + + mChannelCollection.clear(); + + for (int i = 0; i < settings.size(); ++i) { + WifiScanner.ScanSettings setting = settings.get(i); + int requestedReportEvents = setting.reportEvents; + if ((requestedReportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { + reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH; + } + if ((requestedReportEvents & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { + reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; + } + if ((requestedReportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { + reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; + } + + mChannelCollection.addChannels(setting); + + // For the bucket allocated to exponential back off scan, the values of + // the exponential back off scan related parameters from the very first + // setting in the settings list will be used to configure this bucket. + // + if (i == 0 && setting.maxPeriodInMs != 0 + && setting.maxPeriodInMs != setting.periodInMs) { + // Align the starting period with one of the pre-defined regular + // scan periods. This will optimize the scan schedule when it has + // both exponential back off scan and regular scan(s). + bucketIndex = findBestRegularBucketIndex(setting.periodInMs, + NUM_OF_REGULAR_BUCKETS); + period = PREDEFINED_BUCKET_PERIODS[bucketIndex]; + maxPeriodInMs = (setting.maxPeriodInMs < period) + ? period + : setting.maxPeriodInMs; + stepCount = setting.stepCount; + } + + } + + WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); + bucketSettings.bucket = bucketId; + bucketSettings.report_events = reportEvents; + bucketSettings.period_ms = period; + bucketSettings.max_period_ms = maxPeriodInMs; + bucketSettings.step_count = stepCount; + mChannelCollection.fillBucketSettings(bucketSettings, maxChannels); + return bucketSettings; + } + } + + /** + * Maintains a list of buckets and the number that are active (non-null) + */ + private class BucketList { + private final Bucket[] mBuckets; + private int mActiveBucketCount = 0; + + BucketList() { + mBuckets = new Bucket[PREDEFINED_BUCKET_PERIODS.length]; + } + + public void clearAll() { + Arrays.fill(mBuckets, null); + mActiveBucketCount = 0; + } + + public void clear(int index) { + if (mBuckets[index] != null) { + --mActiveBucketCount; + mBuckets[index] = null; + } + } + + public Bucket getOrCreate(int index) { + Bucket bucket = mBuckets[index]; + if (bucket == null) { + ++mActiveBucketCount; + bucket = mBuckets[index] = new Bucket(PREDEFINED_BUCKET_PERIODS[index]); + } + return bucket; + } + + public boolean isActive(int index) { + return mBuckets[index] != null; + } + + public Bucket get(int index) { + return mBuckets[index]; + } + + public int size() { + return mBuckets.length; + } + + public int getActiveCount() { + return mActiveBucketCount; + } + + public int getActiveRegularBucketCount() { + if (isActive(EXPONENTIAL_BACK_OFF_BUCKET_IDX)) { + return mActiveBucketCount - 1; + } else { + return mActiveBucketCount; + } + } + } + + private int mMaxBuckets = DEFAULT_MAX_BUCKETS; + private int mMaxChannels = DEFAULT_MAX_CHANNELS; + private int mMaxBatch = DEFAULT_MAX_SCANS_TO_BATCH; + private int mMaxApPerScan = DEFAULT_MAX_AP_PER_SCAN; + + public int getMaxBuckets() { + return mMaxBuckets; + } + + public void setMaxBuckets(int maxBuckets) { + mMaxBuckets = maxBuckets; + } + + public int getMaxChannels() { + return mMaxChannels; + } + + // TODO: find a way to get max channels + public void setMaxChannels(int maxChannels) { + mMaxChannels = maxChannels; + } + + public int getMaxBatch() { + return mMaxBatch; + } + + // TODO: find a way to get max batch size + public void setMaxBatch(int maxBatch) { + mMaxBatch = maxBatch; + } + + public int getMaxApPerScan() { + return mMaxApPerScan; + } + + public void setMaxApPerScan(int maxApPerScan) { + mMaxApPerScan = maxApPerScan; + } + + private final BucketList mBuckets = new BucketList(); + private final ChannelHelper mChannelHelper; + private final ChannelCollection mChannelCollection; + private WifiNative.ScanSettings mSchedule; + + public BackgroundScanScheduler(ChannelHelper channelHelper) { + mChannelHelper = channelHelper; + mChannelCollection = mChannelHelper.createChannelCollection(); + mSchedule = createSchedule(Collections.emptyList()); + } + + /** + * Updates the schedule from the given set of requests. + */ + public void updateSchedule(@NonNull Collection requests) { + // create initial schedule + mBuckets.clearAll(); + for (ScanSettings request : requests) { + addScanToBuckets(request); + } + + compactBuckets(getMaxBuckets()); + + mSchedule = createSchedule(requests); + } + + /** + * Retrieves the current scanning schedule. + */ + public @NonNull WifiNative.ScanSettings getSchedule() { + return mSchedule; + } + + /** + * Returns true if the given scan result should be reported to a listener with the given + * settings. + */ + public boolean shouldReportFullScanResultForSettings(@NonNull ScanResult result, + @NonNull ScanSettings settings) { + return mChannelHelper.settingsContainChannel(settings, result.frequency); + } + + /** + * Returns a filtered version of the scan results from the chip that represents only the data + * requested in the settings. Will return null if the result should not be reported. + */ + public @Nullable ScanData[] filterResultsForSettings(@NonNull ScanData[] scanDatas, + @NonNull ScanSettings settings) { + ArrayList filteredScanDatas = new ArrayList<>(scanDatas.length); + ArrayList filteredResults = new ArrayList<>(); + for (ScanData scanData : scanDatas) { + filteredResults.clear(); + for (ScanResult scanResult : scanData.getResults()) { + if (mChannelHelper.settingsContainChannel(settings, scanResult.frequency)) { + filteredResults.add(scanResult); + } + if (settings.numBssidsPerScan > 0 + && filteredResults.size() >= settings.numBssidsPerScan) { + break; + } + } + // TODO correctly note if scan results may be incomplete + if (filteredResults.size() == scanData.getResults().length) { + filteredScanDatas.add(scanData); + } else if (filteredResults.size() > 0) { + filteredScanDatas.add(new WifiScanner.ScanData(scanData.getId(), + scanData.getFlags(), + filteredResults.toArray( + new ScanResult[filteredResults.size()]))); + } + } + if (filteredScanDatas.size() == 0) { + return null; + } else { + return filteredScanDatas.toArray(new ScanData[filteredScanDatas.size()]); + } + } + + // creates a schedule for the given buckets and requests + private WifiNative.ScanSettings createSchedule(Collection requests) { + WifiNative.ScanSettings schedule = new WifiNative.ScanSettings(); + schedule.num_buckets = mBuckets.getActiveCount(); + schedule.buckets = new WifiNative.BucketSettings[mBuckets.getActiveCount()]; + + // set all buckets in schedule + int bucketId = 0; + for (int i = 0; i < mBuckets.size(); ++i) { + if (mBuckets.isActive(i)) { + schedule.buckets[bucketId++] = + mBuckets.get(i).createBucketSettings(bucketId, getMaxChannels()); + } + } + + schedule.report_threshold_percent = DEFAULT_REPORT_THRESHOLD_PERCENTAGE; + + // update batching settings + schedule.max_ap_per_scan = 0; + schedule.report_threshold_num_scans = getMaxBatch(); + HashSet hiddenNetworkIdSet = new HashSet<>(); + for (ScanSettings settings : requests) { + // set APs per scan + if (settings.numBssidsPerScan > schedule.max_ap_per_scan) { + schedule.max_ap_per_scan = settings.numBssidsPerScan; + } + + // set batching + if (settings.maxScansToCache != 0 + && settings.maxScansToCache < schedule.report_threshold_num_scans) { + schedule.report_threshold_num_scans = settings.maxScansToCache; + } + + if (settings.hiddenNetworkIds != null) { + for (int i = 0; i < settings.hiddenNetworkIds.length; i++) { + hiddenNetworkIdSet.add(settings.hiddenNetworkIds[i]); + } + } + } + if (schedule.max_ap_per_scan == 0 || schedule.max_ap_per_scan > getMaxApPerScan()) { + schedule.max_ap_per_scan = getMaxApPerScan(); + } + if (hiddenNetworkIdSet.size() > 0) { + schedule.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()]; + int numHiddenNetworks = 0; + for (Integer hiddenNetworkId : hiddenNetworkIdSet) { + schedule.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId; + } + } + + // update base period as gcd of periods + if (schedule.num_buckets > 0) { + int gcd = schedule.buckets[0].period_ms; + for (int b = 1; b < schedule.num_buckets; b++) { + gcd = Rational.gcd(schedule.buckets[b].period_ms, gcd); + } + + if (gcd < PERIOD_MIN_GCD_MS) { + Slog.wtf(TAG, "found gcd less than min gcd"); + gcd = PERIOD_MIN_GCD_MS; + } + + schedule.base_period_ms = gcd; + } else { + schedule.base_period_ms = DEFAULT_PERIOD_MS; + } + + return schedule; + } + + /** + * Add a scan to the most appropriate bucket, creating the bucket if necessary. + */ + private void addScanToBuckets(ScanSettings settings) { + int bucketIndex; + + if (settings.maxPeriodInMs != 0 + && settings.maxPeriodInMs != settings.periodInMs) { + // exponential back off scan has a dedicated bucket + bucketIndex = EXPONENTIAL_BACK_OFF_BUCKET_IDX; + } else { + bucketIndex = findBestRegularBucketIndex(settings.periodInMs, + NUM_OF_REGULAR_BUCKETS); + } + + mBuckets.getOrCreate(bucketIndex).settings.add(settings); + } + + /** + * find closest bucket period to the requested period in all predefined buckets + */ + private static int findBestRegularBucketIndex(int requestedPeriod, int maxNumBuckets) { + maxNumBuckets = Math.min(maxNumBuckets, NUM_OF_REGULAR_BUCKETS); + int index = -1; + int minDiff = Integer.MAX_VALUE; + for (int i = 0; i < maxNumBuckets; ++i) { + int diff = Math.abs(PREDEFINED_BUCKET_PERIODS[i] - requestedPeriod); + if (diff < minDiff) { + minDiff = diff; + index = i; + } + } + if (index == -1) { + Slog.wtf(TAG, "Could not find best bucket for period " + requestedPeriod + " in " + + maxNumBuckets + " buckets"); + } + return index; + } + + /** + * Reduce the number of required buckets by reassigning lower priority buckets to the next + * closest period bucket. + */ + private void compactBuckets(int maxBuckets) { + int maxRegularBuckets = maxBuckets; + + // reserve one bucket for exponential back off scan if there is + // such request(s) + if (mBuckets.isActive(EXPONENTIAL_BACK_OFF_BUCKET_IDX)) { + maxRegularBuckets--; + } + for (int i = NUM_OF_REGULAR_BUCKETS - 1; + i >= 0 && mBuckets.getActiveRegularBucketCount() > maxRegularBuckets; --i) { + if (mBuckets.isActive(i)) { + for (ScanSettings scanRequest : mBuckets.get(i).settings) { + int newBucketIndex = findBestRegularBucketIndex(scanRequest.periodInMs, i); + mBuckets.getOrCreate(newBucketIndex).settings.add(scanRequest); + } + mBuckets.clear(i); + } + } + } +} diff --git a/service/java/com/android/server/wifi/scanner/ScanScheduleUtil.java b/service/java/com/android/server/wifi/scanner/ScanScheduleUtil.java new file mode 100644 index 000000000..734a190ef --- /dev/null +++ b/service/java/com/android/server/wifi/scanner/ScanScheduleUtil.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.scanner; + +import android.annotation.Nullable; + +import com.android.server.wifi.WifiNative; + +/** + * A class with utilities for dealing with scan schedules. + */ +public class ScanScheduleUtil { + + /** + * Compares two ChannelSettings for equality. + */ + public static boolean channelEquals(@Nullable WifiNative.ChannelSettings channel1, + @Nullable WifiNative.ChannelSettings channel2) { + if (channel1 == null || channel2 == null) return false; + if (channel1 == channel2) return true; + + if (channel1.frequency != channel2.frequency) return false; + if (channel1.dwell_time_ms != channel2.dwell_time_ms) return false; + return channel1.passive == channel2.passive; + } + + /** + * Compares two BucketSettings for equality. + */ + public static boolean bucketEquals(@Nullable WifiNative.BucketSettings bucket1, + @Nullable WifiNative.BucketSettings bucket2) { + if (bucket1 == null || bucket2 == null) return false; + if (bucket1 == bucket2) return true; + + if (bucket1.bucket != bucket2.bucket) return false; + if (bucket1.band != bucket2.band) return false; + if (bucket1.period_ms != bucket2.period_ms) return false; + if (bucket1.report_events != bucket2.report_events) return false; + if (bucket1.num_channels != bucket2.num_channels) return false; + for (int c = 0; c < bucket1.num_channels; c++) { + if (!channelEquals(bucket1.channels[c], bucket2.channels[c])) { + return false; + } + } + + return true; + } + + /** + * Compares two ScanSettings for equality. + */ + public static boolean scheduleEquals(@Nullable WifiNative.ScanSettings schedule1, + @Nullable WifiNative.ScanSettings schedule2) { + if (schedule1 == null || schedule2 == null) return false; + if (schedule1 == schedule2) return true; + + if (schedule1.base_period_ms != schedule2.base_period_ms) return false; + if (schedule1.max_ap_per_scan != schedule2.max_ap_per_scan) return false; + if (schedule1.report_threshold_percent != schedule2.report_threshold_percent) return false; + if (schedule1.report_threshold_num_scans != schedule2.report_threshold_num_scans) { + return false; + } + if (schedule1.num_buckets != schedule2.num_buckets) return false; + for (int b = 0; b < schedule1.num_buckets; b++) { + if (!bucketEquals(schedule1.buckets[b], schedule2.buckets[b])) { + return false; + } + } + + return true; + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerFilterTest.java b/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerFilterTest.java deleted file mode 100644 index 3c539ed8f..000000000 --- a/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerFilterTest.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wifi; - -import static com.android.server.wifi.ScanTestUtil.channelsToSpec; -import static com.android.server.wifi.ScanTestUtil.createRequest; -import static com.android.server.wifi.ScanTestUtil.createScanDatas; -import static com.android.server.wifi.ScanTestUtil.createScanResult; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.validateMockitoUsage; - -import android.net.wifi.WifiScanner; -import android.net.wifi.WifiScanner.ScanData; -import android.net.wifi.WifiScanner.ScanSettings; -import android.test.suitebuilder.annotation.SmallTest; - -import com.android.server.wifi.scanner.ChannelHelper; -import com.android.server.wifi.scanner.PresetKnownBandsChannelHelper; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collection; -import java.util.Collections; - -/** - * Unit tests for filtering of scan results in {@link com.android.server.wifi.MultiClientScheduler}. - */ -@SmallTest -public class MultiClientSchedulerFilterTest { - - private static final int DEFAULT_MAX_BUCKETS = 8; - private static final int DEFAULT_MAX_CHANNELS = 8; - private static final int DEFAULT_MAX_BATCH = 10; - - private WifiNative mWifiNative; - private MultiClientScheduler mScheduler; - - @Before - public void setUp() throws Exception { - ChannelHelper channelHelper = new PresetKnownBandsChannelHelper( - new int[]{2400, 2450}, - new int[]{5150, 5175}, - new int[]{5600, 5650}); - mScheduler = new MultiClientScheduler(channelHelper); - mScheduler.setMaxBuckets(DEFAULT_MAX_BUCKETS); - mScheduler.setMaxChannels(DEFAULT_MAX_CHANNELS); - mScheduler.setMaxBatch(DEFAULT_MAX_BATCH); - } - - @After - public void cleanup() { - validateMockitoUsage(); - } - - @Test - public void reportFullResultTrueForBands() { - ScanSettings settings = createRequest( - WifiScanner.WIFI_BAND_24_GHZ, 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - assertTrue(mScheduler.shouldReportFullScanResultForSettings( - createScanResult(2400), settings)); - } - - @Test - public void reportFullResultFalseForBands() { - ScanSettings settings = createRequest( - WifiScanner.WIFI_BAND_24_GHZ, 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - assertFalse(mScheduler.shouldReportFullScanResultForSettings( - createScanResult(5150), settings)); - } - - @Test - public void reportFullResultTrueForChannels() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - assertTrue(mScheduler.shouldReportFullScanResultForSettings( - createScanResult(2400), settings)); - } - - @Test - public void reportFullResultFalseForChannels() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - assertFalse(mScheduler.shouldReportFullScanResultForSettings( - createScanResult(5175), settings)); - } - - @Test - public void filterScanDataEmpty() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings(new ScanData[0], settings); - assertScanDataFreqsEquals(null, results); - } - - @Test - public void filterScanDataSingleNotMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings( - createScanDatas(new int[][]{ { 2450 } }), settings); - assertScanDataFreqsEquals(null, results); - } - - @Test - public void filterScanDataSingleMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings( - createScanDatas(new int[][]{ { 2400 } }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400 } }, results); - } - - @Test - public void filterScanDataSinglePartialMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings( - createScanDatas(new int[][]{ { 2400, 2450, 5150, 5175 } }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400, 5150 } }, results); - } - - @Test - public void filterScanDataMultipleNotMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings( - createScanDatas(new int[][]{ { 2450 }, { 2450, 5175 } }), settings); - assertScanDataFreqsEquals(null, results); - } - - @Test - public void filterScanDataMultipleMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings( - createScanDatas(new int[][]{ { 2400 }, {2400, 5150} }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400 }, {2400, 5150} }, results); - } - - @Test - public void filterScanDataMultiplePartialMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( - new int[][]{ { 2400, 2450, 5150, 5175 }, { 2400, 2450, 5175 } }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400, 5150 }, { 2400 } }, results); - } - - @Test - public void filterScanDataMultipleDuplicateFrequencies() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( - new int[][]{ { 2400, 2450, 5150, 5175, 2400 }, - { 2400, 2450, 5175 }, - { 5175, 5175, 5150 } }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400, 5150, 2400 }, { 2400 }, { 5150 } }, results); - } - - @Test - public void filterScanDataMultipleSomeNotMatching() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( - new int[][]{ { 2400, 2450, 5150, 5175, 2400 }, - { 5175 }, - { 5175, 5175, 5150 } }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400, 5150, 2400 }, { 5150 } }, results); - } - - @Test - public void filterScanDataExceedMaxBssidsPerScan() { - ScanSettings settings = createRequest( - channelsToSpec(2400, 5150), 30000, 0, 3, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - ); - Collection requests = Collections.singleton(settings); - mScheduler.updateSchedule(requests); - - ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( - new int[][]{ { 2400, 2450, 5150, 5175, 2400, 2400}, - { 5175 }, - { 5175, 5175, 5150, 2400, 2400, 5150 } }), settings); - - assertScanDataFreqsEquals(new int[][]{ { 2400, 5150, 2400 }, { 5150, 2400, 2400 } }, - results); - } - - - public static void assertScanDataFreqsEquals(int[][] expected, ScanData[] results) { - if (expected == null) { - assertNull(results); - } else { - assertNotNull(results); - assertEquals("num scans", expected.length, results.length); - for (int i = 0; i < expected.length; ++i) { - assertNotNull("scan[" + i + "] was null", results[i]); - assertEquals("num aps in scan[" + i + "]", expected[i].length, - results[i].getResults().length); - for (int j = 0; j < expected[i].length; ++j) { - assertNotNull("ap result[" + i + "][" + j + "] was null", - results[i].getResults()[j]); - assertEquals("ap freq in result[" + i + "][" + j + "]", expected[i][j], - results[i].getResults()[j].frequency); - } - } - } - } -} diff --git a/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java b/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java deleted file mode 100644 index 86db34cb6..000000000 --- a/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wifi; - -import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; -import static com.android.server.wifi.ScanTestUtil.assertNativeScanSettingsEquals; -import static com.android.server.wifi.ScanTestUtil.channelsToSpec; -import static com.android.server.wifi.ScanTestUtil.createRequest; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.validateMockitoUsage; - -import android.net.wifi.WifiScanner; -import android.net.wifi.WifiScanner.ScanSettings; -import android.test.suitebuilder.annotation.SmallTest; - -import com.android.server.wifi.WifiNative.BucketSettings; -import com.android.server.wifi.scanner.KnownBandsChannelHelper; -import com.android.server.wifi.scanner.KnownBandsChannelHelper.KnownBandsChannelCollection; -import com.android.server.wifi.scanner.PresetKnownBandsChannelHelper; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -/** - * Unit tests for {@link com.android.server.wifi.MultiClientScheduler}. - */ -@SmallTest -public class MultiClientSchedulerTest { - - private static final int DEFAULT_MAX_BUCKETS = 9; - private static final int DEFAULT_MAX_CHANNELS = 23; - private static final int DEFAULT_MAX_BATCH = 11; - private static final int DEFAULT_MAX_AP_PER_SCAN = 33; - - private KnownBandsChannelHelper mChannelHelper; - private MultiClientScheduler mScheduler; - - @Before - public void setUp() throws Exception { - mChannelHelper = new PresetKnownBandsChannelHelper( - new int[]{2400, 2450}, - new int[]{5150, 5175}, - new int[]{5600, 5650, 5660}); - mScheduler = new MultiClientScheduler(mChannelHelper); - mScheduler.setMaxBuckets(DEFAULT_MAX_BUCKETS); - mScheduler.setMaxChannels(DEFAULT_MAX_CHANNELS); - mScheduler.setMaxBatch(DEFAULT_MAX_BATCH); - mScheduler.setMaxApPerScan(DEFAULT_MAX_AP_PER_SCAN); - } - - @After - public void cleanup() { - validateMockitoUsage(); - } - - @Test - public void noRequest() { - Collection requests = Collections.emptyList(); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals(40000, schedule.base_period_ms); - assertBuckets(schedule, 0); - } - - @Test - public void singleRequest() { - Collection requests = Collections.singleton(createRequest( - WifiScanner.WIFI_BAND_BOTH, 20000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - )); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals(20000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - @Test - public void singleRequestWithoutPredefinedBucket() { - Collection requests = Collections.singleton(createRequest( - WifiScanner.WIFI_BAND_BOTH, 7500, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - )); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 10000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - @Test - public void fewRequests() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 30000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 14000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 10000, schedule.base_period_ms); - assertBuckets(schedule, 2); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - @Test - public void manyRequests() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 15000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 10000, schedule.base_period_ms); - assertBuckets(schedule, 2); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, false); - } - } - - @Test - public void requestsWithNoPeriodCommonDenominator() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 299999, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10500, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 10000, schedule.base_period_ms); - assertBuckets(schedule, 2); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - @Test - public void manyRequestsDifferentReportScans() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(5175), 40000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); - requests.add(createRequest(channelsToSpec(2400), 40000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(2450), 40000, 0, 20, - WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - requests.add(createRequest(channelsToSpec(5150), 40000, 0, 20, - WifiScanner.REPORT_EVENT_NO_BATCH)); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 40000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - @Test - public void exceedMaxBatch() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(5175), 20000, 10, 20, - WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); - - mScheduler.setMaxBatch(5); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - assertEquals("maxScansToCache", 5, schedule.report_threshold_num_scans); - } - - @Test - public void defaultMaxBatch() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(5175), 40000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); - - mScheduler.setMaxBatch(6); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 40000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - assertEquals("maxScansToCache", 6, schedule.report_threshold_num_scans); - } - - @Test - public void exceedMaxAps() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(5175), 30000, 10, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxApPerScan(5); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("maxScansToCache", 5, schedule.max_ap_per_scan); - } - - @Test - public void defaultMaxAps() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(5175), 30000, 10, 0, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxApPerScan(8); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("maxApsPerScan", 8, schedule.max_ap_per_scan); - } - - @Test - public void optimalScheduleExceedsNumberOfAvailableBuckets() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(2400), 20000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(2450), 10000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5150), 40000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxBuckets(2); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 2); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, true, true); - } - } - - @Test - public void optimalScheduleExceedsNumberOfAvailableBuckets2() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(2400), 20000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(2450), 40000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5150), 2560000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxBuckets(2); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 2); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, true, true); - } - } - - /** - * Ensure that a channel request is placed in the bucket closest to the original - * period and not the bucket it is initially placed in. Here the 12 min period is - * initially placed in the 640s bucket, but that bucket is eliminated because it - * would be a 7th bucket. This test ensures that the request is placed in the 1280s - * bucket and not the 320s bucket. - */ - @Test - public void optimalScheduleExceedsNumberOfAvailableBucketsClosestToOriginal() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(2400), 40 * 1000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(2450), 20 * 1000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5150), 160 * 1000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5175), 320 * 1000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5600), 10 * 1000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5650), 1280 * 1000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - requests.add(createRequest(channelsToSpec(5660), 720 * 1000, 0, 20, // 12 min - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxBuckets(6); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 10000, schedule.base_period_ms); - assertBuckets(schedule, 6); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, true, true); - } - } - - @Test - public void optimalScheduleExceedsMaxChannelsOnSingleBand() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(2400, 2450), 20000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxBuckets(2); - mScheduler.setMaxChannels(1); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, true, true); - } - } - - @Test - public void optimalScheduleExceedsMaxChannelsOnMultipleBands() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(2400, 2450, 5150), 20000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxBuckets(2); - mScheduler.setMaxChannels(2); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, true, true); - } - } - - @Test - public void optimalScheduleExceedsMaxChannelsOnMultipleBandsFromMultipleRequests() { - ArrayList requests = new ArrayList<>(); - requests.add(createRequest(channelsToSpec(2400, 2450), 20000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ, 20000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - - mScheduler.setMaxBuckets(2); - mScheduler.setMaxChannels(2); - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, true, true); - } - } - - @Test - public void exactRequests() { - scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, 0, - 20, WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); - scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_5_GHZ, 40000, 3, - 13, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10000, 2, - 10, WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); - scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 25000, 0, - 10, WifiScanner.REPORT_EVENT_NO_BATCH)); - scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 25000, 3, - 0, WifiScanner.REPORT_EVENT_NO_BATCH)); - scheduleAndTestExactRequest(createRequest(channelsToSpec(2400, 5175, 5650) , 25000, 3, - 0, WifiScanner.REPORT_EVENT_NO_BATCH)); - } - - @Test - public void singleExponentialBackOffRequest() { - Collection requests = Collections.singleton(createRequest( - WifiScanner.WIFI_BAND_BOTH, 20000, 160000, 2, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN - )); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals(20000, schedule.base_period_ms); - assertBuckets(schedule, 1); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - @Test - public void exponentialBackOffAndRegularRequests() { - Collection requests = new ArrayList<>(); - requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, 200000, 1, - 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); - requests.add(createRequest(channelsToSpec(5175), 30000, 0, 20, - WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - assertEquals("base_period_ms", 20000, schedule.base_period_ms); - assertBuckets(schedule, 2); - for (ScanSettings request : requests) { - assertSettingsSatisfied(schedule, request, false, true); - } - } - - protected Set getAllChannels(BucketSettings bucket) { - KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); - collection.addChannels(bucket); - return collection.getAllChannels(); - } - - protected Set getAllChannels(WifiScanner.ScanSettings settings) { - KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); - collection.addChannels(settings); - return collection.getAllChannels(); - } - - public void scheduleAndTestExactRequest(ScanSettings settings) { - Collection requests = new ArrayList<>(); - requests.add(settings); - - mScheduler.updateSchedule(requests); - WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - - int expectedPeriod = computeExpectedPeriod(settings.periodInMs); - NativeScanSettingsBuilder expectedBuilder = new NativeScanSettingsBuilder() - .withBasePeriod(expectedPeriod) - .withMaxApPerScan(settings.numBssidsPerScan == 0 - ? DEFAULT_MAX_AP_PER_SCAN - : settings.numBssidsPerScan) - .withMaxScansToCache(settings.maxScansToCache == 0 - ? DEFAULT_MAX_BATCH - : settings.maxScansToCache); - - if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { - expectedBuilder.addBucketWithChannels(expectedPeriod, settings.reportEvents, - settings.channels); - } else { - expectedBuilder.addBucketWithBand(expectedPeriod, settings.reportEvents, settings.band); - } - assertNativeScanSettingsEquals(expectedBuilder.build(), schedule); - } - - private void assertBuckets(WifiNative.ScanSettings schedule, int numBuckets) { - assertEquals("num_buckets", numBuckets, schedule.num_buckets); - assertNotNull("buckets was null", schedule.buckets); - assertEquals("num_buckets and actual buckets", schedule.num_buckets, - schedule.buckets.length); - for (int i = 0; i < numBuckets; i++) { - assertNotNull("bucket[" + i + "] was null", schedule.buckets[i]); - if (schedule.buckets[i].band == WifiScanner.WIFI_BAND_UNSPECIFIED) { - assertTrue("num channels <= 0", schedule.buckets[i].num_channels > 0); - assertTrue("bucket channels > max channels", - schedule.buckets[i].num_channels <= mScheduler.getMaxChannels()); - assertNotNull("Channels was null", schedule.buckets[i].channels); - for (int c = 0; c < schedule.buckets[i].num_channels; c++) { - assertNotNull("Channel was null", schedule.buckets[i].channels[c]); - } - } else { - assertTrue("Invalid band: " + schedule.buckets[i].band, - schedule.buckets[i].band > WifiScanner.WIFI_BAND_UNSPECIFIED - && schedule.buckets[i].band <= WifiScanner.WIFI_BAND_BOTH_WITH_DFS); - } - } - } - - private void assertSettingsSatisfied(WifiNative.ScanSettings schedule, - ScanSettings settings, boolean bucketsLimited, boolean exactPeriod) { - assertTrue("bssids per scan: " + schedule.max_ap_per_scan + " /<= " - + settings.numBssidsPerScan, - schedule.max_ap_per_scan <= settings.numBssidsPerScan); - - if (settings.maxScansToCache > 0) { - assertTrue("scans to cache: " + schedule.report_threshold_num_scans + " /<= " - + settings.maxScansToCache, - schedule.report_threshold_num_scans <= settings.maxScansToCache); - } - - Set channelSet = getAllChannels(settings); - - StringBuilder ignoreString = new StringBuilder(); - - KnownBandsChannelCollection scheduleChannels = mChannelHelper.createChannelCollection(); - for (int b = 0; b < schedule.num_buckets; b++) { - BucketSettings bucket = schedule.buckets[b]; - if ((settings.reportEvents & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { - if ((bucket.report_events & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) == 0) { - ignoreString - .append(" ") - .append(getAllChannels(bucket)) - .append("=after_each_scan:") - .append(bucket.report_events & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) - .append("!=") - .append(settings.reportEvents - & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); - continue; - } - } - if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { - if ((bucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) == 0) { - ignoreString - .append(" ") - .append(getAllChannels(bucket)) - .append("=full_result:") - .append(bucket.report_events - & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) - .append("!=") - .append(settings.reportEvents - & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT); - continue; - } - } - if ((settings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { - if ((bucket.report_events & WifiScanner.REPORT_EVENT_NO_BATCH) != 0) { - ignoreString - .append(" ") - .append(getAllChannels(bucket)) - .append("=no_batch:") - .append(bucket.report_events & WifiScanner.REPORT_EVENT_NO_BATCH) - .append("!=") - .append(settings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH); - continue; - } - } - int expectedPeriod; - - if (settings.maxPeriodInMs != 0 && settings.periodInMs != settings.maxPeriodInMs) { - // exponential back off scan - expectedPeriod = settings.periodInMs; - } else { - if (bucketsLimited) { - expectedPeriod = computeExpectedPeriod(settings.periodInMs, schedule); - } else { - expectedPeriod = computeExpectedPeriod(settings.periodInMs); - } - } - - if (exactPeriod) { - if (bucket.period_ms != expectedPeriod) { - ignoreString - .append(" ") - .append(getAllChannels(bucket)) - .append("=period:") - .append(bucket.period_ms) - .append("!=") - .append(settings.periodInMs); - continue; - } - } else { - if (bucket.period_ms > expectedPeriod) { - ignoreString - .append(" ") - .append(getAllChannels(bucket)) - .append("=period:") - .append(bucket.period_ms) - .append(">") - .append(settings.periodInMs); - continue; - } - } - scheduleChannels.addChannels(bucket); - } - - assertTrue("expected that " + scheduleChannels.getAllChannels() + " contained " - + channelSet + ", Channel ignore reasons:" + ignoreString.toString(), - scheduleChannels.getAllChannels().containsAll(channelSet)); - } - - private static int[] getPredefinedBuckets() { - try { - Field f = MultiClientScheduler.class.getDeclaredField("PREDEFINED_BUCKET_PERIODS"); - f.setAccessible(true); - return (int[]) f.get(null); - } catch (Exception e) { - throw new RuntimeException("Could not get predefined buckets", e); - } - } - private static final int[] PREDEFINED_BUCKET_PERIODS = getPredefinedBuckets(); - - // find closest bucket period to the requested period - private static int computeExpectedPeriod(int requestedPeriod) { - int period = 0; - int minDiff = Integer.MAX_VALUE; - for (int bucketPeriod : PREDEFINED_BUCKET_PERIODS) { - int diff = Math.abs(bucketPeriod - requestedPeriod); - if (diff < minDiff) { - minDiff = diff; - period = bucketPeriod; - } - } - return period; - } - - // find closest bucket period to the requested period that exists in the schedule - private static int computeExpectedPeriod(int requestedPeriod, - WifiNative.ScanSettings schedule) { - int period = 0; - int minDiff = Integer.MAX_VALUE; - for (int i = 0; i < schedule.num_buckets; ++i) { - int bucketPeriod = schedule.buckets[i].period_ms; - int diff = Math.abs(bucketPeriod - requestedPeriod); - if (diff < minDiff) { - minDiff = diff; - period = bucketPeriod; - } - } - return period; - } -} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java index 3d4055a52..7d6982238 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java @@ -50,6 +50,7 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.app.IBatteryStats; import com.android.internal.util.Protocol; import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments; +import com.android.server.wifi.scanner.BackgroundScanScheduler; import com.android.server.wifi.scanner.ChannelHelper; import com.android.server.wifi.scanner.PresetKnownBandsChannelHelper; @@ -685,7 +686,7 @@ public class WifiScanningServiceTest { WifiNative.ScanSettings nativeSettings = new NativeScanSettingsBuilder() .withBasePeriod(20000) .withMaxApPerScan(MAX_AP_PER_SCAN) - .withMaxScansToCache(WifiScanningScheduler.DEFAULT_MAX_SCANS_TO_BATCH) + .withMaxScansToCache(BackgroundScanScheduler.DEFAULT_MAX_SCANS_TO_BATCH) .addBucketWithBand(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, WifiScanner.WIFI_BAND_BOTH) .build(); @@ -702,7 +703,7 @@ public class WifiScanningServiceTest { WifiNative.ScanSettings nativeSettings = new NativeScanSettingsBuilder() .withBasePeriod(20000) .withMaxApPerScan(MAX_AP_PER_SCAN) - .withMaxScansToCache(WifiScanningScheduler.DEFAULT_MAX_SCANS_TO_BATCH) + .withMaxScansToCache(BackgroundScanScheduler.DEFAULT_MAX_SCANS_TO_BATCH) .addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5150) .build(); doSuccessfulBackgroundScan(requestSettings, nativeSettings); diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerFilterTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerFilterTest.java new file mode 100644 index 000000000..65fe7bd7c --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerFilterTest.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.scanner; + +import static com.android.server.wifi.ScanTestUtil.channelsToSpec; +import static com.android.server.wifi.ScanTestUtil.createRequest; +import static com.android.server.wifi.ScanTestUtil.createScanDatas; +import static com.android.server.wifi.ScanTestUtil.createScanResult; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.validateMockitoUsage; + +import android.net.wifi.WifiScanner; +import android.net.wifi.WifiScanner.ScanData; +import android.net.wifi.WifiScanner.ScanSettings; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.wifi.WifiNative; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; + +/** + * Unit tests for filtering of scan results in + * {@link com.android.server.wifi.scanner.BackgroundScanScheduler}. + */ +@SmallTest +public class BackgroundScanSchedulerFilterTest { + + private static final int DEFAULT_MAX_BUCKETS = 8; + private static final int DEFAULT_MAX_CHANNELS = 8; + private static final int DEFAULT_MAX_BATCH = 10; + + private WifiNative mWifiNative; + private BackgroundScanScheduler mScheduler; + + @Before + public void setUp() throws Exception { + ChannelHelper channelHelper = new PresetKnownBandsChannelHelper( + new int[]{2400, 2450}, + new int[]{5150, 5175}, + new int[]{5600, 5650}); + mScheduler = new BackgroundScanScheduler(channelHelper); + mScheduler.setMaxBuckets(DEFAULT_MAX_BUCKETS); + mScheduler.setMaxChannels(DEFAULT_MAX_CHANNELS); + mScheduler.setMaxBatch(DEFAULT_MAX_BATCH); + } + + @After + public void cleanup() { + validateMockitoUsage(); + } + + @Test + public void reportFullResultTrueForBands() { + ScanSettings settings = createRequest( + WifiScanner.WIFI_BAND_24_GHZ, 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + assertTrue(mScheduler.shouldReportFullScanResultForSettings( + createScanResult(2400), settings)); + } + + @Test + public void reportFullResultFalseForBands() { + ScanSettings settings = createRequest( + WifiScanner.WIFI_BAND_24_GHZ, 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + assertFalse(mScheduler.shouldReportFullScanResultForSettings( + createScanResult(5150), settings)); + } + + @Test + public void reportFullResultTrueForChannels() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + assertTrue(mScheduler.shouldReportFullScanResultForSettings( + createScanResult(2400), settings)); + } + + @Test + public void reportFullResultFalseForChannels() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + assertFalse(mScheduler.shouldReportFullScanResultForSettings( + createScanResult(5175), settings)); + } + + @Test + public void filterScanDataEmpty() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings(new ScanData[0], settings); + assertScanDataFreqsEquals(null, results); + } + + @Test + public void filterScanDataSingleNotMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings( + createScanDatas(new int[][]{ { 2450 } }), settings); + assertScanDataFreqsEquals(null, results); + } + + @Test + public void filterScanDataSingleMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings( + createScanDatas(new int[][]{ { 2400 } }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400 } }, results); + } + + @Test + public void filterScanDataSinglePartialMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings( + createScanDatas(new int[][]{ { 2400, 2450, 5150, 5175 } }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400, 5150 } }, results); + } + + @Test + public void filterScanDataMultipleNotMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings( + createScanDatas(new int[][]{ { 2450 }, { 2450, 5175 } }), settings); + assertScanDataFreqsEquals(null, results); + } + + @Test + public void filterScanDataMultipleMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings( + createScanDatas(new int[][]{ { 2400 }, {2400, 5150} }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400 }, {2400, 5150} }, results); + } + + @Test + public void filterScanDataMultiplePartialMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( + new int[][]{ { 2400, 2450, 5150, 5175 }, { 2400, 2450, 5175 } }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400, 5150 }, { 2400 } }, results); + } + + @Test + public void filterScanDataMultipleDuplicateFrequencies() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( + new int[][]{ { 2400, 2450, 5150, 5175, 2400 }, + { 2400, 2450, 5175 }, + { 5175, 5175, 5150 } }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400, 5150, 2400 }, { 2400 }, { 5150 } }, results); + } + + @Test + public void filterScanDataMultipleSomeNotMatching() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( + new int[][]{ { 2400, 2450, 5150, 5175, 2400 }, + { 5175 }, + { 5175, 5175, 5150 } }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400, 5150, 2400 }, { 5150 } }, results); + } + + @Test + public void filterScanDataExceedMaxBssidsPerScan() { + ScanSettings settings = createRequest( + channelsToSpec(2400, 5150), 30000, 0, 3, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + ); + Collection requests = Collections.singleton(settings); + mScheduler.updateSchedule(requests); + + ScanData[] results = mScheduler.filterResultsForSettings(createScanDatas( + new int[][]{ { 2400, 2450, 5150, 5175, 2400, 2400}, + { 5175 }, + { 5175, 5175, 5150, 2400, 2400, 5150 } }), settings); + + assertScanDataFreqsEquals(new int[][]{ { 2400, 5150, 2400 }, { 5150, 2400, 2400 } }, + results); + } + + + public static void assertScanDataFreqsEquals(int[][] expected, ScanData[] results) { + if (expected == null) { + assertNull(results); + } else { + assertNotNull(results); + assertEquals("num scans", expected.length, results.length); + for (int i = 0; i < expected.length; ++i) { + assertNotNull("scan[" + i + "] was null", results[i]); + assertEquals("num aps in scan[" + i + "]", expected[i].length, + results[i].getResults().length); + for (int j = 0; j < expected[i].length; ++j) { + assertNotNull("ap result[" + i + "][" + j + "] was null", + results[i].getResults()[j]); + assertEquals("ap freq in result[" + i + "][" + j + "]", expected[i][j], + results[i].getResults()[j].frequency); + } + } + } + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java new file mode 100644 index 000000000..b3abbafad --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/scanner/BackgroundScanSchedulerTest.java @@ -0,0 +1,653 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.scanner; + +import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; +import static com.android.server.wifi.ScanTestUtil.assertNativeScanSettingsEquals; +import static com.android.server.wifi.ScanTestUtil.channelsToSpec; +import static com.android.server.wifi.ScanTestUtil.createRequest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.validateMockitoUsage; + +import android.net.wifi.WifiScanner; +import android.net.wifi.WifiScanner.ScanSettings; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiNative.BucketSettings; +import com.android.server.wifi.scanner.KnownBandsChannelHelper.KnownBandsChannelCollection; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * Unit tests for {@link com.android.server.wifi.scanner.BackgroundScanScheduler}. + */ +@SmallTest +public class BackgroundScanSchedulerTest { + + private static final int DEFAULT_MAX_BUCKETS = 9; + private static final int DEFAULT_MAX_CHANNELS = 23; + private static final int DEFAULT_MAX_BATCH = 11; + private static final int DEFAULT_MAX_AP_PER_SCAN = 33; + + private KnownBandsChannelHelper mChannelHelper; + private BackgroundScanScheduler mScheduler; + + @Before + public void setUp() throws Exception { + mChannelHelper = new PresetKnownBandsChannelHelper( + new int[]{2400, 2450}, + new int[]{5150, 5175}, + new int[]{5600, 5650, 5660}); + mScheduler = new BackgroundScanScheduler(mChannelHelper); + mScheduler.setMaxBuckets(DEFAULT_MAX_BUCKETS); + mScheduler.setMaxChannels(DEFAULT_MAX_CHANNELS); + mScheduler.setMaxBatch(DEFAULT_MAX_BATCH); + mScheduler.setMaxApPerScan(DEFAULT_MAX_AP_PER_SCAN); + } + + @After + public void cleanup() { + validateMockitoUsage(); + } + + @Test + public void noRequest() { + Collection requests = Collections.emptyList(); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals(40000, schedule.base_period_ms); + assertBuckets(schedule, 0); + } + + @Test + public void singleRequest() { + Collection requests = Collections.singleton(createRequest( + WifiScanner.WIFI_BAND_BOTH, 20000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + )); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals(20000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + @Test + public void singleRequestWithoutPredefinedBucket() { + Collection requests = Collections.singleton(createRequest( + WifiScanner.WIFI_BAND_BOTH, 7500, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT + )); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 10000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + @Test + public void fewRequests() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 30000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 14000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 10000, schedule.base_period_ms); + assertBuckets(schedule, 2); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + @Test + public void manyRequests() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 15000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 10000, schedule.base_period_ms); + assertBuckets(schedule, 2); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, false); + } + } + + @Test + public void requestsWithNoPeriodCommonDenominator() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 299999, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10500, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 10000, schedule.base_period_ms); + assertBuckets(schedule, 2); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + @Test + public void manyRequestsDifferentReportScans() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(5175), 40000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); + requests.add(createRequest(channelsToSpec(2400), 40000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(2450), 40000, 0, 20, + WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + requests.add(createRequest(channelsToSpec(5150), 40000, 0, 20, + WifiScanner.REPORT_EVENT_NO_BATCH)); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 40000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + @Test + public void exceedMaxBatch() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(5175), 20000, 10, 20, + WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); + + mScheduler.setMaxBatch(5); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + assertEquals("maxScansToCache", 5, schedule.report_threshold_num_scans); + } + + @Test + public void defaultMaxBatch() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(5175), 40000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); + + mScheduler.setMaxBatch(6); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 40000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + assertEquals("maxScansToCache", 6, schedule.report_threshold_num_scans); + } + + @Test + public void exceedMaxAps() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(5175), 30000, 10, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxApPerScan(5); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("maxScansToCache", 5, schedule.max_ap_per_scan); + } + + @Test + public void defaultMaxAps() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(5175), 30000, 10, 0, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxApPerScan(8); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("maxApsPerScan", 8, schedule.max_ap_per_scan); + } + + @Test + public void optimalScheduleExceedsNumberOfAvailableBuckets() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(2400), 20000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(2450), 10000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5150), 40000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxBuckets(2); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 2); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, true, true); + } + } + + @Test + public void optimalScheduleExceedsNumberOfAvailableBuckets2() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(2400), 20000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(2450), 40000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5150), 2560000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxBuckets(2); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 2); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, true, true); + } + } + + /** + * Ensure that a channel request is placed in the bucket closest to the original + * period and not the bucket it is initially placed in. Here the 12 min period is + * initially placed in the 640s bucket, but that bucket is eliminated because it + * would be a 7th bucket. This test ensures that the request is placed in the 1280s + * bucket and not the 320s bucket. + */ + @Test + public void optimalScheduleExceedsNumberOfAvailableBucketsClosestToOriginal() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(2400), 40 * 1000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(2450), 20 * 1000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5150), 160 * 1000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5175), 320 * 1000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5600), 10 * 1000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5650), 1280 * 1000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + requests.add(createRequest(channelsToSpec(5660), 720 * 1000, 0, 20, // 12 min + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxBuckets(6); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 10000, schedule.base_period_ms); + assertBuckets(schedule, 6); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, true, true); + } + } + + @Test + public void optimalScheduleExceedsMaxChannelsOnSingleBand() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(2400, 2450), 20000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxBuckets(2); + mScheduler.setMaxChannels(1); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, true, true); + } + } + + @Test + public void optimalScheduleExceedsMaxChannelsOnMultipleBands() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(2400, 2450, 5150), 20000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxBuckets(2); + mScheduler.setMaxChannels(2); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, true, true); + } + } + + @Test + public void optimalScheduleExceedsMaxChannelsOnMultipleBandsFromMultipleRequests() { + ArrayList requests = new ArrayList<>(); + requests.add(createRequest(channelsToSpec(2400, 2450), 20000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(WifiScanner.WIFI_BAND_5_GHZ, 20000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + + mScheduler.setMaxBuckets(2); + mScheduler.setMaxChannels(2); + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, true, true); + } + } + + @Test + public void exactRequests() { + scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, 0, + 20, WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); + scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_5_GHZ, 40000, 3, + 13, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, 10000, 2, + 10, WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)); + scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 25000, 0, + 10, WifiScanner.REPORT_EVENT_NO_BATCH)); + scheduleAndTestExactRequest(createRequest(WifiScanner.WIFI_BAND_BOTH, 25000, 3, + 0, WifiScanner.REPORT_EVENT_NO_BATCH)); + scheduleAndTestExactRequest(createRequest(channelsToSpec(2400, 5175, 5650) , 25000, 3, + 0, WifiScanner.REPORT_EVENT_NO_BATCH)); + } + + @Test + public void singleExponentialBackOffRequest() { + Collection requests = Collections.singleton(createRequest( + WifiScanner.WIFI_BAND_BOTH, 20000, 160000, 2, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN + )); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals(20000, schedule.base_period_ms); + assertBuckets(schedule, 1); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + @Test + public void exponentialBackOffAndRegularRequests() { + Collection requests = new ArrayList<>(); + requests.add(createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, 200000, 1, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)); + requests.add(createRequest(channelsToSpec(5175), 30000, 0, 20, + WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL)); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + assertEquals("base_period_ms", 20000, schedule.base_period_ms); + assertBuckets(schedule, 2); + for (ScanSettings request : requests) { + assertSettingsSatisfied(schedule, request, false, true); + } + } + + protected Set getAllChannels(BucketSettings bucket) { + KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); + collection.addChannels(bucket); + return collection.getAllChannels(); + } + + protected Set getAllChannels(WifiScanner.ScanSettings settings) { + KnownBandsChannelCollection collection = mChannelHelper.createChannelCollection(); + collection.addChannels(settings); + return collection.getAllChannels(); + } + + public void scheduleAndTestExactRequest(ScanSettings settings) { + Collection requests = new ArrayList<>(); + requests.add(settings); + + mScheduler.updateSchedule(requests); + WifiNative.ScanSettings schedule = mScheduler.getSchedule(); + + int expectedPeriod = computeExpectedPeriod(settings.periodInMs); + NativeScanSettingsBuilder expectedBuilder = new NativeScanSettingsBuilder() + .withBasePeriod(expectedPeriod) + .withMaxApPerScan(settings.numBssidsPerScan == 0 + ? DEFAULT_MAX_AP_PER_SCAN + : settings.numBssidsPerScan) + .withMaxScansToCache(settings.maxScansToCache == 0 + ? DEFAULT_MAX_BATCH + : settings.maxScansToCache); + + if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + expectedBuilder.addBucketWithChannels(expectedPeriod, settings.reportEvents, + settings.channels); + } else { + expectedBuilder.addBucketWithBand(expectedPeriod, settings.reportEvents, settings.band); + } + assertNativeScanSettingsEquals(expectedBuilder.build(), schedule); + } + + private void assertBuckets(WifiNative.ScanSettings schedule, int numBuckets) { + assertEquals("num_buckets", numBuckets, schedule.num_buckets); + assertNotNull("buckets was null", schedule.buckets); + assertEquals("num_buckets and actual buckets", schedule.num_buckets, + schedule.buckets.length); + for (int i = 0; i < numBuckets; i++) { + assertNotNull("bucket[" + i + "] was null", schedule.buckets[i]); + if (schedule.buckets[i].band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + assertTrue("num channels <= 0", schedule.buckets[i].num_channels > 0); + assertTrue("bucket channels > max channels", + schedule.buckets[i].num_channels <= mScheduler.getMaxChannels()); + assertNotNull("Channels was null", schedule.buckets[i].channels); + for (int c = 0; c < schedule.buckets[i].num_channels; c++) { + assertNotNull("Channel was null", schedule.buckets[i].channels[c]); + } + } else { + assertTrue("Invalid band: " + schedule.buckets[i].band, + schedule.buckets[i].band > WifiScanner.WIFI_BAND_UNSPECIFIED + && schedule.buckets[i].band <= WifiScanner.WIFI_BAND_BOTH_WITH_DFS); + } + } + } + + private void assertSettingsSatisfied(WifiNative.ScanSettings schedule, + ScanSettings settings, boolean bucketsLimited, boolean exactPeriod) { + assertTrue("bssids per scan: " + schedule.max_ap_per_scan + " /<= " + + settings.numBssidsPerScan, + schedule.max_ap_per_scan <= settings.numBssidsPerScan); + + if (settings.maxScansToCache > 0) { + assertTrue("scans to cache: " + schedule.report_threshold_num_scans + " /<= " + + settings.maxScansToCache, + schedule.report_threshold_num_scans <= settings.maxScansToCache); + } + + Set channelSet = getAllChannels(settings); + + StringBuilder ignoreString = new StringBuilder(); + + KnownBandsChannelCollection scheduleChannels = mChannelHelper.createChannelCollection(); + for (int b = 0; b < schedule.num_buckets; b++) { + BucketSettings bucket = schedule.buckets[b]; + if ((settings.reportEvents & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { + if ((bucket.report_events & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) == 0) { + ignoreString + .append(" ") + .append(getAllChannels(bucket)) + .append("=after_each_scan:") + .append(bucket.report_events & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) + .append("!=") + .append(settings.reportEvents + & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + continue; + } + } + if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { + if ((bucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) == 0) { + ignoreString + .append(" ") + .append(getAllChannels(bucket)) + .append("=full_result:") + .append(bucket.report_events + & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) + .append("!=") + .append(settings.reportEvents + & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT); + continue; + } + } + if ((settings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { + if ((bucket.report_events & WifiScanner.REPORT_EVENT_NO_BATCH) != 0) { + ignoreString + .append(" ") + .append(getAllChannels(bucket)) + .append("=no_batch:") + .append(bucket.report_events & WifiScanner.REPORT_EVENT_NO_BATCH) + .append("!=") + .append(settings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH); + continue; + } + } + int expectedPeriod; + + if (settings.maxPeriodInMs != 0 && settings.periodInMs != settings.maxPeriodInMs) { + // exponential back off scan + expectedPeriod = settings.periodInMs; + } else { + if (bucketsLimited) { + expectedPeriod = computeExpectedPeriod(settings.periodInMs, schedule); + } else { + expectedPeriod = computeExpectedPeriod(settings.periodInMs); + } + } + + if (exactPeriod) { + if (bucket.period_ms != expectedPeriod) { + ignoreString + .append(" ") + .append(getAllChannels(bucket)) + .append("=period:") + .append(bucket.period_ms) + .append("!=") + .append(settings.periodInMs); + continue; + } + } else { + if (bucket.period_ms > expectedPeriod) { + ignoreString + .append(" ") + .append(getAllChannels(bucket)) + .append("=period:") + .append(bucket.period_ms) + .append(">") + .append(settings.periodInMs); + continue; + } + } + scheduleChannels.addChannels(bucket); + } + + assertTrue("expected that " + scheduleChannels.getAllChannels() + " contained " + + channelSet + ", Channel ignore reasons:" + ignoreString.toString(), + scheduleChannels.getAllChannels().containsAll(channelSet)); + } + + private static int[] getPredefinedBuckets() { + try { + Field f = BackgroundScanScheduler.class.getDeclaredField("PREDEFINED_BUCKET_PERIODS"); + f.setAccessible(true); + return (int[]) f.get(null); + } catch (Exception e) { + throw new RuntimeException("Could not get predefined buckets", e); + } + } + private static final int[] PREDEFINED_BUCKET_PERIODS = getPredefinedBuckets(); + + // find closest bucket period to the requested period + private static int computeExpectedPeriod(int requestedPeriod) { + int period = 0; + int minDiff = Integer.MAX_VALUE; + for (int bucketPeriod : PREDEFINED_BUCKET_PERIODS) { + int diff = Math.abs(bucketPeriod - requestedPeriod); + if (diff < minDiff) { + minDiff = diff; + period = bucketPeriod; + } + } + return period; + } + + // find closest bucket period to the requested period that exists in the schedule + private static int computeExpectedPeriod(int requestedPeriod, + WifiNative.ScanSettings schedule) { + int period = 0; + int minDiff = Integer.MAX_VALUE; + for (int i = 0; i < schedule.num_buckets; ++i) { + int bucketPeriod = schedule.buckets[i].period_ms; + int diff = Math.abs(bucketPeriod - requestedPeriod); + if (diff < minDiff) { + minDiff = diff; + period = bucketPeriod; + } + } + return period; + } +} -- cgit v1.2.3