1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi.scanner; 18 19 import android.annotation.Nullable; 20 import android.net.wifi.ScanResult; 21 import android.net.wifi.WifiScanner.ScanData; 22 import android.net.wifi.WifiScanner.ScanSettings; 23 24 import com.android.server.wifi.WifiNative; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 29 /** 30 * A class with utilities for dealing with scan schedules. 31 */ 32 public class ScanScheduleUtil { 33 34 /** 35 * Compares two ChannelSettings for equality. 36 */ channelEquals(@ullable WifiNative.ChannelSettings channel1, @Nullable WifiNative.ChannelSettings channel2)37 public static boolean channelEquals(@Nullable WifiNative.ChannelSettings channel1, 38 @Nullable WifiNative.ChannelSettings channel2) { 39 if (channel1 == null || channel2 == null) return false; 40 if (channel1 == channel2) return true; 41 42 if (channel1.frequency != channel2.frequency) return false; 43 if (channel1.dwell_time_ms != channel2.dwell_time_ms) return false; 44 return channel1.passive == channel2.passive; 45 } 46 47 /** 48 * Compares two BucketSettings for equality. 49 */ bucketEquals(@ullable WifiNative.BucketSettings bucket1, @Nullable WifiNative.BucketSettings bucket2)50 public static boolean bucketEquals(@Nullable WifiNative.BucketSettings bucket1, 51 @Nullable WifiNative.BucketSettings bucket2) { 52 if (bucket1 == null || bucket2 == null) return false; 53 if (bucket1 == bucket2) return true; 54 55 if (bucket1.bucket != bucket2.bucket) return false; 56 if (bucket1.band != bucket2.band) return false; 57 if (bucket1.period_ms != bucket2.period_ms) return false; 58 if (bucket1.report_events != bucket2.report_events) return false; 59 if (bucket1.num_channels != bucket2.num_channels) return false; 60 for (int c = 0; c < bucket1.num_channels; c++) { 61 if (!channelEquals(bucket1.channels[c], bucket2.channels[c])) { 62 return false; 63 } 64 } 65 66 return true; 67 } 68 69 /** 70 * Compares two ScanSettings for equality. 71 */ scheduleEquals(@ullable WifiNative.ScanSettings schedule1, @Nullable WifiNative.ScanSettings schedule2)72 public static boolean scheduleEquals(@Nullable WifiNative.ScanSettings schedule1, 73 @Nullable WifiNative.ScanSettings schedule2) { 74 if (schedule1 == null || schedule2 == null) return false; 75 if (schedule1 == schedule2) return true; 76 77 if (schedule1.base_period_ms != schedule2.base_period_ms) return false; 78 if (schedule1.max_ap_per_scan != schedule2.max_ap_per_scan) return false; 79 if (schedule1.report_threshold_percent != schedule2.report_threshold_percent) return false; 80 if (schedule1.report_threshold_num_scans != schedule2.report_threshold_num_scans) { 81 return false; 82 } 83 if (schedule1.num_buckets != schedule2.num_buckets) return false; 84 for (int b = 0; b < schedule1.num_buckets; b++) { 85 if (!bucketEquals(schedule1.buckets[b], schedule2.buckets[b])) { 86 return false; 87 } 88 } 89 90 return true; 91 } 92 93 /** 94 * Check if the specified bucket was scanned. If not all information is available then this 95 * method will return true. 96 * 97 * @param scheduledBucket Index of the bucket to check for, zero indexed, or -1 if any scan 98 * should be treated as scanning this bucket. 99 * @param bucketsScannedBitSet The bitset of all buckets scanned, 0 if unavailable 100 */ isBucketMaybeScanned(int scheduledBucket, int bucketsScannedBitSet)101 private static boolean isBucketMaybeScanned(int scheduledBucket, int bucketsScannedBitSet) { 102 if (bucketsScannedBitSet == 0 || scheduledBucket < 0) { 103 return true; 104 } else { 105 return (bucketsScannedBitSet & (1 << scheduledBucket)) != 0; 106 } 107 } 108 109 /** 110 * Check if the specified bucket was scanned. If not all information is available then this 111 * method will return false. 112 * 113 * @param scheduledBucket Index of the bucket to check for, zero indexed, or -1 if any scan 114 * should be treated as scanning this bucket. 115 * @param bucketsScannedBitSet The bitset of all buckets scanned, 0 if unavailable 116 */ isBucketDefinitlyScanned(int scheduledBucket, int bucketsScannedBitSet)117 private static boolean isBucketDefinitlyScanned(int scheduledBucket, int bucketsScannedBitSet) { 118 if (scheduledBucket < 0) { 119 return true; 120 } else if (bucketsScannedBitSet == 0) { 121 return false; 122 } else { 123 return (bucketsScannedBitSet & (1 << scheduledBucket)) != 0; 124 } 125 } 126 127 /** 128 * Returns true if the given scan result should be reported to a listener with the given 129 * settings. 130 */ shouldReportFullScanResultForSettings(ChannelHelper channelHelper, ScanResult result, int bucketsScanned, ScanSettings settings, int scheduledBucket)131 public static boolean shouldReportFullScanResultForSettings(ChannelHelper channelHelper, 132 ScanResult result, int bucketsScanned, ScanSettings settings, int scheduledBucket) { 133 if (isBucketMaybeScanned(scheduledBucket, bucketsScanned)) { 134 return channelHelper.settingsContainChannel(settings, result.frequency); 135 } else { 136 return false; 137 } 138 } 139 140 /** 141 * Returns a filtered version of the scan results from the chip that represents only the data 142 * requested in the settings. Will return null if the result should not be reported. 143 * 144 * If a ScanData indicates that the bucket the settings were placed in was scanned then it 145 * will always be included (filtered to only include requested channels). If it indicates that 146 * the bucket was definitely not scanned then the scan data will not be reported. 147 * If it is not possible to determine if the settings bucket was scanned or not then a 148 * ScanData will be included if the scan was empty or there was at least one scan result that 149 * matches a requested channel (again the results will be filtered to only include requested 150 * channels. 151 */ filterResultsForSettings(ChannelHelper channelHelper, ScanData[] scanDatas, ScanSettings settings, int scheduledBucket)152 public static ScanData[] filterResultsForSettings(ChannelHelper channelHelper, 153 ScanData[] scanDatas, ScanSettings settings, int scheduledBucket) { 154 List<ScanData> filteredScanDatas = new ArrayList<>(scanDatas.length); 155 List<ScanResult> filteredResults = new ArrayList<>(); 156 for (ScanData scanData : scanDatas) { 157 // only report ScanData if the settings bucket could have been scanned 158 if (isBucketMaybeScanned(scheduledBucket, scanData.getBucketsScanned())) { 159 filteredResults.clear(); 160 for (ScanResult scanResult : scanData.getResults()) { 161 if (channelHelper.settingsContainChannel(settings, scanResult.frequency)) { 162 filteredResults.add(scanResult); 163 } 164 if (settings.numBssidsPerScan > 0 165 && filteredResults.size() >= settings.numBssidsPerScan) { 166 break; 167 } 168 } 169 // will include scan results if the scan was empty, there was at least one 170 // one result that matched the scan request or we are sure that all the requested 171 // channels were scanned. 172 if (filteredResults.size() == scanData.getResults().length) { 173 filteredScanDatas.add(scanData); 174 } else if (filteredResults.size() > 0 || isBucketDefinitlyScanned(scheduledBucket, 175 scanData.getBucketsScanned())) { 176 filteredScanDatas.add(new ScanData(scanData.getId(), 177 scanData.getFlags(), 178 0, 179 scanData.getBandScanned(), 180 filteredResults.toArray( 181 new ScanResult[filteredResults.size()]))); 182 } 183 } 184 } 185 if (filteredScanDatas.size() == 0) { 186 return null; 187 } else { 188 return filteredScanDatas.toArray(new ScanData[filteredScanDatas.size()]); 189 } 190 } 191 } 192