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