1/*
2 * Copyright (C) 2008 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 android.net.wifi;
18
19import android.os.Parcelable;
20import android.os.Parcel;
21
22import java.util.ArrayList;
23import java.util.Collection;
24
25/**
26 * Describes the settings for batched wifi scans where the firmware performs many
27 * scans and stores the timestamped results without waking the main processor each time.
28 * This can give information over time with minimal battery impact.
29 * @hide pending review
30 */
31public class BatchedScanSettings implements Parcelable {
32    private static final String TAG = "BatchedScanSettings";
33
34    /** Used to indicate no preference for an int value */
35    public final static int UNSPECIFIED = Integer.MAX_VALUE;
36
37    // TODO - make MIN/mAX dynamic and gservices adjustable.
38    public final static int MIN_SCANS_PER_BATCH = 2;
39    public final static int MAX_SCANS_PER_BATCH = 255;
40    public final static int DEFAULT_SCANS_PER_BATCH = MAX_SCANS_PER_BATCH;
41
42    public final static int MIN_AP_PER_SCAN = 2;
43    public final static int MAX_AP_PER_SCAN = 255;
44    public final static int DEFAULT_AP_PER_SCAN = 16;
45
46    public final static int MIN_INTERVAL_SEC = 0;
47    public final static int MAX_INTERVAL_SEC = 3600;
48    public final static int DEFAULT_INTERVAL_SEC = 30;
49
50    public final static int MIN_AP_FOR_DISTANCE = 0;
51    public final static int MAX_AP_FOR_DISTANCE = MAX_AP_PER_SCAN;
52    public final static int DEFAULT_AP_FOR_DISTANCE = 0;
53
54    public final static int MAX_WIFI_CHANNEL = 196;
55
56    /** The expected number of scans per batch.  Note that the firmware may drop scans
57     *  leading to fewer scans during the normal batch scan duration.  This value need not
58     *  be specified (may be set to {@link UNSPECIFIED}) by the application and we will try
59     *  to scan as many times as the firmware can support.  If another app requests fewer
60     *  scans per batch we will attempt to honor that.
61     */
62    public int maxScansPerBatch;
63
64    /** The maximum desired AP listed per scan.  Fewer AP may be returned if that's all
65     *  that the driver detected.  If another application requests more AP per scan that
66     *  will take precedence.  The if more channels are detected than we request, the APs
67     *  with the lowest signal strength will be dropped.
68     */
69    public int maxApPerScan;
70
71    /** The channels used in the scan.  If all channels should be used, {@code null} may be
72     *  specified.  If another application requests more channels or all channels, that
73     *  will take precedence.
74     */
75    public Collection<String> channelSet;
76
77    /** The time between the start of two sequential scans, in seconds.  If another
78     *  application requests more frequent scans, that will take precedence.  If this
79     * value is less than the duration of a scan, the next scan should start immediately.
80     */
81    public int scanIntervalSec;
82
83    /** The number of the best (strongest signal) APs for which the firmware will
84     *  attempt to get distance information (RTT).  Not all firmware supports this
85     *  feature, so it may be ignored.  If another application requests a greater
86     *  number, that will take precedence.
87     */
88    public int maxApForDistance;
89
90    public BatchedScanSettings() {
91        clear();
92    }
93
94    public void clear() {
95        maxScansPerBatch = UNSPECIFIED;
96        maxApPerScan = UNSPECIFIED;
97        channelSet = null;
98        scanIntervalSec = UNSPECIFIED;
99        maxApForDistance = UNSPECIFIED;
100    }
101
102    public BatchedScanSettings(BatchedScanSettings source) {
103        maxScansPerBatch = source.maxScansPerBatch;
104        maxApPerScan = source.maxApPerScan;
105        if (source.channelSet != null) {
106            channelSet = new ArrayList(source.channelSet);
107        }
108        scanIntervalSec = source.scanIntervalSec;
109        maxApForDistance = source.maxApForDistance;
110    }
111
112    private boolean channelSetIsValid() {
113        if (channelSet == null || channelSet.isEmpty()) return true;
114        for (String channel : channelSet) {
115            try {
116                int i = Integer.parseInt(channel);
117                if (i > 0 && i <= MAX_WIFI_CHANNEL) continue;
118            } catch (NumberFormatException e) {}
119            if (channel.equals("A") || channel.equals("B")) continue;
120            return false;
121        }
122        return true;
123    }
124    /** @hide */
125    public boolean isInvalid() {
126        if (maxScansPerBatch != UNSPECIFIED && (maxScansPerBatch < MIN_SCANS_PER_BATCH ||
127                maxScansPerBatch > MAX_SCANS_PER_BATCH)) return true;
128        if (maxApPerScan != UNSPECIFIED && (maxApPerScan < MIN_AP_PER_SCAN ||
129                maxApPerScan > MAX_AP_PER_SCAN)) return true;
130        if (channelSetIsValid() == false) return true;
131        if (scanIntervalSec != UNSPECIFIED && (scanIntervalSec < MIN_INTERVAL_SEC ||
132                scanIntervalSec > MAX_INTERVAL_SEC)) return true;
133        if (maxApForDistance != UNSPECIFIED && (maxApForDistance < MIN_AP_FOR_DISTANCE ||
134                maxApForDistance > MAX_AP_FOR_DISTANCE)) return true;
135        return false;
136    }
137
138    /** @hide */
139    public void constrain() {
140        if (scanIntervalSec == UNSPECIFIED) {
141            scanIntervalSec = DEFAULT_INTERVAL_SEC;
142        } else if (scanIntervalSec < MIN_INTERVAL_SEC) {
143            scanIntervalSec = MIN_INTERVAL_SEC;
144        } else if (scanIntervalSec > MAX_INTERVAL_SEC) {
145            scanIntervalSec = MAX_INTERVAL_SEC;
146        }
147
148        if (maxScansPerBatch == UNSPECIFIED) {
149            maxScansPerBatch = DEFAULT_SCANS_PER_BATCH;
150        } else if (maxScansPerBatch < MIN_SCANS_PER_BATCH) {
151            maxScansPerBatch = MIN_SCANS_PER_BATCH;
152        } else if (maxScansPerBatch > MAX_SCANS_PER_BATCH) {
153            maxScansPerBatch = MAX_SCANS_PER_BATCH;
154        }
155
156        if (maxApPerScan == UNSPECIFIED) {
157            maxApPerScan = DEFAULT_AP_PER_SCAN;
158        } else if (maxApPerScan < MIN_AP_PER_SCAN) {
159            maxApPerScan = MIN_AP_PER_SCAN;
160        } else if (maxApPerScan > MAX_AP_PER_SCAN) {
161            maxApPerScan = MAX_AP_PER_SCAN;
162        }
163
164        if (maxApForDistance == UNSPECIFIED) {
165            maxApForDistance = DEFAULT_AP_FOR_DISTANCE;
166        } else if (maxApForDistance < MIN_AP_FOR_DISTANCE) {
167            maxApForDistance = MIN_AP_FOR_DISTANCE;
168        } else if (maxApForDistance > MAX_AP_FOR_DISTANCE) {
169            maxApForDistance = MAX_AP_FOR_DISTANCE;
170        }
171    }
172
173
174    @Override
175    public boolean equals(Object obj) {
176        if (obj instanceof BatchedScanSettings == false) return false;
177        BatchedScanSettings o = (BatchedScanSettings)obj;
178        if (maxScansPerBatch != o.maxScansPerBatch ||
179              maxApPerScan != o.maxApPerScan ||
180              scanIntervalSec != o.scanIntervalSec ||
181              maxApForDistance != o.maxApForDistance) return false;
182        if (channelSet == null) {
183            return (o.channelSet == null);
184        }
185        return channelSet.equals(o.channelSet);
186    }
187
188    @Override
189    public int hashCode() {
190        return maxScansPerBatch +
191                (maxApPerScan * 3) +
192                (scanIntervalSec * 5) +
193                (maxApForDistance * 7) +
194                (channelSet.hashCode() * 11);
195    }
196
197    @Override
198    public String toString() {
199        StringBuffer sb = new StringBuffer();
200        String none = "<none>";
201
202        sb.append("BatchScanSettings [maxScansPerBatch: ").
203                append(maxScansPerBatch == UNSPECIFIED ? none : maxScansPerBatch).
204                append(", maxApPerScan: ").append(maxApPerScan == UNSPECIFIED? none : maxApPerScan).
205                append(", scanIntervalSec: ").
206                append(scanIntervalSec == UNSPECIFIED ? none : scanIntervalSec).
207                append(", maxApForDistance: ").
208                append(maxApForDistance == UNSPECIFIED ? none : maxApForDistance).
209                append(", channelSet: ");
210        if (channelSet == null) {
211            sb.append("ALL");
212        } else {
213            sb.append("<");
214            for (String channel : channelSet) {
215                sb.append(" " + channel);
216            }
217            sb.append(">");
218        }
219        sb.append("]");
220        return sb.toString();
221    }
222
223    /** Implement the Parcelable interface {@hide} */
224    public int describeContents() {
225        return 0;
226    }
227
228    /** Implement the Parcelable interface {@hide} */
229    public void writeToParcel(Parcel dest, int flags) {
230        dest.writeInt(maxScansPerBatch);
231        dest.writeInt(maxApPerScan);
232        dest.writeInt(scanIntervalSec);
233        dest.writeInt(maxApForDistance);
234        dest.writeInt(channelSet == null ? 0 : channelSet.size());
235        if (channelSet != null) {
236            for (String channel : channelSet) dest.writeString(channel);
237        }
238    }
239
240    /** Implement the Parcelable interface {@hide} */
241    public static final Creator<BatchedScanSettings> CREATOR =
242        new Creator<BatchedScanSettings>() {
243            public BatchedScanSettings createFromParcel(Parcel in) {
244                BatchedScanSettings settings = new BatchedScanSettings();
245                settings.maxScansPerBatch = in.readInt();
246                settings.maxApPerScan = in.readInt();
247                settings.scanIntervalSec = in.readInt();
248                settings.maxApForDistance = in.readInt();
249                int channelCount = in.readInt();
250                if (channelCount > 0) {
251                    settings.channelSet = new ArrayList(channelCount);
252                    while (channelCount-- > 0) {
253                        settings.channelSet.add(in.readString());
254                    }
255                }
256                return settings;
257            }
258
259            public BatchedScanSettings[] newArray(int size) {
260                return new BatchedScanSettings[size];
261            }
262        };
263}
264