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