1 /*
2  * Copyright (C) 2016 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.net.wifi.WifiScanner;
20 import android.util.ArraySet;
21 
22 import com.android.server.wifi.WifiNative;
23 
24 import java.util.Set;
25 import java.util.StringJoiner;
26 
27 /**
28  * ChannelHelper offers an abstraction for channel manipulation utilities allowing operation to be
29  * adjusted based on the amount of information known about the available channels.
30  */
31 public abstract class ChannelHelper {
32 
33     // TODO: Currently this is simply an estimate and is used for both active and passive channels
34     //       scans. Eventually it should be split between passive and active and perhaps retrieved
35     //       from the driver.
36     /**
37      * The estimated period spent scanning each channel. This is used for estimating scan duration.
38      */
39     public static final int SCAN_PERIOD_PER_CHANNEL_MS = 200;
40 
41     protected static final WifiScanner.ChannelSpec[] NO_CHANNELS = new WifiScanner.ChannelSpec[0];
42 
43     /**
44      * Create a new collection that can be used to store channels
45      */
createChannelCollection()46     public abstract ChannelCollection createChannelCollection();
47 
48     /**
49      * Return true if the specified channel is expected for a scan with the given settings
50      */
settingsContainChannel(WifiScanner.ScanSettings settings, int channel)51     public abstract boolean settingsContainChannel(WifiScanner.ScanSettings settings, int channel);
52 
53     /**
54      * Get the channels that are available for scanning on the supplied band.
55      * This method may return empty if the information is not available.
56      * The channels will be returned in a 2d array, each row will represent channels within a
57      * {@link #WifiBandBasic}.
58      * For example, if band is WIFI_BAND_BOTH (for both 2.4GHz and 5GHz no DFS),
59      * the returned 2d array will be something like:
60      * [[2412, 2417, 2422],[5180, 5190, 5200, 5210,5220],[]]
61      * The first row is the 2.4GHz channels, second row is the 5GHz (no DFS channels), and the third
62      * row is empty (since the requested band does not include DFS channels).
63      */
getAvailableScanChannels(int band)64     public abstract WifiScanner.ChannelSpec[][] getAvailableScanChannels(int band);
65 
66     /**
67      * Compares the channels / bands available from this helper with the channels / bands available
68      * from the other channel helper.
69      *
70      * @return true if the all the channels available from the other channel helper is also
71      * available in this helper.
72      */
satisfies(ChannelHelper otherChannelHelper)73     public abstract boolean satisfies(ChannelHelper otherChannelHelper);
74 
75     /**
76      * Estimates the duration that the chip will spend scanning with the given settings
77      */
estimateScanDuration(WifiScanner.ScanSettings settings)78     public abstract int estimateScanDuration(WifiScanner.ScanSettings settings);
79 
80     /**
81      * Update the channel information that this object has. The source of the update is
82      * implementation dependent and may result in no change. Warning the behavior of a
83      * ChannelCollection created using {@link #createChannelCollection createChannelCollection} is
84      * undefined after calling this method until the {@link ChannelColleciton#clear() clear} method
85      * is called on it.
86      */
updateChannels()87     public void updateChannels() {
88         // default implementation does nothing
89     }
90 
91     /**
92      * Object that supports accumulation of channels and bands
93      */
94     public abstract class ChannelCollection {
95         /**
96          * Add a channel to the collection
97          */
addChannel(int channel)98         public abstract void addChannel(int channel);
99         /**
100          * Add all channels in the band to the collection
101          */
addBand(int band)102         public abstract void addBand(int band);
103         /**
104          * @return true if the collection contains the supplied channel
105          */
containsChannel(int channel)106         public abstract boolean containsChannel(int channel);
107         /**
108          * @return true if the collection contains all the channels of the supplied band
109          */
containsBand(int band)110         public abstract boolean containsBand(int band);
111         /**
112          * @return true if the collection contains some of the channels of the supplied band
113          */
partiallyContainsBand(int band)114         public abstract boolean partiallyContainsBand(int band);
115         /**
116          * @return true if the collection contains no channels
117          */
isEmpty()118         public abstract boolean isEmpty();
119         /**
120          * @return true if the collection contains all available channels
121          */
isAllChannels()122         public abstract boolean isAllChannels();
123         /**
124          * Remove all channels from the collection
125          */
clear()126         public abstract void clear();
127         /**
128          * Retrieves a list of channels from the band which are missing in the channel collection.
129          */
getMissingChannelsFromBand(int band)130         public abstract Set<Integer> getMissingChannelsFromBand(int band);
131         /**
132          * Retrieves a list of channels from the band which are contained in the channel collection.
133          */
getContainingChannelsFromBand(int band)134         public abstract Set<Integer> getContainingChannelsFromBand(int band);
135         /**
136          * Gets a list of channels specified in the current channel collection. This will return
137          * an empty set if an entire Band if specified or if the list is empty.
138          */
getChannelSet()139         public abstract Set<Integer> getChannelSet();
140 
141         /**
142          * Add all channels in the ScanSetting to the collection
143          */
addChannels(WifiScanner.ScanSettings scanSettings)144         public void addChannels(WifiScanner.ScanSettings scanSettings) {
145             if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
146                 for (int j = 0; j < scanSettings.channels.length; ++j) {
147                     addChannel(scanSettings.channels[j].frequency);
148                 }
149             } else {
150                 addBand(scanSettings.band);
151             }
152         }
153 
154         /**
155          * Add all channels in the BucketSettings to the collection
156          */
addChannels(WifiNative.BucketSettings bucketSettings)157         public void addChannels(WifiNative.BucketSettings bucketSettings) {
158             if (bucketSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
159                 for (int j = 0; j < bucketSettings.channels.length; ++j) {
160                     addChannel(bucketSettings.channels[j].frequency);
161                 }
162             } else {
163                 addBand(bucketSettings.band);
164             }
165         }
166 
167         /**
168          * Checks if all channels in ScanSetting is in the collection
169          */
containsSettings(WifiScanner.ScanSettings scanSettings)170         public boolean containsSettings(WifiScanner.ScanSettings scanSettings) {
171             if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
172                 for (int j = 0; j < scanSettings.channels.length; ++j) {
173                     if (!containsChannel(scanSettings.channels[j].frequency)) {
174                         return false;
175                     }
176                 }
177                 return true;
178             } else {
179                 return containsBand(scanSettings.band);
180             }
181         }
182 
183         /**
184          * Checks if at least some of the channels in ScanSetting is in the collection
185          */
partiallyContainsSettings(WifiScanner.ScanSettings scanSettings)186         public boolean partiallyContainsSettings(WifiScanner.ScanSettings scanSettings) {
187             if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
188                 for (int j = 0; j < scanSettings.channels.length; ++j) {
189                     if (containsChannel(scanSettings.channels[j].frequency)) {
190                         return true;
191                     }
192                 }
193                 return false;
194             } else {
195                 return partiallyContainsBand(scanSettings.band);
196             }
197         }
198 
199         /**
200          * Retrieves a list of missing channels in the collection from the provided settings.
201          */
getMissingChannelsFromSettings(WifiScanner.ScanSettings scanSettings)202         public Set<Integer> getMissingChannelsFromSettings(WifiScanner.ScanSettings scanSettings) {
203             if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
204                 ArraySet<Integer> missingChannels = new ArraySet<>();
205                 for (int j = 0; j < scanSettings.channels.length; ++j) {
206                     if (!containsChannel(scanSettings.channels[j].frequency)) {
207                         missingChannels.add(scanSettings.channels[j].frequency);
208                     }
209                 }
210                 return missingChannels;
211             } else {
212                 return getMissingChannelsFromBand(scanSettings.band);
213             }
214         }
215 
216         /**
217          * Retrieves a list of containing channels in the collection from the provided settings.
218          */
getContainingChannelsFromSettings( WifiScanner.ScanSettings scanSettings)219         public Set<Integer> getContainingChannelsFromSettings(
220                 WifiScanner.ScanSettings scanSettings) {
221             if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
222                 ArraySet<Integer> containingChannels = new ArraySet<>();
223                 for (int j = 0; j < scanSettings.channels.length; ++j) {
224                     if (containsChannel(scanSettings.channels[j].frequency)) {
225                         containingChannels.add(scanSettings.channels[j].frequency);
226                     }
227                 }
228                 return containingChannels;
229             } else {
230                 return getContainingChannelsFromBand(scanSettings.band);
231             }
232         }
233 
234         /**
235          * Store the channels in this collection in the supplied BucketSettings. If maxChannels is
236          * exceeded or a band better describes the channels then a band is specified instead of a
237          * channel list.
238          */
fillBucketSettings(WifiNative.BucketSettings bucket, int maxChannels)239         public abstract void fillBucketSettings(WifiNative.BucketSettings bucket, int maxChannels);
240 
241         /**
242          * Gets the list of channels scan. Will either be a collection of all channels or null
243          * if all channels should be scanned.
244          */
getScanFreqs()245         public abstract Set<Integer> getScanFreqs();
246     }
247 
248 
249     /*
250      * Utility methods for converting band/channels to strings
251      */
252 
253     /**
254      * Create a string representation of the channels in the ScanSettings.
255      * If it contains a list of channels then the channels are returned, otherwise a string name of
256      * the band is returned.
257      */
toString(WifiScanner.ScanSettings scanSettings)258     public static String toString(WifiScanner.ScanSettings scanSettings) {
259         if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
260             return toString(scanSettings.channels);
261         } else {
262             return bandToString(scanSettings.band);
263         }
264     }
265 
266     /**
267      * Create a string representation of the channels in the BucketSettings.
268      * If it contains a list of channels then the channels are returned, otherwise a string name of
269      * the band is returned.
270      */
toString(WifiNative.BucketSettings bucketSettings)271     public static String toString(WifiNative.BucketSettings bucketSettings) {
272         if (bucketSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
273             return toString(bucketSettings.channels, bucketSettings.num_channels);
274         } else {
275             return bandToString(bucketSettings.band);
276         }
277     }
278 
toString(WifiScanner.ChannelSpec[] channels)279     private static String toString(WifiScanner.ChannelSpec[] channels) {
280         if (channels == null) {
281             return "null";
282         }
283 
284         StringBuilder sb = new StringBuilder();
285         sb.append("[");
286         for (int c = 0; c < channels.length; c++) {
287             sb.append(channels[c].frequency);
288             if (c != channels.length - 1) {
289                 sb.append(",");
290             }
291         }
292         sb.append("]");
293         return sb.toString();
294     }
295 
toString(WifiNative.ChannelSettings[] channels, int numChannels)296     private static String toString(WifiNative.ChannelSettings[] channels, int numChannels) {
297         if (channels == null) {
298             return "null";
299         }
300 
301         StringBuilder sb = new StringBuilder();
302         sb.append("[");
303         for (int c = 0; c < numChannels; c++) {
304             sb.append(channels[c].frequency);
305             if (c != numChannels - 1) {
306                 sb.append(",");
307             }
308         }
309         sb.append("]");
310         return sb.toString();
311     }
312 
313     /**
314      * Converts a WifiScanner.WIFI_BAND_* constant to a meaningful String
315      */
bandToString(int band)316     public static String bandToString(int band) {
317         StringJoiner sj = new StringJoiner(" & ");
318         sj.setEmptyValue("unspecified");
319 
320         if ((band & WifiScanner.WIFI_BAND_24_GHZ) != 0) {
321             sj.add("24Ghz");
322         }
323         band &= ~WifiScanner.WIFI_BAND_24_GHZ;
324 
325         switch (band & WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS) {
326             case WifiScanner.WIFI_BAND_5_GHZ:
327                 sj.add("5Ghz (no DFS)");
328                 break;
329             case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
330                 sj.add("5Ghz (DFS only)");
331                 break;
332             case WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS:
333                 sj.add("5Ghz (DFS incl)");
334                 break;
335         }
336         band &= ~WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS;
337 
338         if ((band & WifiScanner.WIFI_BAND_6_GHZ) != 0) {
339             sj.add("6Ghz");
340         }
341         band &= ~WifiScanner.WIFI_BAND_6_GHZ;
342 
343         if (band != 0) {
344             return "Invalid band";
345         }
346         return sj.toString();
347     }
348 }
349