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