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 
17 package android.net.wifi;
18 
19 import android.os.Parcelable;
20 import android.os.Parcel;
21 
22 import java.util.ArrayList;
23 import 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  */
31 public 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 as standard for wifi batch capability requirement.
38     public final static int MIN_SCANS_PER_BATCH = 2;
39     public final static int MAX_SCANS_PER_BATCH = 20;
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 = 16;
44     public final static int DEFAULT_AP_PER_SCAN = 16;
45 
46     public final static int MIN_INTERVAL_SEC = 10;
47     public final static int MAX_INTERVAL_SEC = 500;
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 
BatchedScanSettings()90     public BatchedScanSettings() {
91         clear();
92     }
93 
clear()94     public void clear() {
95         maxScansPerBatch = UNSPECIFIED;
96         maxApPerScan = UNSPECIFIED;
97         channelSet = null;
98         scanIntervalSec = UNSPECIFIED;
99         maxApForDistance = UNSPECIFIED;
100     }
101 
BatchedScanSettings(BatchedScanSettings source)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 
channelSetIsValid()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 */
isInvalid()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 */
constrain()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
equals(Object obj)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
hashCode()189     public int hashCode() {
190         return maxScansPerBatch +
191                 (maxApPerScan * 3) +
192                 (scanIntervalSec * 5) +
193                 (maxApForDistance * 7) +
194                 (channelSet.hashCode() * 11);
195     }
196 
197     @Override
toString()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} */
describeContents()224     public int describeContents() {
225         return 0;
226     }
227 
228     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)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