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.Parcel;
20 import android.os.Parcelable;
21 import android.util.Log;
22 
23 /**
24  * Describes information about a detected access point. In addition
25  * to the attributes described here, the supplicant keeps track of
26  * {@code quality}, {@code noise}, and {@code maxbitrate} attributes,
27  * but does not currently report them to external clients.
28  */
29 public class ScanResult implements Parcelable {
30     /**
31      * The network name.
32      */
33     public String SSID;
34 
35     /**
36      * Ascii encoded SSID. This will replace SSID when we deprecate it. @hide
37      */
38     public WifiSsid wifiSsid;
39 
40     /**
41      * The address of the access point.
42      */
43     public String BSSID;
44     /**
45      * Describes the authentication, key management, and encryption schemes
46      * supported by the access point.
47      */
48     public String capabilities;
49     /**
50      * The detected signal level in dBm, also known as the RSSI.
51      *
52      * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into
53      * an absolute signal level which can be displayed to a user.
54      */
55     public int level;
56     /**
57      * The primary 20 MHz frequency (in MHz) of the channel over which the client is communicating
58      * with the access point.
59      */
60     public int frequency;
61 
62    /**
63     * AP Channel bandwidth is 20 MHZ
64     */
65     public static final int CHANNEL_WIDTH_20MHZ = 0;
66    /**
67     * AP Channel bandwidth is 40 MHZ
68     */
69     public static final int CHANNEL_WIDTH_40MHZ = 1;
70    /**
71     * AP Channel bandwidth is 80 MHZ
72     */
73     public static final int CHANNEL_WIDTH_80MHZ = 2;
74    /**
75     * AP Channel bandwidth is 160 MHZ
76     */
77     public static final int CHANNEL_WIDTH_160MHZ = 3;
78    /**
79     * AP Channel bandwidth is 160 MHZ, but 80MHZ + 80MHZ
80     */
81     public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4;
82 
83    /**
84     * AP Channel bandwidth; one of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ},
85     * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ}
86     * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}.
87     */
88     public int channelWidth;
89 
90     /**
91      * Not used if the AP bandwidth is 20 MHz
92      * If the AP use 40, 80 or 160 MHz, this is the center frequency (in MHz)
93      * if the AP use 80 + 80 MHz, this is the center frequency of the first segment (in MHz)
94      */
95     public int centerFreq0;
96 
97     /**
98      * Only used if the AP bandwidth is 80 + 80 MHz
99      * if the AP use 80 + 80 MHz, this is the center frequency of the second segment (in MHz)
100      */
101     public int centerFreq1;
102 
103     /**
104      * @deprecated use is80211mcResponder() instead
105      * @hide
106      */
107     public boolean is80211McRTTResponder;
108 
109     /**
110      * timestamp in microseconds (since boot) when
111      * this result was last seen.
112      */
113     public long timestamp;
114 
115     /**
116      * Timestamp representing date when this result was last seen, in milliseconds from 1970
117      * {@hide}
118      */
119     public long seen;
120 
121     /**
122      * If the scan result is a valid autojoin candidate
123      * {@hide}
124      */
125     public int isAutoJoinCandidate;
126 
127     /**
128      * @hide
129      * Update RSSI of the scan result
130      * @param previousRssi
131      * @param previousSeen
132      * @param maxAge
133      */
averageRssi(int previousRssi, long previousSeen, int maxAge)134     public void averageRssi(int previousRssi, long previousSeen, int maxAge) {
135 
136         if (seen == 0) {
137             seen = System.currentTimeMillis();
138         }
139         long age = seen - previousSeen;
140 
141         if (previousSeen > 0 && age > 0 && age < maxAge/2) {
142             // Average the RSSI with previously seen instances of this scan result
143             double alpha = 0.5 - (double) age / (double) maxAge;
144             level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha);
145         }
146     }
147 
148     /** @hide */
149     public static final int ENABLED                                          = 0;
150     /** @hide */
151     public static final int AUTO_ROAM_DISABLED                               = 16;
152     /** @hide */
153     public static final int AUTO_JOIN_DISABLED                               = 32;
154     /** @hide */
155     public static final int AUTHENTICATION_ERROR                              = 128;
156 
157     /**
158      * Status: indicating join status
159      * @hide
160      */
161     public int autoJoinStatus;
162 
163     /**
164      * num IP configuration failures
165      * @hide
166      */
167     public int numIpConfigFailures;
168 
169     /**
170      * @hide
171      * Last time we blacklisted the ScanResult
172      */
173     public long blackListTimestamp;
174 
175     /** @hide **/
setAutoJoinStatus(int status)176     public void setAutoJoinStatus(int status) {
177         if (status < 0) status = 0;
178         if (status == 0) {
179             blackListTimestamp = 0;
180         }  else if (status > autoJoinStatus) {
181             blackListTimestamp = System.currentTimeMillis();
182         }
183         autoJoinStatus = status;
184     }
185 
186     /**
187      * Status: indicating the scan result is not a result
188      * that is part of user's saved configurations
189      * @hide
190      */
191     public boolean untrusted;
192 
193     /**
194      * Number of time we connected to it
195      * @hide
196      */
197     public int numConnection;
198 
199     /**
200      * Number of time autojoin used it
201      * @hide
202      */
203     public int numUsage;
204 
205     /**
206      * The approximate distance to the AP in centimeter, if available.  Else
207      * {@link UNSPECIFIED}.
208      * {@hide}
209      */
210     public int distanceCm;
211 
212     /**
213      * The standard deviation of the distance to the access point, if available.
214      * Else {@link UNSPECIFIED}.
215      * {@hide}
216      */
217     public int distanceSdCm;
218 
219     /** {@hide} */
220     public static final long FLAG_PASSPOINT_NETWORK               = 0x0000000000000001;
221 
222     /** {@hide} */
223     public static final long FLAG_80211mc_RESPONDER               = 0x0000000000000002;
224 
225     /**
226      * Defines flags; such as {@link #FLAG_PASSPOINT_NETWORK}.
227      * {@hide}
228      */
229     public long flags;
230 
231     /**
232      * sets a flag in {@link #flags} field
233      * @param flag flag to set
234      * @hide
235      */
setFlag(long flag)236     public void setFlag(long flag) {
237         flags |= flag;
238     }
239 
240     /**
241      * clears a flag in {@link #flags} field
242      * @param flag flag to set
243      * @hide
244      */
clearFlag(long flag)245     public void clearFlag(long flag) {
246         flags &= ~flag;
247     }
248 
is80211mcResponder()249     public boolean is80211mcResponder() {
250         return (flags & FLAG_80211mc_RESPONDER) != 0;
251     }
252 
isPasspointNetwork()253     public boolean isPasspointNetwork() {
254         return (flags & FLAG_PASSPOINT_NETWORK) != 0;
255     }
256 
257     /**
258      * Indicates venue name (such as 'San Francisco Airport') published by access point; only
259      * available on passpoint network and if published by access point.
260      */
261     public CharSequence venueName;
262 
263     /**
264      * Indicates passpoint operator name published by access point.
265      */
266     public CharSequence operatorFriendlyName;
267 
268     /**
269      * {@hide}
270      */
271     public final static int UNSPECIFIED = -1;
272     /**
273      * @hide
274      */
is24GHz()275     public boolean is24GHz() {
276         return ScanResult.is24GHz(frequency);
277     }
278 
279     /**
280      * @hide
281      * TODO: makes real freq boundaries
282      */
is24GHz(int freq)283     public static boolean is24GHz(int freq) {
284         return freq > 2400 && freq < 2500;
285     }
286 
287     /**
288      * @hide
289      */
is5GHz()290     public boolean is5GHz() {
291         return ScanResult.is5GHz(frequency);
292     }
293 
294     /**
295      * @hide
296      * TODO: makes real freq boundaries
297      */
is5GHz(int freq)298     public static boolean is5GHz(int freq) {
299         return freq > 4900 && freq < 5900;
300     }
301 
302     /**
303      *  @hide
304      * storing the raw bytes of full result IEs
305      **/
306     public byte[] bytes;
307 
308     /** information elements from beacon
309      * @hide
310      */
311     public static class InformationElement {
312         public int id;
313         public byte[] bytes;
314 
InformationElement()315         public InformationElement() {
316         }
317 
InformationElement(InformationElement rhs)318         public InformationElement(InformationElement rhs) {
319             this.id = rhs.id;
320             this.bytes = rhs.bytes.clone();
321         }
322     }
323 
324     /** information elements found in the beacon
325      * @hide
326      */
327     public InformationElement informationElements[];
328 
329     /** {@hide} */
ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, long tsf)330     public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
331             long tsf) {
332         this.wifiSsid = wifiSsid;
333         this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
334         this.BSSID = BSSID;
335         this.capabilities = caps;
336         this.level = level;
337         this.frequency = frequency;
338         this.timestamp = tsf;
339         this.distanceCm = UNSPECIFIED;
340         this.distanceSdCm = UNSPECIFIED;
341         this.channelWidth = UNSPECIFIED;
342         this.centerFreq0 = UNSPECIFIED;
343         this.centerFreq1 = UNSPECIFIED;
344         this.flags = 0;
345     }
346 
347     /** {@hide} */
ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, long tsf, int distCm, int distSdCm)348     public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
349             long tsf, int distCm, int distSdCm) {
350         this.wifiSsid = wifiSsid;
351         this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
352         this.BSSID = BSSID;
353         this.capabilities = caps;
354         this.level = level;
355         this.frequency = frequency;
356         this.timestamp = tsf;
357         this.distanceCm = distCm;
358         this.distanceSdCm = distSdCm;
359         this.channelWidth = UNSPECIFIED;
360         this.centerFreq0 = UNSPECIFIED;
361         this.centerFreq1 = UNSPECIFIED;
362         this.flags = 0;
363     }
364 
365     /** {@hide} */
ScanResult(String Ssid, String BSSID, String caps, int level, int frequency, long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, boolean is80211McRTTResponder)366     public ScanResult(String Ssid, String BSSID, String caps, int level, int frequency,
367             long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1,
368             boolean is80211McRTTResponder) {
369         this.SSID = Ssid;
370         this.BSSID = BSSID;
371         this.capabilities = caps;
372         this.level = level;
373         this.frequency = frequency;
374         this.timestamp = tsf;
375         this.distanceCm = distCm;
376         this.distanceSdCm = distSdCm;
377         this.channelWidth = channelWidth;
378         this.centerFreq0 = centerFreq0;
379         this.centerFreq1 = centerFreq1;
380         if (is80211McRTTResponder) {
381             this.flags = FLAG_80211mc_RESPONDER;
382         } else {
383             this.flags = 0;
384         }
385     }
386 
387     /** {@hide} */
ScanResult(WifiSsid wifiSsid, String Ssid, String BSSID, String caps, int level, int frequency, long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, boolean is80211McRTTResponder)388     public ScanResult(WifiSsid wifiSsid, String Ssid, String BSSID, String caps, int level,
389                   int frequency, long tsf, int distCm, int distSdCm, int channelWidth,
390                   int centerFreq0, int centerFreq1, boolean is80211McRTTResponder) {
391         this(Ssid, BSSID, caps,level, frequency, tsf, distCm, distSdCm, channelWidth, centerFreq0,
392                 centerFreq1, is80211McRTTResponder);
393         this.wifiSsid = wifiSsid;
394     }
395 
396     /** copy constructor {@hide} */
ScanResult(ScanResult source)397     public ScanResult(ScanResult source) {
398         if (source != null) {
399             wifiSsid = source.wifiSsid;
400             SSID = source.SSID;
401             BSSID = source.BSSID;
402             capabilities = source.capabilities;
403             level = source.level;
404             frequency = source.frequency;
405             channelWidth = source.channelWidth;
406             centerFreq0 = source.centerFreq0;
407             centerFreq1 = source.centerFreq1;
408             timestamp = source.timestamp;
409             distanceCm = source.distanceCm;
410             distanceSdCm = source.distanceSdCm;
411             seen = source.seen;
412             autoJoinStatus = source.autoJoinStatus;
413             untrusted = source.untrusted;
414             numConnection = source.numConnection;
415             numUsage = source.numUsage;
416             numIpConfigFailures = source.numIpConfigFailures;
417             isAutoJoinCandidate = source.isAutoJoinCandidate;
418             venueName = source.venueName;
419             operatorFriendlyName = source.operatorFriendlyName;
420             flags = source.flags;
421         }
422     }
423 
424     /** empty scan result
425      *
426      * {@hide}
427      * */
ScanResult()428     public ScanResult() {
429     }
430 
431     @Override
toString()432     public String toString() {
433         StringBuffer sb = new StringBuffer();
434         String none = "<none>";
435 
436         sb.append("SSID: ").
437             append(wifiSsid == null ? WifiSsid.NONE : wifiSsid).
438             append(", BSSID: ").
439             append(BSSID == null ? none : BSSID).
440             append(", capabilities: ").
441             append(capabilities == null ? none : capabilities).
442             append(", level: ").
443             append(level).
444             append(", frequency: ").
445             append(frequency).
446             append(", timestamp: ").
447             append(timestamp);
448 
449         sb.append(", distance: ").append((distanceCm != UNSPECIFIED ? distanceCm : "?")).
450                 append("(cm)");
451         sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")).
452                 append("(cm)");
453 
454         sb.append(", passpoint: ");
455         sb.append(((flags & FLAG_PASSPOINT_NETWORK) != 0) ? "yes" : "no");
456         if (autoJoinStatus != 0) {
457             sb.append(", status: ").append(autoJoinStatus);
458         }
459         sb.append(", ChannelBandwidth: ").append(channelWidth);
460         sb.append(", centerFreq0: ").append(centerFreq0);
461         sb.append(", centerFreq1: ").append(centerFreq1);
462         sb.append(", 80211mcResponder: ");
463         sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported");
464         return sb.toString();
465     }
466 
467     /** Implement the Parcelable interface {@hide} */
describeContents()468     public int describeContents() {
469         return 0;
470     }
471 
472     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)473     public void writeToParcel(Parcel dest, int flags) {
474         if (wifiSsid != null) {
475             dest.writeInt(1);
476             wifiSsid.writeToParcel(dest, flags);
477         } else {
478             dest.writeInt(0);
479         }
480         dest.writeString(SSID);
481         dest.writeString(BSSID);
482         dest.writeString(capabilities);
483         dest.writeInt(level);
484         dest.writeInt(frequency);
485         dest.writeLong(timestamp);
486         dest.writeInt(distanceCm);
487         dest.writeInt(distanceSdCm);
488         dest.writeInt(channelWidth);
489         dest.writeInt(centerFreq0);
490         dest.writeInt(centerFreq1);
491         dest.writeLong(seen);
492         dest.writeInt(autoJoinStatus);
493         dest.writeInt(untrusted ? 1 : 0);
494         dest.writeInt(numConnection);
495         dest.writeInt(numUsage);
496         dest.writeInt(numIpConfigFailures);
497         dest.writeInt(isAutoJoinCandidate);
498         dest.writeString((venueName != null) ? venueName.toString() : "");
499         dest.writeString((operatorFriendlyName != null) ? operatorFriendlyName.toString() : "");
500         dest.writeLong(this.flags);
501 
502         if (informationElements != null) {
503             dest.writeInt(informationElements.length);
504             for (int i = 0; i < informationElements.length; i++) {
505                 dest.writeInt(informationElements[i].id);
506                 dest.writeInt(informationElements[i].bytes.length);
507                 dest.writeByteArray(informationElements[i].bytes);
508             }
509         } else {
510             dest.writeInt(0);
511         }
512     }
513 
514     /** Implement the Parcelable interface {@hide} */
515     public static final Creator<ScanResult> CREATOR =
516         new Creator<ScanResult>() {
517             public ScanResult createFromParcel(Parcel in) {
518                 WifiSsid wifiSsid = null;
519                 if (in.readInt() == 1) {
520                     wifiSsid = WifiSsid.CREATOR.createFromParcel(in);
521                 }
522                 ScanResult sr = new ScanResult(
523                     wifiSsid,
524                     in.readString(),                    /* SSID  */
525                     in.readString(),                    /* BSSID */
526                     in.readString(),                    /* capabilities */
527                     in.readInt(),                       /* level */
528                     in.readInt(),                       /* frequency */
529                     in.readLong(),                      /* timestamp */
530                     in.readInt(),                       /* distanceCm */
531                     in.readInt(),                       /* distanceSdCm */
532                     in.readInt(),                       /* channelWidth */
533                     in.readInt(),                       /* centerFreq0 */
534                     in.readInt(),                       /* centerFreq1 */
535                     false                               /* rtt responder, fixed with flags below */
536                 );
537 
538                 sr.seen = in.readLong();
539                 sr.autoJoinStatus = in.readInt();
540                 sr.untrusted = in.readInt() != 0;
541                 sr.numConnection = in.readInt();
542                 sr.numUsage = in.readInt();
543                 sr.numIpConfigFailures = in.readInt();
544                 sr.isAutoJoinCandidate = in.readInt();
545                 sr.venueName = in.readString();
546                 sr.operatorFriendlyName = in.readString();
547                 sr.flags = in.readLong();
548                 int n = in.readInt();
549                 if (n != 0) {
550                     sr.informationElements = new InformationElement[n];
551                     for (int i = 0; i < n; i++) {
552                         sr.informationElements[i] = new InformationElement();
553                         sr.informationElements[i].id = in.readInt();
554                         int len = in.readInt();
555                         sr.informationElements[i].bytes = new byte[len];
556                         in.readByteArray(sr.informationElements[i].bytes);
557                     }
558                 }
559                 return sr;
560             }
561 
562             public ScanResult[] newArray(int size) {
563                 return new ScanResult[size];
564             }
565         };
566 }
567