• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.server.wifi.hotspot2;
2 
3 import static com.android.server.wifi.hotspot2.anqp.Constants.BYTES_IN_EUI48;
4 import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK;
5 
6 import android.net.wifi.ScanResult;
7 import android.util.Log;
8 
9 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
10 import com.android.server.wifi.hotspot2.anqp.Constants;
11 import com.android.server.wifi.hotspot2.anqp.RawByteElement;
12 import com.android.server.wifi.util.InformationElementUtil;
13 
14 import java.nio.BufferUnderflowException;
15 import java.nio.ByteBuffer;
16 import java.nio.CharBuffer;
17 import java.nio.charset.CharacterCodingException;
18 import java.nio.charset.CharsetDecoder;
19 import java.nio.charset.StandardCharsets;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Map;
23 
24 public class NetworkDetail {
25 
26     private static final boolean DBG = false;
27 
28     private static final String TAG = "NetworkDetail";
29 
30     public enum Ant {
31         Private,
32         PrivateWithGuest,
33         ChargeablePublic,
34         FreePublic,
35         Personal,
36         EmergencyOnly,
37         Resvd6,
38         Resvd7,
39         Resvd8,
40         Resvd9,
41         Resvd10,
42         Resvd11,
43         Resvd12,
44         Resvd13,
45         TestOrExperimental,
46         Wildcard
47     }
48 
49     public enum HSRelease {
50         R1,
51         R2,
52         R3,
53         Unknown
54     }
55 
56     // General identifiers:
57     private final String mSSID;
58     private final long mHESSID;
59     private final long mBSSID;
60     // True if the SSID is potentially from a hidden network
61     private final boolean mIsHiddenSsid;
62 
63     // BSS Load element:
64     private final int mStationCount;
65     private final int mChannelUtilization;
66     private final int mCapacity;
67 
68     //channel detailed information
69    /*
70     * 0 -- 20 MHz
71     * 1 -- 40 MHz
72     * 2 -- 80 MHz
73     * 3 -- 160 MHz
74     * 4 -- 80 + 80 MHz
75     */
76     private final int mChannelWidth;
77     private final int mPrimaryFreq;
78     private final int mCenterfreq0;
79     private final int mCenterfreq1;
80 
81     /*
82      * 802.11 Standard (calculated from Capabilities and Supported Rates)
83      * 0 -- Unknown
84      * 1 -- 802.11a
85      * 2 -- 802.11b
86      * 3 -- 802.11g
87      * 4 -- 802.11n
88      * 7 -- 802.11ac
89      */
90     private final int mWifiMode;
91     private final int mMaxRate;
92     private final int mMaxNumberSpatialStreams;
93 
94     /*
95      * From Interworking element:
96      * mAnt non null indicates the presence of Interworking, i.e. 802.11u
97      */
98     private final Ant mAnt;
99     private final boolean mInternet;
100 
101     /*
102      * From HS20 Indication element:
103      * mHSRelease is null only if the HS20 Indication element was not present.
104      * mAnqpDomainID is set to -1 if not present in the element.
105      */
106     private final HSRelease mHSRelease;
107     private final int mAnqpDomainID;
108 
109     /*
110      * From beacon:
111      * mAnqpOICount is how many additional OIs are available through ANQP.
112      * mRoamingConsortiums is either null, if the element was not present, or is an array of
113      * 1, 2 or 3 longs in which the roaming consortium values occupy the LSBs.
114      */
115     private final int mAnqpOICount;
116     private final long[] mRoamingConsortiums;
117     private int mDtimInterval = -1;
118 
119     private final InformationElementUtil.ExtendedCapabilities mExtendedCapabilities;
120 
121     private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
122 
123     /*
124      * From Wi-Fi Alliance MBO-OCE Information element.
125      * mMboAssociationDisallowedReasonCode is the reason code for AP not accepting new connections
126      * and is set to -1 if association disallowed attribute is not present in the element.
127      */
128     private final int mMboAssociationDisallowedReasonCode;
129     private final boolean mMboSupported;
130     private final boolean mMboCellularDataAware;
131     private final boolean mOceSupported;
132 
NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements, List<String> anqpLines, int freq)133     public NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements,
134             List<String> anqpLines, int freq) {
135         if (infoElements == null) {
136             throw new IllegalArgumentException("Null information elements");
137         }
138 
139         mBSSID = Utils.parseMac(bssid);
140 
141         String ssid = null;
142         boolean isHiddenSsid = false;
143         byte[] ssidOctets = null;
144 
145         InformationElementUtil.BssLoad bssLoad = new InformationElementUtil.BssLoad();
146 
147         InformationElementUtil.Interworking interworking =
148                 new InformationElementUtil.Interworking();
149 
150         InformationElementUtil.RoamingConsortium roamingConsortium =
151                 new InformationElementUtil.RoamingConsortium();
152 
153         InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa();
154 
155         InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
156         InformationElementUtil.VhtOperation vhtOperation =
157                 new InformationElementUtil.VhtOperation();
158         InformationElementUtil.HeOperation heOperation = new InformationElementUtil.HeOperation();
159 
160         InformationElementUtil.HtCapabilities htCapabilities =
161                 new InformationElementUtil.HtCapabilities();
162         InformationElementUtil.VhtCapabilities vhtCapabilities =
163                 new InformationElementUtil.VhtCapabilities();
164         InformationElementUtil.HeCapabilities heCapabilities =
165                 new InformationElementUtil.HeCapabilities();
166 
167         InformationElementUtil.ExtendedCapabilities extendedCapabilities =
168                 new InformationElementUtil.ExtendedCapabilities();
169 
170         InformationElementUtil.TrafficIndicationMap trafficIndicationMap =
171                 new InformationElementUtil.TrafficIndicationMap();
172 
173         InformationElementUtil.SupportedRates supportedRates =
174                 new InformationElementUtil.SupportedRates();
175         InformationElementUtil.SupportedRates extendedSupportedRates =
176                 new InformationElementUtil.SupportedRates();
177 
178         RuntimeException exception = null;
179 
180         ArrayList<Integer> iesFound = new ArrayList<Integer>();
181         try {
182             for (ScanResult.InformationElement ie : infoElements) {
183                 iesFound.add(ie.id);
184                 switch (ie.id) {
185                     case ScanResult.InformationElement.EID_SSID:
186                         ssidOctets = ie.bytes;
187                         break;
188                     case ScanResult.InformationElement.EID_BSS_LOAD:
189                         bssLoad.from(ie);
190                         break;
191                     case ScanResult.InformationElement.EID_HT_OPERATION:
192                         htOperation.from(ie);
193                         break;
194                     case ScanResult.InformationElement.EID_VHT_OPERATION:
195                         vhtOperation.from(ie);
196                         break;
197                     case ScanResult.InformationElement.EID_HT_CAPABILITIES:
198                         htCapabilities.from(ie);
199                         break;
200                     case ScanResult.InformationElement.EID_VHT_CAPABILITIES:
201                         vhtCapabilities.from(ie);
202                         break;
203                     case ScanResult.InformationElement.EID_INTERWORKING:
204                         interworking.from(ie);
205                         break;
206                     case ScanResult.InformationElement.EID_ROAMING_CONSORTIUM:
207                         roamingConsortium.from(ie);
208                         break;
209                     case ScanResult.InformationElement.EID_VSA:
210                         vsa.from(ie);
211                         break;
212                     case ScanResult.InformationElement.EID_EXTENDED_CAPS:
213                         extendedCapabilities.from(ie);
214                         break;
215                     case ScanResult.InformationElement.EID_TIM:
216                         trafficIndicationMap.from(ie);
217                         break;
218                     case ScanResult.InformationElement.EID_SUPPORTED_RATES:
219                         supportedRates.from(ie);
220                         break;
221                     case ScanResult.InformationElement.EID_EXTENDED_SUPPORTED_RATES:
222                         extendedSupportedRates.from(ie);
223                         break;
224                     case ScanResult.InformationElement.EID_EXTENSION_PRESENT:
225                         switch(ie.idExt) {
226                             case ScanResult.InformationElement.EID_EXT_HE_OPERATION:
227                                 heOperation.from(ie);
228                                 break;
229                             case ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES:
230                                 heCapabilities.from(ie);
231                                 break;
232                             default:
233                                 break;
234                         }
235                         break;
236                     default:
237                         break;
238                 }
239             }
240         }
241         catch (IllegalArgumentException | BufferUnderflowException | ArrayIndexOutOfBoundsException e) {
242             Log.d(Utils.hs2LogTag(getClass()), "Caught " + e);
243             if (ssidOctets == null) {
244                 throw new IllegalArgumentException("Malformed IE string (no SSID)", e);
245             }
246             exception = e;
247         }
248         if (ssidOctets != null) {
249             /*
250              * Strict use of the "UTF-8 SSID" bit by APs appears to be spotty at best even if the
251              * encoding truly is in UTF-8. An unconditional attempt to decode the SSID as UTF-8 is
252              * therefore always made with a fall back to 8859-1 under normal circumstances.
253              * If, however, a previous exception was detected and the UTF-8 bit is set, failure to
254              * decode the SSID will be used as an indication that the whole frame is malformed and
255              * an exception will be triggered.
256              */
257             CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
258             try {
259                 CharBuffer decoded = decoder.decode(ByteBuffer.wrap(ssidOctets));
260                 ssid = decoded.toString();
261             }
262             catch (CharacterCodingException cce) {
263                 ssid = null;
264             }
265 
266             if (ssid == null) {
267                 if (extendedCapabilities.isStrictUtf8() && exception != null) {
268                     throw new IllegalArgumentException("Failed to decode SSID in dubious IE string");
269                 }
270                 else {
271                     ssid = new String(ssidOctets, StandardCharsets.ISO_8859_1);
272                 }
273             }
274             isHiddenSsid = true;
275             for (byte byteVal : ssidOctets) {
276                 if (byteVal != 0) {
277                     isHiddenSsid = false;
278                     break;
279                 }
280             }
281         }
282 
283         mSSID = ssid;
284         mHESSID = interworking.hessid;
285         mIsHiddenSsid = isHiddenSsid;
286         mStationCount = bssLoad.stationCount;
287         mChannelUtilization = bssLoad.channelUtilization;
288         mCapacity = bssLoad.capacity;
289         mAnt = interworking.ant;
290         mInternet = interworking.internet;
291         mHSRelease = vsa.hsRelease;
292         mAnqpDomainID = vsa.anqpDomainID;
293         mMboSupported = vsa.IsMboCapable;
294         mMboCellularDataAware = vsa.IsMboApCellularDataAware;
295         mOceSupported = vsa.IsOceCapable;
296         mMboAssociationDisallowedReasonCode = vsa.mboAssociationDisallowedReasonCode;
297         mAnqpOICount = roamingConsortium.anqpOICount;
298         mRoamingConsortiums = roamingConsortium.getRoamingConsortiums();
299         mExtendedCapabilities = extendedCapabilities;
300         mANQPElements = null;
301         //set up channel info
302         mPrimaryFreq = freq;
303         int channelWidth = ScanResult.UNSPECIFIED;
304         int centerFreq0 = mPrimaryFreq;
305         int centerFreq1 = 0;
306 
307         // First check if HE Operation IE is present
308         if (heOperation.isPresent()) {
309             // If 6GHz info is present, then parameters should be acquired from HE Operation IE
310             if (heOperation.is6GhzInfoPresent()) {
311                 channelWidth = heOperation.getChannelWidth();
312                 centerFreq0 = heOperation.getCenterFreq0();
313                 centerFreq1 = heOperation.getCenterFreq1();
314             } else if (heOperation.isVhtInfoPresent()) {
315                 // VHT Operation Info could be included inside the HE Operation IE
316                 vhtOperation.from(heOperation.getVhtInfoElement());
317             }
318         }
319 
320         // Proceed to VHT Operation IE if parameters were not obtained from HE Operation IE
321         // Not operating in 6GHz
322         if (channelWidth == ScanResult.UNSPECIFIED) {
323             if (vhtOperation.isPresent()) {
324                 channelWidth = vhtOperation.getChannelWidth();
325                 if (channelWidth != ScanResult.UNSPECIFIED) {
326                     centerFreq0 = vhtOperation.getCenterFreq0();
327                     centerFreq1 = vhtOperation.getCenterFreq1();
328                 }
329             }
330         }
331 
332         // Proceed to HT Operation IE if parameters were not obtained from VHT/HE Operation IEs
333         // Apply to operating in 2.4/5GHz with 20/40MHz channels
334         if (channelWidth == ScanResult.UNSPECIFIED) {
335             //Either no vht, or vht shows BW is 40/20 MHz
336             if (htOperation.isPresent()) {
337                 channelWidth = htOperation.getChannelWidth();
338                 centerFreq0 = htOperation.getCenterFreq0(mPrimaryFreq);
339             }
340         }
341 
342         if (channelWidth == ScanResult.UNSPECIFIED) {
343             // Failed to obtain channel info from HE, VHT, HT IEs (possibly a 802.11a/b/g legacy AP)
344             channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ;
345         }
346 
347         mChannelWidth = channelWidth;
348         mCenterfreq0 = centerFreq0;
349         mCenterfreq1 = centerFreq1;
350 
351         // If trafficIndicationMap is not valid, mDtimPeriod will be negative
352         if (trafficIndicationMap.isValid()) {
353             mDtimInterval = trafficIndicationMap.mDtimPeriod;
354         }
355 
356         mMaxNumberSpatialStreams = Math.max(heCapabilities.getMaxNumberSpatialStreams(),
357                 Math.max(vhtCapabilities.getMaxNumberSpatialStreams(),
358                 htCapabilities.getMaxNumberSpatialStreams()));
359 
360         int maxRateA = 0;
361         int maxRateB = 0;
362         // If we got some Extended supported rates, consider them, if not default to 0
363         if (extendedSupportedRates.isValid()) {
364             // rates are sorted from smallest to largest in InformationElement
365             maxRateB = extendedSupportedRates.mRates.get(extendedSupportedRates.mRates.size() - 1);
366         }
367         // Only process the determination logic if we got a 'SupportedRates'
368         if (supportedRates.isValid()) {
369             maxRateA = supportedRates.mRates.get(supportedRates.mRates.size() - 1);
370             mMaxRate = maxRateA > maxRateB ? maxRateA : maxRateB;
371             mWifiMode = InformationElementUtil.WifiMode.determineMode(mPrimaryFreq, mMaxRate,
372                     heOperation.isPresent(), vhtOperation.isPresent(), htOperation.isPresent(),
373                     iesFound.contains(ScanResult.InformationElement.EID_ERP));
374         } else {
375             mWifiMode = 0;
376             mMaxRate = 0;
377         }
378         if (DBG) {
379             Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: "
380                     + mPrimaryFreq + " Centerfreq0: " + mCenterfreq0 + " Centerfreq1: "
381                     + mCenterfreq1 + (extendedCapabilities.is80211McRTTResponder()
382                     ? " Support RTT responder" : " Do not support RTT responder")
383                     + " MaxNumberSpatialStreams: " + mMaxNumberSpatialStreams
384                     + " MboAssociationDisallowedReasonCode: "
385                     + mMboAssociationDisallowedReasonCode);
386             Log.v("WifiMode", mSSID
387                     + ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode)
388                     + ", Freq: " + mPrimaryFreq
389                     + ", MaxRate: " + mMaxRate
390                     + ", HE: " + String.valueOf(heOperation.isPresent())
391                     + ", VHT: " + String.valueOf(vhtOperation.isPresent())
392                     + ", HT: " + String.valueOf(htOperation.isPresent())
393                     + ", ERP: " + String.valueOf(
394                     iesFound.contains(ScanResult.InformationElement.EID_ERP))
395                     + ", SupportedRates: " + supportedRates.toString()
396                     + " ExtendedSupportedRates: " + extendedSupportedRates.toString());
397         }
398     }
399 
getAndAdvancePayload(ByteBuffer data, int plLength)400     private static ByteBuffer getAndAdvancePayload(ByteBuffer data, int plLength) {
401         ByteBuffer payload = data.duplicate().order(data.order());
402         payload.limit(payload.position() + plLength);
403         data.position(data.position() + plLength);
404         return payload;
405     }
406 
NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements)407     private NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
408         mSSID = base.mSSID;
409         mIsHiddenSsid = base.mIsHiddenSsid;
410         mBSSID = base.mBSSID;
411         mHESSID = base.mHESSID;
412         mStationCount = base.mStationCount;
413         mChannelUtilization = base.mChannelUtilization;
414         mCapacity = base.mCapacity;
415         mAnt = base.mAnt;
416         mInternet = base.mInternet;
417         mHSRelease = base.mHSRelease;
418         mAnqpDomainID = base.mAnqpDomainID;
419         mAnqpOICount = base.mAnqpOICount;
420         mRoamingConsortiums = base.mRoamingConsortiums;
421         mExtendedCapabilities =
422                 new InformationElementUtil.ExtendedCapabilities(base.mExtendedCapabilities);
423         mANQPElements = anqpElements;
424         mChannelWidth = base.mChannelWidth;
425         mPrimaryFreq = base.mPrimaryFreq;
426         mCenterfreq0 = base.mCenterfreq0;
427         mCenterfreq1 = base.mCenterfreq1;
428         mDtimInterval = base.mDtimInterval;
429         mWifiMode = base.mWifiMode;
430         mMaxRate = base.mMaxRate;
431         mMaxNumberSpatialStreams = base.mMaxNumberSpatialStreams;
432         mMboSupported = base.mMboSupported;
433         mMboCellularDataAware = base.mMboCellularDataAware;
434         mOceSupported = base.mOceSupported;
435         mMboAssociationDisallowedReasonCode = base.mMboAssociationDisallowedReasonCode;
436     }
437 
complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements)438     public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
439         return new NetworkDetail(this, anqpElements);
440     }
441 
queriable(List<Constants.ANQPElementType> queryElements)442     public boolean queriable(List<Constants.ANQPElementType> queryElements) {
443         return mAnt != null &&
444                 (Constants.hasBaseANQPElements(queryElements) ||
445                  Constants.hasR2Elements(queryElements) && mHSRelease == HSRelease.R2);
446     }
447 
has80211uInfo()448     public boolean has80211uInfo() {
449         return mAnt != null || mRoamingConsortiums != null || mHSRelease != null;
450     }
451 
hasInterworking()452     public boolean hasInterworking() {
453         return mAnt != null;
454     }
455 
getSSID()456     public String getSSID() {
457         return mSSID;
458     }
459 
getTrimmedSSID()460     public String getTrimmedSSID() {
461         if (mSSID != null) {
462             for (int n = 0; n < mSSID.length(); n++) {
463                 if (mSSID.charAt(n) != 0) {
464                     return mSSID;
465                 }
466             }
467         }
468         return "";
469     }
470 
getHESSID()471     public long getHESSID() {
472         return mHESSID;
473     }
474 
getBSSID()475     public long getBSSID() {
476         return mBSSID;
477     }
478 
getStationCount()479     public int getStationCount() {
480         return mStationCount;
481     }
482 
getChannelUtilization()483     public int getChannelUtilization() {
484         return mChannelUtilization;
485     }
486 
getCapacity()487     public int getCapacity() {
488         return mCapacity;
489     }
490 
isInterworking()491     public boolean isInterworking() {
492         return mAnt != null;
493     }
494 
getAnt()495     public Ant getAnt() {
496         return mAnt;
497     }
498 
isInternet()499     public boolean isInternet() {
500         return mInternet;
501     }
502 
getHSRelease()503     public HSRelease getHSRelease() {
504         return mHSRelease;
505     }
506 
getAnqpDomainID()507     public int getAnqpDomainID() {
508         return mAnqpDomainID;
509     }
510 
getOsuProviders()511     public byte[] getOsuProviders() {
512         if (mANQPElements == null) {
513             return null;
514         }
515         ANQPElement osuProviders = mANQPElements.get(Constants.ANQPElementType.HSOSUProviders);
516         return osuProviders != null ? ((RawByteElement) osuProviders).getPayload() : null;
517     }
518 
getAnqpOICount()519     public int getAnqpOICount() {
520         return mAnqpOICount;
521     }
522 
getRoamingConsortiums()523     public long[] getRoamingConsortiums() {
524         return mRoamingConsortiums;
525     }
526 
getANQPElements()527     public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
528         return mANQPElements;
529     }
530 
getChannelWidth()531     public int getChannelWidth() {
532         return mChannelWidth;
533     }
534 
getCenterfreq0()535     public int getCenterfreq0() {
536         return mCenterfreq0;
537     }
538 
getCenterfreq1()539     public int getCenterfreq1() {
540         return mCenterfreq1;
541     }
542 
getWifiMode()543     public int getWifiMode() {
544         return mWifiMode;
545     }
546 
getMaxNumberSpatialStreams()547     public int getMaxNumberSpatialStreams() {
548         return mMaxNumberSpatialStreams;
549     }
550 
getDtimInterval()551     public int getDtimInterval() {
552         return mDtimInterval;
553     }
554 
is80211McResponderSupport()555     public boolean is80211McResponderSupport() {
556         return mExtendedCapabilities.is80211McRTTResponder();
557     }
558 
isSSID_UTF8()559     public boolean isSSID_UTF8() {
560         return mExtendedCapabilities.isStrictUtf8();
561     }
562 
563     @Override
equals(Object thatObject)564     public boolean equals(Object thatObject) {
565         if (this == thatObject) {
566             return true;
567         }
568         if (thatObject == null || getClass() != thatObject.getClass()) {
569             return false;
570         }
571 
572         NetworkDetail that = (NetworkDetail)thatObject;
573 
574         return getSSID().equals(that.getSSID()) && getBSSID() == that.getBSSID();
575     }
576 
577     @Override
hashCode()578     public int hashCode() {
579         return ((mSSID.hashCode() * 31) + (int)(mBSSID >>> 32)) * 31 + (int)mBSSID;
580     }
581 
582     @Override
toString()583     public String toString() {
584         return String.format("NetworkInfo{SSID='%s', HESSID=%x, BSSID=%x, StationCount=%d, " +
585                 "ChannelUtilization=%d, Capacity=%d, Ant=%s, Internet=%s, " +
586                 "HSRelease=%s, AnqpDomainID=%d, " +
587                 "AnqpOICount=%d, RoamingConsortiums=%s}",
588                 mSSID, mHESSID, mBSSID, mStationCount,
589                 mChannelUtilization, mCapacity, mAnt, mInternet,
590                 mHSRelease, mAnqpDomainID,
591                 mAnqpOICount, Utils.roamingConsortiumsToString(mRoamingConsortiums));
592     }
593 
toKeyString()594     public String toKeyString() {
595         return mHESSID != 0 ?
596             String.format("'%s':%012x (%012x)", mSSID, mBSSID, mHESSID) :
597             String.format("'%s':%012x", mSSID, mBSSID);
598     }
599 
getBSSIDString()600     public String getBSSIDString() {
601         return toMACString(mBSSID);
602     }
603 
604     /**
605      * Evaluates the ScanResult this NetworkDetail is built from
606      * returns true if built from a Beacon Frame
607      * returns false if built from a Probe Response
608      */
isBeaconFrame()609     public boolean isBeaconFrame() {
610         // Beacon frames have a 'Traffic Indication Map' Information element
611         // Probe Responses do not. This is indicated by a DTIM period > 0
612         return mDtimInterval > 0;
613     }
614 
615     /**
616      * Evaluates the ScanResult this NetworkDetail is built from
617      * returns true if built from a hidden Beacon Frame
618      * returns false if not hidden or not a Beacon
619      */
isHiddenBeaconFrame()620     public boolean isHiddenBeaconFrame() {
621         // Hidden networks are not 80211 standard, but it is common for a hidden network beacon
622         // frame to either send zero-value bytes as the SSID, or to send no bytes at all.
623         return isBeaconFrame() && mIsHiddenSsid;
624     }
625 
toMACString(long mac)626     public static String toMACString(long mac) {
627         StringBuilder sb = new StringBuilder();
628         boolean first = true;
629         for (int n = BYTES_IN_EUI48 - 1; n >= 0; n--) {
630             if (first) {
631                 first = false;
632             } else {
633                 sb.append(':');
634             }
635             sb.append(String.format("%02x", (mac >>> (n * Byte.SIZE)) & BYTE_MASK));
636         }
637         return sb.toString();
638     }
639 
getMboAssociationDisallowedReasonCode()640     public int getMboAssociationDisallowedReasonCode() {
641         return mMboAssociationDisallowedReasonCode;
642     }
643 
isMboSupported()644     public boolean isMboSupported() {
645         return mMboSupported;
646     }
647 
isMboCellularDataAware()648     public boolean isMboCellularDataAware() {
649         return mMboCellularDataAware;
650     }
651 
isOceSupported()652     public boolean isOceSupported() {
653         return mOceSupported;
654     }
655 }
656