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