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