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