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