1 package com.android.server.wifi.hotspot2;
2 
3 import android.net.MacAddress;
4 import android.net.wifi.MloLink;
5 import android.net.wifi.ScanResult;
6 import android.util.Log;
7 
8 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
9 import com.android.server.wifi.hotspot2.anqp.Constants;
10 import com.android.server.wifi.hotspot2.anqp.RawByteElement;
11 import com.android.server.wifi.util.InformationElementUtil;
12 
13 import java.nio.BufferUnderflowException;
14 import java.nio.ByteBuffer;
15 import java.nio.CharBuffer;
16 import java.nio.charset.CharacterCodingException;
17 import java.nio.charset.CharsetDecoder;
18 import java.nio.charset.StandardCharsets;
19 import java.util.ArrayList;
20 import java.util.Collections;
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     private String mCountryCode;
119 
120     private final InformationElementUtil.ExtendedCapabilities mExtendedCapabilities;
121 
122     private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
123 
124     /*
125      * From Wi-Fi Alliance MBO-OCE Information element.
126      * mMboAssociationDisallowedReasonCode is the reason code for AP not accepting new connections
127      * and is set to -1 if association disallowed attribute is not present in the element.
128      */
129     private final int mMboAssociationDisallowedReasonCode;
130     private final boolean mMboSupported;
131     private final boolean mMboCellularDataAware;
132     private final boolean mOceSupported;
133 
134     // Target wake time (TWT) allows an AP to manage activity in the BSS in order to minimize
135     // contention between STAs and to reduce the required amount of time that a STA utilizing a
136     // power management mode needs to be awake.
137 
138     // The HE AP requests that STAs participate in TWT by setting the TWT Required subfield to 1
139     // in HE Operation elements. STAs that support TWT and receive an HE Operation element with
140     // the TWT Required subfield set to 1 must either negotiate individual TWT agreements or
141     // participate in broadcast TWT operation.
142     private final boolean mTwtRequired;
143     // With Individual TWT operation, a STA negotiate a wake schedule with an access point, allowing
144     // it to wake up only when required.
145     private final boolean mIndividualTwtSupported;
146     // In Broadcast TWT operation, an AP can set up a shared TWT session for a group of stations
147     // and specify the TWT parameters periodically in Beacon frames.
148     private final boolean mBroadcastTwtSupported;
149     // Restricted Target Wake Time (TWT) is a feature that allows an access point to allocate
150     // exclusive access to a medium at specified times.
151     private final boolean mRestrictedTwtSupported;
152 
153     // EPCS priority access is a mechanism that provides prioritized access to the wireless
154     // medium for authorized users to increase their probability of successful communication
155     // during periods of network congestion.
156     private final boolean mEpcsPriorityAccessSupported;
157 
158     // Fast Initial Link Setup (FILS)
159     private final boolean mFilsCapable;
160 
161     // 6 GHz Access Point Type
162     private final InformationElementUtil.ApType6GHz mApType6GHz;
163 
164     // IEEE 802.11az non-trigger based & trigger based
165     private final boolean mIs11azNtbResponder;
166     private final boolean mIs11azTbResponder;
167 
168     // MLO Attributes
169     private MacAddress mMldMacAddress = null;
170     private int mMloLinkId = MloLink.INVALID_MLO_LINK_ID;
171     private List<MloLink> mAffiliatedMloLinks = Collections.emptyList();
172     private byte[] mDisabledSubchannelBitmap;
173 
NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements, List<String> anqpLines, int freq)174     public NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements,
175             List<String> anqpLines, int freq) {
176         if (infoElements == null) {
177             infoElements = new ScanResult.InformationElement[0];
178         }
179 
180         mBSSID = Utils.parseMac(bssid);
181 
182         String ssid = null;
183         boolean isHiddenSsid = false;
184         byte[] ssidOctets = null;
185 
186         InformationElementUtil.BssLoad bssLoad = new InformationElementUtil.BssLoad();
187 
188         InformationElementUtil.Interworking interworking =
189                 new InformationElementUtil.Interworking();
190 
191         InformationElementUtil.RoamingConsortium roamingConsortium =
192                 new InformationElementUtil.RoamingConsortium();
193 
194         InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa();
195 
196         InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
197         InformationElementUtil.VhtOperation vhtOperation =
198                 new InformationElementUtil.VhtOperation();
199         InformationElementUtil.HeOperation heOperation = new InformationElementUtil.HeOperation();
200         InformationElementUtil.EhtOperation ehtOperation =
201                 new InformationElementUtil.EhtOperation();
202 
203         InformationElementUtil.HtCapabilities htCapabilities =
204                 new InformationElementUtil.HtCapabilities();
205         InformationElementUtil.VhtCapabilities vhtCapabilities =
206                 new InformationElementUtil.VhtCapabilities();
207         InformationElementUtil.HeCapabilities heCapabilities =
208                 new InformationElementUtil.HeCapabilities();
209         InformationElementUtil.EhtCapabilities ehtCapabilities =
210                 new InformationElementUtil.EhtCapabilities();
211         InformationElementUtil.Rnr rnr =
212                 new InformationElementUtil.Rnr();
213         InformationElementUtil.MultiLink multiLink =
214                 new InformationElementUtil.MultiLink();
215         InformationElementUtil.ExtendedCapabilities extendedCapabilities =
216                 new InformationElementUtil.ExtendedCapabilities();
217 
218         InformationElementUtil.Country country =
219                 new InformationElementUtil.Country();
220 
221         InformationElementUtil.TrafficIndicationMap trafficIndicationMap =
222                 new InformationElementUtil.TrafficIndicationMap();
223 
224         InformationElementUtil.SupportedRates supportedRates =
225                 new InformationElementUtil.SupportedRates();
226         InformationElementUtil.SupportedRates extendedSupportedRates =
227                 new InformationElementUtil.SupportedRates();
228 
229         RuntimeException exception = null;
230 
231         ArrayList<Integer> iesFound = new ArrayList<Integer>();
232         try {
233             for (ScanResult.InformationElement ie : infoElements) {
234                 iesFound.add(ie.id);
235                 switch (ie.id) {
236                     case ScanResult.InformationElement.EID_SSID:
237                         ssidOctets = ie.bytes;
238                         break;
239                     case ScanResult.InformationElement.EID_BSS_LOAD:
240                         bssLoad.from(ie);
241                         break;
242                     case ScanResult.InformationElement.EID_HT_OPERATION:
243                         htOperation.from(ie);
244                         break;
245                     case ScanResult.InformationElement.EID_VHT_OPERATION:
246                         vhtOperation.from(ie);
247                         break;
248                     case ScanResult.InformationElement.EID_HT_CAPABILITIES:
249                         htCapabilities.from(ie);
250                         break;
251                     case ScanResult.InformationElement.EID_VHT_CAPABILITIES:
252                         vhtCapabilities.from(ie);
253                         break;
254                     case ScanResult.InformationElement.EID_INTERWORKING:
255                         interworking.from(ie);
256                         break;
257                     case ScanResult.InformationElement.EID_ROAMING_CONSORTIUM:
258                         roamingConsortium.from(ie);
259                         break;
260                     case ScanResult.InformationElement.EID_VSA:
261                         vsa.from(ie);
262                         break;
263                     case ScanResult.InformationElement.EID_EXTENDED_CAPS:
264                         extendedCapabilities.from(ie);
265                         break;
266                     case ScanResult.InformationElement.EID_COUNTRY:
267                         country.from(ie);
268                         break;
269                     case ScanResult.InformationElement.EID_TIM:
270                         trafficIndicationMap.from(ie);
271                         break;
272                     case ScanResult.InformationElement.EID_SUPPORTED_RATES:
273                         supportedRates.from(ie);
274                         break;
275                     case ScanResult.InformationElement.EID_EXTENDED_SUPPORTED_RATES:
276                         extendedSupportedRates.from(ie);
277                         break;
278                     case ScanResult.InformationElement.EID_RNR:
279                         rnr.from(ie);
280                         break;
281                     case ScanResult.InformationElement.EID_EXTENSION_PRESENT:
282                         switch(ie.idExt) {
283                             case ScanResult.InformationElement.EID_EXT_HE_OPERATION:
284                                 heOperation.from(ie);
285                                 break;
286                             case ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES:
287                                 heCapabilities.from(ie);
288                                 break;
289                             case ScanResult.InformationElement.EID_EXT_EHT_OPERATION:
290                                 ehtOperation.from(ie);
291                                 break;
292                             case ScanResult.InformationElement.EID_EXT_EHT_CAPABILITIES:
293                                 ehtCapabilities.from(ie);
294                                 break;
295                             case ScanResult.InformationElement.EID_EXT_MULTI_LINK:
296                                 multiLink.from(ie);
297                                 break;
298                             default:
299                                 break;
300                         }
301                         break;
302                     default:
303                         break;
304                 }
305             }
306         }
307         catch (IllegalArgumentException | BufferUnderflowException | ArrayIndexOutOfBoundsException e) {
308             Log.d(TAG, "Caught " + e);
309             if (ssidOctets == null) {
310                 throw new IllegalArgumentException("Malformed IE string (no SSID)", e);
311             }
312             exception = e;
313         }
314         if (ssidOctets != null) {
315             /*
316              * Strict use of the "UTF-8 SSID" bit by APs appears to be spotty at best even if the
317              * encoding truly is in UTF-8. An unconditional attempt to decode the SSID as UTF-8 is
318              * therefore always made with a fall back to 8859-1 under normal circumstances.
319              * If, however, a previous exception was detected and the UTF-8 bit is set, failure to
320              * decode the SSID will be used as an indication that the whole frame is malformed and
321              * an exception will be triggered.
322              */
323             CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
324             try {
325                 CharBuffer decoded = decoder.decode(ByteBuffer.wrap(ssidOctets));
326                 ssid = decoded.toString();
327             }
328             catch (CharacterCodingException cce) {
329                 ssid = null;
330             }
331 
332             if (ssid == null) {
333                 if (extendedCapabilities.isStrictUtf8() && exception != null) {
334                     throw new IllegalArgumentException("Failed to decode SSID in dubious IE string");
335                 }
336                 else {
337                     ssid = new String(ssidOctets, StandardCharsets.ISO_8859_1);
338                 }
339             }
340             isHiddenSsid = true;
341             for (byte byteVal : ssidOctets) {
342                 if (byteVal != 0) {
343                     isHiddenSsid = false;
344                     break;
345                 }
346             }
347         }
348 
349         mSSID = ssid;
350         mHESSID = interworking.hessid;
351         mIsHiddenSsid = isHiddenSsid;
352         mStationCount = bssLoad.stationCount;
353         mChannelUtilization = bssLoad.channelUtilization;
354         mCapacity = bssLoad.capacity;
355         mAnt = interworking.ant;
356         mInternet = interworking.internet;
357         mHSRelease = vsa.hsRelease;
358         mAnqpDomainID = vsa.anqpDomainID;
359         mMboSupported = vsa.IsMboCapable;
360         mMboCellularDataAware = vsa.IsMboApCellularDataAware;
361         mOceSupported = vsa.IsOceCapable;
362         mMboAssociationDisallowedReasonCode = vsa.mboAssociationDisallowedReasonCode;
363         mAnqpOICount = roamingConsortium.anqpOICount;
364         mRoamingConsortiums = roamingConsortium.getRoamingConsortiums();
365         mExtendedCapabilities = extendedCapabilities;
366         mANQPElements = null;
367         //set up channel info
368         mPrimaryFreq = freq;
369         mTwtRequired = heOperation.isTwtRequired();
370         mIndividualTwtSupported = heCapabilities.isTwtResponderSupported();
371         mBroadcastTwtSupported = heCapabilities.isBroadcastTwtSupported();
372         mRestrictedTwtSupported = ehtCapabilities.isRestrictedTwtSupported();
373         mEpcsPriorityAccessSupported = ehtCapabilities.isEpcsPriorityAccessSupported();
374         mFilsCapable = extendedCapabilities.isFilsCapable();
375         mApType6GHz = heOperation.getApType6GHz();
376         mIs11azNtbResponder =  extendedCapabilities.is80211azNtbResponder();
377         mIs11azTbResponder = extendedCapabilities.is80211azTbResponder();
378         int channelWidth = ScanResult.UNSPECIFIED;
379         int centerFreq0 = mPrimaryFreq;
380         int centerFreq1 = 0;
381 
382         // Check if EHT Operation Info is present in EHT operation IE.
383         if (ehtOperation.isEhtOperationInfoPresent()) {
384             int operatingBand = ScanResult.toBand(mPrimaryFreq);
385             channelWidth = ehtOperation.getChannelWidth();
386             centerFreq0 = ehtOperation.getCenterFreq0(operatingBand);
387             centerFreq1 = ehtOperation.getCenterFreq1(operatingBand);
388             mDisabledSubchannelBitmap = ehtOperation.getDisabledSubchannelBitmap();
389         }
390 
391         // Proceed to HE Operation IE if channel width and center frequencies were not obtained
392         // from EHT Operation IE
393         if (channelWidth == ScanResult.UNSPECIFIED) {
394             // Check if HE Operation IE is present
395             if (heOperation.isPresent()) {
396                 // If 6GHz info is present, then parameters should be acquired from HE Operation IE
397                 if (heOperation.is6GhzInfoPresent()) {
398                     channelWidth = heOperation.getChannelWidth();
399                     centerFreq0 = heOperation.getCenterFreq0();
400                     centerFreq1 = heOperation.getCenterFreq1();
401                 } else if (heOperation.isVhtInfoPresent()) {
402                     // VHT Operation Info could be included inside the HE Operation IE
403                     vhtOperation.from(heOperation.getVhtInfoElement());
404                 }
405             }
406         }
407 
408         // Proceed to VHT Operation IE if parameters were not obtained from HE Operation IE
409         // Not operating in 6GHz
410         if (channelWidth == ScanResult.UNSPECIFIED) {
411             if (vhtOperation.isPresent()) {
412                 channelWidth = vhtOperation.getChannelWidth();
413                 if (channelWidth != ScanResult.UNSPECIFIED) {
414                     centerFreq0 = vhtOperation.getCenterFreq0();
415                     centerFreq1 = vhtOperation.getCenterFreq1();
416                 }
417             }
418         }
419 
420         // Proceed to HT Operation IE if parameters were not obtained from VHT/HE Operation IEs
421         // Apply to operating in 2.4/5GHz with 20/40MHz channels
422         if (channelWidth == ScanResult.UNSPECIFIED) {
423             //Either no vht, or vht shows BW is 40/20 MHz
424             if (htOperation.isPresent()) {
425                 channelWidth = htOperation.getChannelWidth();
426                 centerFreq0 = htOperation.getCenterFreq0(mPrimaryFreq);
427             }
428         }
429 
430         if (channelWidth == ScanResult.UNSPECIFIED) {
431             // Failed to obtain channel info from HE, VHT, HT IEs (possibly a 802.11a/b/g legacy AP)
432             channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ;
433         }
434 
435         mChannelWidth = channelWidth;
436         mCenterfreq0 = centerFreq0;
437         mCenterfreq1 = centerFreq1;
438 
439         if (country.isValid()) {
440             mCountryCode = country.getCountryCode();
441         }
442 
443         // If trafficIndicationMap is not valid, mDtimPeriod will be negative
444         if (trafficIndicationMap.isValid()) {
445             mDtimInterval = trafficIndicationMap.mDtimPeriod;
446         }
447 
448         mMaxNumberSpatialStreams = Math.max(heCapabilities.getMaxNumberSpatialStreams(),
449                 Math.max(vhtCapabilities.getMaxNumberSpatialStreams(),
450                 htCapabilities.getMaxNumberSpatialStreams()));
451 
452         int maxRateA = 0;
453         int maxRateB = 0;
454         // If we got some Extended supported rates, consider them, if not default to 0
455         if (extendedSupportedRates.isValid()) {
456             // rates are sorted from smallest to largest in InformationElement
457             maxRateB = extendedSupportedRates.mRates.get(extendedSupportedRates.mRates.size() - 1);
458         }
459         // Only process the determination logic if we got a 'SupportedRates'
460         if (supportedRates.isValid()) {
461             maxRateA = supportedRates.mRates.get(supportedRates.mRates.size() - 1);
462             mMaxRate = maxRateA > maxRateB ? maxRateA : maxRateB;
463             mWifiMode = InformationElementUtil.WifiMode.determineMode(mPrimaryFreq, mMaxRate,
464                     ehtOperation.isPresent(), heOperation.isPresent(), vhtOperation.isPresent(),
465                     htOperation.isPresent(),
466                     iesFound.contains(ScanResult.InformationElement.EID_ERP));
467         } else {
468             mWifiMode = 0;
469             mMaxRate = 0;
470         }
471 
472         if (multiLink.isPresent()) {
473             mMldMacAddress = multiLink.getMldMacAddress();
474             mMloLinkId = multiLink.getLinkId();
475             if (rnr.isPresent()) {
476                 if (!rnr.getAffiliatedMloLinks().isEmpty()) {
477                     mAffiliatedMloLinks = new ArrayList<>(rnr.getAffiliatedMloLinks());
478                 } else if (!multiLink.getAffiliatedLinks().isEmpty()) {
479                     mAffiliatedMloLinks = new ArrayList<>(multiLink.getAffiliatedLinks());
480                 }
481             }
482 
483             // Add the current link to the list of links if not empty
484             if (!mAffiliatedMloLinks.isEmpty()) {
485                 MloLink link = new MloLink();
486                 link.setApMacAddress(MacAddress.fromString(bssid));
487                 link.setChannel(ScanResult.convertFrequencyMhzToChannelIfSupported(mPrimaryFreq));
488                 link.setBand(ScanResult.toBand(mPrimaryFreq));
489                 link.setLinkId(mMloLinkId);
490                 mAffiliatedMloLinks.add(link);
491             }
492         }
493 
494         if (DBG) {
495             Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: "
496                     + mPrimaryFreq + " Centerfreq0: " + mCenterfreq0 + " Centerfreq1: "
497                     + mCenterfreq1 + (extendedCapabilities.is80211McRTTResponder()
498                     ? " Support RTT responder" : " Do not support RTT responder")
499                     + " MaxNumberSpatialStreams: " + mMaxNumberSpatialStreams
500                     + " MboAssociationDisallowedReasonCode: "
501                     + mMboAssociationDisallowedReasonCode);
502             Log.v("WifiMode", mSSID
503                     + ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode)
504                     + ", Freq: " + mPrimaryFreq
505                     + ", MaxRate: " + mMaxRate
506                     + ", EHT: " + String.valueOf(ehtOperation.isPresent())
507                     + ", HE: " + String.valueOf(heOperation.isPresent())
508                     + ", VHT: " + String.valueOf(vhtOperation.isPresent())
509                     + ", HT: " + String.valueOf(htOperation.isPresent())
510                     + ", ERP: " + String.valueOf(
511                     iesFound.contains(ScanResult.InformationElement.EID_ERP))
512                     + ", SupportedRates: " + supportedRates.toString()
513                     + " ExtendedSupportedRates: " + extendedSupportedRates.toString());
514         }
515     }
516 
517     /**
518      * Copy constructor
519      */
NetworkDetail(NetworkDetail networkDetail)520     public NetworkDetail(NetworkDetail networkDetail) {
521         this(networkDetail, networkDetail.mANQPElements);
522     }
523 
getAndAdvancePayload(ByteBuffer data, int plLength)524     private static ByteBuffer getAndAdvancePayload(ByteBuffer data, int plLength) {
525         ByteBuffer payload = data.duplicate().order(data.order());
526         payload.limit(payload.position() + plLength);
527         data.position(data.position() + plLength);
528         return payload;
529     }
530 
NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements)531     private NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
532         mSSID = base.mSSID;
533         mIsHiddenSsid = base.mIsHiddenSsid;
534         mBSSID = base.mBSSID;
535         mHESSID = base.mHESSID;
536         mStationCount = base.mStationCount;
537         mChannelUtilization = base.mChannelUtilization;
538         mCapacity = base.mCapacity;
539         mAnt = base.mAnt;
540         mInternet = base.mInternet;
541         mHSRelease = base.mHSRelease;
542         mAnqpDomainID = base.mAnqpDomainID;
543         mAnqpOICount = base.mAnqpOICount;
544         mRoamingConsortiums = base.mRoamingConsortiums;
545         mExtendedCapabilities =
546                 new InformationElementUtil.ExtendedCapabilities(base.mExtendedCapabilities);
547         mANQPElements = anqpElements;
548         mChannelWidth = base.mChannelWidth;
549         mPrimaryFreq = base.mPrimaryFreq;
550         mCenterfreq0 = base.mCenterfreq0;
551         mCenterfreq1 = base.mCenterfreq1;
552         mDtimInterval = base.mDtimInterval;
553         mCountryCode = base.mCountryCode;
554         mWifiMode = base.mWifiMode;
555         mMaxRate = base.mMaxRate;
556         mMaxNumberSpatialStreams = base.mMaxNumberSpatialStreams;
557         mMboSupported = base.mMboSupported;
558         mMboCellularDataAware = base.mMboCellularDataAware;
559         mOceSupported = base.mOceSupported;
560         mMboAssociationDisallowedReasonCode = base.mMboAssociationDisallowedReasonCode;
561         mTwtRequired = base.mTwtRequired;
562         mIndividualTwtSupported = base.mIndividualTwtSupported;
563         mBroadcastTwtSupported = base.mBroadcastTwtSupported;
564         mRestrictedTwtSupported = base.mRestrictedTwtSupported;
565         mEpcsPriorityAccessSupported = base.mEpcsPriorityAccessSupported;
566         mFilsCapable = base.mFilsCapable;
567         mApType6GHz = base.mApType6GHz;
568         mIs11azNtbResponder = base.mIs11azNtbResponder;
569         mIs11azTbResponder = base.mIs11azTbResponder;
570     }
571 
complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements)572     public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
573         return new NetworkDetail(this, anqpElements);
574     }
575 
queriable(List<Constants.ANQPElementType> queryElements)576     public boolean queriable(List<Constants.ANQPElementType> queryElements) {
577         return mAnt != null &&
578                 (Constants.hasBaseANQPElements(queryElements) ||
579                  Constants.hasR2Elements(queryElements) && mHSRelease == HSRelease.R2);
580     }
581 
has80211uInfo()582     public boolean has80211uInfo() {
583         return mAnt != null || mRoamingConsortiums != null || mHSRelease != null;
584     }
585 
hasInterworking()586     public boolean hasInterworking() {
587         return mAnt != null;
588     }
589 
getSSID()590     public String getSSID() {
591         return mSSID;
592     }
593 
getTrimmedSSID()594     public String getTrimmedSSID() {
595         if (mSSID != null) {
596             for (int n = 0; n < mSSID.length(); n++) {
597                 if (mSSID.charAt(n) != 0) {
598                     return mSSID;
599                 }
600             }
601         }
602         return "";
603     }
604 
getHESSID()605     public long getHESSID() {
606         return mHESSID;
607     }
608 
getBSSID()609     public long getBSSID() {
610         return mBSSID;
611     }
612 
getStationCount()613     public int getStationCount() {
614         return mStationCount;
615     }
616 
getChannelUtilization()617     public int getChannelUtilization() {
618         return mChannelUtilization;
619     }
620 
getCapacity()621     public int getCapacity() {
622         return mCapacity;
623     }
624 
isInterworking()625     public boolean isInterworking() {
626         return mAnt != null;
627     }
628 
getAnt()629     public Ant getAnt() {
630         return mAnt;
631     }
632 
isInternet()633     public boolean isInternet() {
634         return mInternet;
635     }
636 
getHSRelease()637     public HSRelease getHSRelease() {
638         return mHSRelease;
639     }
640 
getAnqpDomainID()641     public int getAnqpDomainID() {
642         return mAnqpDomainID;
643     }
644 
getOsuProviders()645     public byte[] getOsuProviders() {
646         if (mANQPElements == null) {
647             return null;
648         }
649         ANQPElement osuProviders = mANQPElements.get(Constants.ANQPElementType.HSOSUProviders);
650         return osuProviders != null ? ((RawByteElement) osuProviders).getPayload() : null;
651     }
652 
getAnqpOICount()653     public int getAnqpOICount() {
654         return mAnqpOICount;
655     }
656 
getRoamingConsortiums()657     public long[] getRoamingConsortiums() {
658         return mRoamingConsortiums;
659     }
660 
getANQPElements()661     public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
662         return mANQPElements;
663     }
664 
getChannelWidth()665     public int getChannelWidth() {
666         return mChannelWidth;
667     }
668 
getCenterfreq0()669     public int getCenterfreq0() {
670         return mCenterfreq0;
671     }
672 
getCenterfreq1()673     public int getCenterfreq1() {
674         return mCenterfreq1;
675     }
676 
getWifiMode()677     public int getWifiMode() {
678         return mWifiMode;
679     }
680 
getMaxNumberSpatialStreams()681     public int getMaxNumberSpatialStreams() {
682         return mMaxNumberSpatialStreams;
683     }
684 
getDtimInterval()685     public int getDtimInterval() {
686         return mDtimInterval;
687     }
688 
getCountryCode()689     public String getCountryCode() {
690         return mCountryCode;
691     }
692 
is80211McResponderSupport()693     public boolean is80211McResponderSupport() {
694         return mExtendedCapabilities.is80211McRTTResponder();
695     }
696 
isSSID_UTF8()697     public boolean isSSID_UTF8() {
698         return mExtendedCapabilities.isStrictUtf8();
699     }
700 
getMldMacAddress()701     public MacAddress getMldMacAddress() {
702         return mMldMacAddress;
703     }
704 
getMloLinkId()705     public int getMloLinkId() {
706         return mMloLinkId;
707     }
708 
getAffiliatedMloLinks()709     public List<MloLink> getAffiliatedMloLinks() {
710         return mAffiliatedMloLinks;
711     }
712 
getDisabledSubchannelBitmap()713     public byte[] getDisabledSubchannelBitmap() {
714         return mDisabledSubchannelBitmap;
715     }
716 
717     @Override
equals(Object thatObject)718     public boolean equals(Object thatObject) {
719         if (this == thatObject) {
720             return true;
721         }
722         if (thatObject == null || getClass() != thatObject.getClass()) {
723             return false;
724         }
725 
726         NetworkDetail that = (NetworkDetail)thatObject;
727 
728         return getSSID().equals(that.getSSID()) && getBSSID() == that.getBSSID();
729     }
730 
731     @Override
hashCode()732     public int hashCode() {
733         return ((mSSID.hashCode() * 31) + (int)(mBSSID >>> 32)) * 31 + (int)mBSSID;
734     }
735 
736     @Override
toString()737     public String toString() {
738         return "NetworkInfo{SSID='" + mSSID
739                 + "', HESSID=" + Utils.macToString(mHESSID)
740                 + ", BSSID=" + Utils.macToString(mBSSID)
741                 + ", StationCount=" + mStationCount
742                 + ", ChannelUtilization=" + mChannelUtilization
743                 + ", Capacity=" + mCapacity
744                 + ", Ant=" + mAnt + ", Internet=" + mInternet + ", HSRelease="
745                 + mHSRelease + ", AnqpDomainID" + mAnqpDomainID + ", AnqpOICount" + mAnqpOICount
746                 + ", RoamingConsortiums=" + Utils.roamingConsortiumsToString(mRoamingConsortiums)
747                 + "}";
748     }
749 
toKeyString()750     public String toKeyString() {
751         return mHESSID != 0 ?
752                 "'" + mSSID + "':" + Utils.macToString(mBSSID) + " ("
753                         + Utils.macToString(mHESSID) + ")"
754                 : "'" + mSSID + "':" + Utils.macToString(mBSSID);
755     }
756 
getBSSIDString()757     public String getBSSIDString() {
758         return Utils.macToString(mBSSID);
759     }
760 
761     /**
762      * Evaluates the ScanResult this NetworkDetail is built from
763      * returns true if built from a Beacon Frame
764      * returns false if built from a Probe Response
765      */
isBeaconFrame()766     public boolean isBeaconFrame() {
767         // Beacon frames have a 'Traffic Indication Map' Information element
768         // Probe Responses do not. This is indicated by a DTIM period > 0
769         return mDtimInterval > 0;
770     }
771 
772     /**
773      * Evaluates the ScanResult this NetworkDetail is built from
774      * returns true if built from a hidden Beacon Frame
775      * returns false if not hidden or not a Beacon
776      */
isHiddenBeaconFrame()777     public boolean isHiddenBeaconFrame() {
778         // Hidden networks are not 80211 standard, but it is common for a hidden network beacon
779         // frame to either send zero-value bytes as the SSID, or to send no bytes at all.
780         return isBeaconFrame() && mIsHiddenSsid;
781     }
782 
getMboAssociationDisallowedReasonCode()783     public int getMboAssociationDisallowedReasonCode() {
784         return mMboAssociationDisallowedReasonCode;
785     }
786 
isMboSupported()787     public boolean isMboSupported() {
788         return mMboSupported;
789     }
790 
isMboCellularDataAware()791     public boolean isMboCellularDataAware() {
792         return mMboCellularDataAware;
793     }
794 
isOceSupported()795     public boolean isOceSupported() {
796         return mOceSupported;
797     }
798 
799     /** Return whether the AP supports IEEE 802.11az non-trigger based ranging **/
is80211azNtbResponder()800     public boolean is80211azNtbResponder() {
801         return mIs11azNtbResponder;
802     }
803 
804     /** Return whether the AP supports IEEE 802.11az trigger based ranging **/
is80211azTbResponder()805     public boolean is80211azTbResponder() {
806         return mIs11azTbResponder;
807     }
808 
809     /**
810      * Return whether the AP requires HE stations to participate either in individual TWT
811      * agreements or Broadcast TWT operation.
812      **/
isTwtRequired()813     public boolean isTwtRequired() {
814         return mTwtRequired;
815     }
816 
817     /** Return whether individual TWT is supported. */
isIndividualTwtSupported()818     public boolean isIndividualTwtSupported() {
819         return mIndividualTwtSupported;
820     }
821 
822     /** Return whether broadcast TWT is supported */
isBroadcastTwtSupported()823     public boolean isBroadcastTwtSupported() {
824         return mBroadcastTwtSupported;
825     }
826 
827     /**
828      * Returns whether restricted TWT is supported or not. It enables enhanced medium access
829      * protection and resource reservation mechanisms for delivery of latency sensitive
830      * traffic.
831      */
isRestrictedTwtSupported()832     public boolean isRestrictedTwtSupported() {
833         return mRestrictedTwtSupported;
834     }
835 
836     /**
837      * Returns whether EPCS priority access supported or not. EPCS priority access is a
838      * mechanism that provides prioritized access to the wireless medium for authorized users to
839      * increase their probability of successful communication during periods of network
840      * congestion.
841      */
isEpcsPriorityAccessSupported()842     public boolean isEpcsPriorityAccessSupported() {
843         return mEpcsPriorityAccessSupported;
844     }
845 
846     /**
847      * @return true if Fast Initial Link Setup (FILS) capable
848      */
isFilsCapable()849     public boolean isFilsCapable() {
850         return mFilsCapable;
851     }
852 
853     /**
854      * Return 6Ghz AP type as defined in {@link InformationElementUtil.ApType6GHz}
855      **/
getApType6GHz()856     public InformationElementUtil.ApType6GHz getApType6GHz() {
857         return mApType6GHz;
858     }
859 }
860