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 // MLO Attributes 135 private MacAddress mMldMacAddress = null; 136 private int mMloLinkId = MloLink.INVALID_MLO_LINK_ID; 137 private List<MloLink> mAffiliatedMloLinks = Collections.emptyList(); 138 NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements, List<String> anqpLines, int freq)139 public NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements, 140 List<String> anqpLines, int freq) { 141 if (infoElements == null) { 142 infoElements = new ScanResult.InformationElement[0]; 143 } 144 145 mBSSID = Utils.parseMac(bssid); 146 147 String ssid = null; 148 boolean isHiddenSsid = false; 149 byte[] ssidOctets = null; 150 151 InformationElementUtil.BssLoad bssLoad = new InformationElementUtil.BssLoad(); 152 153 InformationElementUtil.Interworking interworking = 154 new InformationElementUtil.Interworking(); 155 156 InformationElementUtil.RoamingConsortium roamingConsortium = 157 new InformationElementUtil.RoamingConsortium(); 158 159 InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); 160 161 InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation(); 162 InformationElementUtil.VhtOperation vhtOperation = 163 new InformationElementUtil.VhtOperation(); 164 InformationElementUtil.HeOperation heOperation = new InformationElementUtil.HeOperation(); 165 InformationElementUtil.EhtOperation ehtOperation = 166 new InformationElementUtil.EhtOperation(); 167 168 InformationElementUtil.HtCapabilities htCapabilities = 169 new InformationElementUtil.HtCapabilities(); 170 InformationElementUtil.VhtCapabilities vhtCapabilities = 171 new InformationElementUtil.VhtCapabilities(); 172 InformationElementUtil.HeCapabilities heCapabilities = 173 new InformationElementUtil.HeCapabilities(); 174 InformationElementUtil.EhtCapabilities ehtCapabilities = 175 new InformationElementUtil.EhtCapabilities(); 176 InformationElementUtil.Rnr rnr = 177 new InformationElementUtil.Rnr(); 178 InformationElementUtil.MultiLink multiLink = 179 new InformationElementUtil.MultiLink(); 180 InformationElementUtil.ExtendedCapabilities extendedCapabilities = 181 new InformationElementUtil.ExtendedCapabilities(); 182 183 InformationElementUtil.Country country = 184 new InformationElementUtil.Country(); 185 186 InformationElementUtil.TrafficIndicationMap trafficIndicationMap = 187 new InformationElementUtil.TrafficIndicationMap(); 188 189 InformationElementUtil.SupportedRates supportedRates = 190 new InformationElementUtil.SupportedRates(); 191 InformationElementUtil.SupportedRates extendedSupportedRates = 192 new InformationElementUtil.SupportedRates(); 193 194 RuntimeException exception = null; 195 196 ArrayList<Integer> iesFound = new ArrayList<Integer>(); 197 try { 198 for (ScanResult.InformationElement ie : infoElements) { 199 iesFound.add(ie.id); 200 switch (ie.id) { 201 case ScanResult.InformationElement.EID_SSID: 202 ssidOctets = ie.bytes; 203 break; 204 case ScanResult.InformationElement.EID_BSS_LOAD: 205 bssLoad.from(ie); 206 break; 207 case ScanResult.InformationElement.EID_HT_OPERATION: 208 htOperation.from(ie); 209 break; 210 case ScanResult.InformationElement.EID_VHT_OPERATION: 211 vhtOperation.from(ie); 212 break; 213 case ScanResult.InformationElement.EID_HT_CAPABILITIES: 214 htCapabilities.from(ie); 215 break; 216 case ScanResult.InformationElement.EID_VHT_CAPABILITIES: 217 vhtCapabilities.from(ie); 218 break; 219 case ScanResult.InformationElement.EID_INTERWORKING: 220 interworking.from(ie); 221 break; 222 case ScanResult.InformationElement.EID_ROAMING_CONSORTIUM: 223 roamingConsortium.from(ie); 224 break; 225 case ScanResult.InformationElement.EID_VSA: 226 vsa.from(ie); 227 break; 228 case ScanResult.InformationElement.EID_EXTENDED_CAPS: 229 extendedCapabilities.from(ie); 230 break; 231 case ScanResult.InformationElement.EID_COUNTRY: 232 country.from(ie); 233 break; 234 case ScanResult.InformationElement.EID_TIM: 235 trafficIndicationMap.from(ie); 236 break; 237 case ScanResult.InformationElement.EID_SUPPORTED_RATES: 238 supportedRates.from(ie); 239 break; 240 case ScanResult.InformationElement.EID_EXTENDED_SUPPORTED_RATES: 241 extendedSupportedRates.from(ie); 242 break; 243 case ScanResult.InformationElement.EID_RNR: 244 rnr.from(ie); 245 break; 246 case ScanResult.InformationElement.EID_EXTENSION_PRESENT: 247 switch(ie.idExt) { 248 case ScanResult.InformationElement.EID_EXT_HE_OPERATION: 249 heOperation.from(ie); 250 break; 251 case ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES: 252 heCapabilities.from(ie); 253 break; 254 case ScanResult.InformationElement.EID_EXT_EHT_OPERATION: 255 ehtOperation.from(ie); 256 break; 257 case ScanResult.InformationElement.EID_EXT_EHT_CAPABILITIES: 258 ehtCapabilities.from(ie); 259 break; 260 case ScanResult.InformationElement.EID_EXT_MULTI_LINK: 261 multiLink.from(ie); 262 break; 263 default: 264 break; 265 } 266 break; 267 default: 268 break; 269 } 270 } 271 } 272 catch (IllegalArgumentException | BufferUnderflowException | ArrayIndexOutOfBoundsException e) { 273 Log.d(Utils.hs2LogTag(getClass()), "Caught " + e); 274 if (ssidOctets == null) { 275 throw new IllegalArgumentException("Malformed IE string (no SSID)", e); 276 } 277 exception = e; 278 } 279 if (ssidOctets != null) { 280 /* 281 * Strict use of the "UTF-8 SSID" bit by APs appears to be spotty at best even if the 282 * encoding truly is in UTF-8. An unconditional attempt to decode the SSID as UTF-8 is 283 * therefore always made with a fall back to 8859-1 under normal circumstances. 284 * If, however, a previous exception was detected and the UTF-8 bit is set, failure to 285 * decode the SSID will be used as an indication that the whole frame is malformed and 286 * an exception will be triggered. 287 */ 288 CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); 289 try { 290 CharBuffer decoded = decoder.decode(ByteBuffer.wrap(ssidOctets)); 291 ssid = decoded.toString(); 292 } 293 catch (CharacterCodingException cce) { 294 ssid = null; 295 } 296 297 if (ssid == null) { 298 if (extendedCapabilities.isStrictUtf8() && exception != null) { 299 throw new IllegalArgumentException("Failed to decode SSID in dubious IE string"); 300 } 301 else { 302 ssid = new String(ssidOctets, StandardCharsets.ISO_8859_1); 303 } 304 } 305 isHiddenSsid = true; 306 for (byte byteVal : ssidOctets) { 307 if (byteVal != 0) { 308 isHiddenSsid = false; 309 break; 310 } 311 } 312 } 313 314 mSSID = ssid; 315 mHESSID = interworking.hessid; 316 mIsHiddenSsid = isHiddenSsid; 317 mStationCount = bssLoad.stationCount; 318 mChannelUtilization = bssLoad.channelUtilization; 319 mCapacity = bssLoad.capacity; 320 mAnt = interworking.ant; 321 mInternet = interworking.internet; 322 mHSRelease = vsa.hsRelease; 323 mAnqpDomainID = vsa.anqpDomainID; 324 mMboSupported = vsa.IsMboCapable; 325 mMboCellularDataAware = vsa.IsMboApCellularDataAware; 326 mOceSupported = vsa.IsOceCapable; 327 mMboAssociationDisallowedReasonCode = vsa.mboAssociationDisallowedReasonCode; 328 mAnqpOICount = roamingConsortium.anqpOICount; 329 mRoamingConsortiums = roamingConsortium.getRoamingConsortiums(); 330 mExtendedCapabilities = extendedCapabilities; 331 mANQPElements = null; 332 //set up channel info 333 mPrimaryFreq = freq; 334 int channelWidth = ScanResult.UNSPECIFIED; 335 int centerFreq0 = mPrimaryFreq; 336 int centerFreq1 = 0; 337 338 if (ehtOperation.isPresent()) { 339 //TODO: include parsing of EHT_Operation to collect BW and center freq. 340 } 341 342 if (ehtOperation.isPresent()) { 343 //TODO Add impact for using info from EHT capabilities and EHT operation IEs 344 } 345 346 // Check if HE Operation IE is present 347 if (heOperation.isPresent()) { 348 // If 6GHz info is present, then parameters should be acquired from HE Operation IE 349 if (heOperation.is6GhzInfoPresent()) { 350 channelWidth = heOperation.getChannelWidth(); 351 centerFreq0 = heOperation.getCenterFreq0(); 352 centerFreq1 = heOperation.getCenterFreq1(); 353 } else if (heOperation.isVhtInfoPresent()) { 354 // VHT Operation Info could be included inside the HE Operation IE 355 vhtOperation.from(heOperation.getVhtInfoElement()); 356 } 357 } 358 359 // Proceed to VHT Operation IE if parameters were not obtained from HE Operation IE 360 // Not operating in 6GHz 361 if (channelWidth == ScanResult.UNSPECIFIED) { 362 if (vhtOperation.isPresent()) { 363 channelWidth = vhtOperation.getChannelWidth(); 364 if (channelWidth != ScanResult.UNSPECIFIED) { 365 centerFreq0 = vhtOperation.getCenterFreq0(); 366 centerFreq1 = vhtOperation.getCenterFreq1(); 367 } 368 } 369 } 370 371 // Proceed to HT Operation IE if parameters were not obtained from VHT/HE Operation IEs 372 // Apply to operating in 2.4/5GHz with 20/40MHz channels 373 if (channelWidth == ScanResult.UNSPECIFIED) { 374 //Either no vht, or vht shows BW is 40/20 MHz 375 if (htOperation.isPresent()) { 376 channelWidth = htOperation.getChannelWidth(); 377 centerFreq0 = htOperation.getCenterFreq0(mPrimaryFreq); 378 } 379 } 380 381 if (channelWidth == ScanResult.UNSPECIFIED) { 382 // Failed to obtain channel info from HE, VHT, HT IEs (possibly a 802.11a/b/g legacy AP) 383 channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ; 384 } 385 386 mChannelWidth = channelWidth; 387 mCenterfreq0 = centerFreq0; 388 mCenterfreq1 = centerFreq1; 389 390 if (country.isValid()) { 391 mCountryCode = country.getCountryCode(); 392 } 393 394 // If trafficIndicationMap is not valid, mDtimPeriod will be negative 395 if (trafficIndicationMap.isValid()) { 396 mDtimInterval = trafficIndicationMap.mDtimPeriod; 397 } 398 399 mMaxNumberSpatialStreams = Math.max(heCapabilities.getMaxNumberSpatialStreams(), 400 Math.max(vhtCapabilities.getMaxNumberSpatialStreams(), 401 htCapabilities.getMaxNumberSpatialStreams())); 402 403 int maxRateA = 0; 404 int maxRateB = 0; 405 // If we got some Extended supported rates, consider them, if not default to 0 406 if (extendedSupportedRates.isValid()) { 407 // rates are sorted from smallest to largest in InformationElement 408 maxRateB = extendedSupportedRates.mRates.get(extendedSupportedRates.mRates.size() - 1); 409 } 410 // Only process the determination logic if we got a 'SupportedRates' 411 if (supportedRates.isValid()) { 412 maxRateA = supportedRates.mRates.get(supportedRates.mRates.size() - 1); 413 mMaxRate = maxRateA > maxRateB ? maxRateA : maxRateB; 414 mWifiMode = InformationElementUtil.WifiMode.determineMode(mPrimaryFreq, mMaxRate, 415 ehtOperation.isPresent(), heOperation.isPresent(), vhtOperation.isPresent(), 416 htOperation.isPresent(), 417 iesFound.contains(ScanResult.InformationElement.EID_ERP)); 418 } else { 419 mWifiMode = 0; 420 mMaxRate = 0; 421 } 422 423 if (multiLink.isPresent()) { 424 mMldMacAddress = multiLink.getMldMacAddress(); 425 mMloLinkId = multiLink.getLinkId(); 426 if (rnr.isPresent()) { 427 if (!rnr.getAffiliatedMloLinks().isEmpty()) { 428 mAffiliatedMloLinks = new ArrayList<>(rnr.getAffiliatedMloLinks()); 429 } else if (!multiLink.getAffiliatedLinks().isEmpty()) { 430 mAffiliatedMloLinks = new ArrayList<>(multiLink.getAffiliatedLinks()); 431 } 432 } 433 434 // Add the current link to the list of links if not empty 435 if (!mAffiliatedMloLinks.isEmpty()) { 436 MloLink link = new MloLink(); 437 link.setApMacAddress(MacAddress.fromString(bssid)); 438 link.setChannel(ScanResult.convertFrequencyMhzToChannelIfSupported(mPrimaryFreq)); 439 link.setBand(ScanResult.toBand(mPrimaryFreq)); 440 link.setLinkId(mMloLinkId); 441 mAffiliatedMloLinks.add(link); 442 } 443 } 444 445 if (DBG) { 446 Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " 447 + mPrimaryFreq + " Centerfreq0: " + mCenterfreq0 + " Centerfreq1: " 448 + mCenterfreq1 + (extendedCapabilities.is80211McRTTResponder() 449 ? " Support RTT responder" : " Do not support RTT responder") 450 + " MaxNumberSpatialStreams: " + mMaxNumberSpatialStreams 451 + " MboAssociationDisallowedReasonCode: " 452 + mMboAssociationDisallowedReasonCode); 453 Log.v("WifiMode", mSSID 454 + ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode) 455 + ", Freq: " + mPrimaryFreq 456 + ", MaxRate: " + mMaxRate 457 + ", EHT: " + String.valueOf(ehtOperation.isPresent()) 458 + ", HE: " + String.valueOf(heOperation.isPresent()) 459 + ", VHT: " + String.valueOf(vhtOperation.isPresent()) 460 + ", HT: " + String.valueOf(htOperation.isPresent()) 461 + ", ERP: " + String.valueOf( 462 iesFound.contains(ScanResult.InformationElement.EID_ERP)) 463 + ", SupportedRates: " + supportedRates.toString() 464 + " ExtendedSupportedRates: " + extendedSupportedRates.toString()); 465 } 466 } 467 468 /** 469 * Copy constructor 470 */ NetworkDetail(NetworkDetail networkDetail)471 public NetworkDetail(NetworkDetail networkDetail) { 472 this(networkDetail, networkDetail.mANQPElements); 473 } 474 getAndAdvancePayload(ByteBuffer data, int plLength)475 private static ByteBuffer getAndAdvancePayload(ByteBuffer data, int plLength) { 476 ByteBuffer payload = data.duplicate().order(data.order()); 477 payload.limit(payload.position() + plLength); 478 data.position(data.position() + plLength); 479 return payload; 480 } 481 NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements)482 private NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 483 mSSID = base.mSSID; 484 mIsHiddenSsid = base.mIsHiddenSsid; 485 mBSSID = base.mBSSID; 486 mHESSID = base.mHESSID; 487 mStationCount = base.mStationCount; 488 mChannelUtilization = base.mChannelUtilization; 489 mCapacity = base.mCapacity; 490 mAnt = base.mAnt; 491 mInternet = base.mInternet; 492 mHSRelease = base.mHSRelease; 493 mAnqpDomainID = base.mAnqpDomainID; 494 mAnqpOICount = base.mAnqpOICount; 495 mRoamingConsortiums = base.mRoamingConsortiums; 496 mExtendedCapabilities = 497 new InformationElementUtil.ExtendedCapabilities(base.mExtendedCapabilities); 498 mANQPElements = anqpElements; 499 mChannelWidth = base.mChannelWidth; 500 mPrimaryFreq = base.mPrimaryFreq; 501 mCenterfreq0 = base.mCenterfreq0; 502 mCenterfreq1 = base.mCenterfreq1; 503 mDtimInterval = base.mDtimInterval; 504 mCountryCode = base.mCountryCode; 505 mWifiMode = base.mWifiMode; 506 mMaxRate = base.mMaxRate; 507 mMaxNumberSpatialStreams = base.mMaxNumberSpatialStreams; 508 mMboSupported = base.mMboSupported; 509 mMboCellularDataAware = base.mMboCellularDataAware; 510 mOceSupported = base.mOceSupported; 511 mMboAssociationDisallowedReasonCode = base.mMboAssociationDisallowedReasonCode; 512 } 513 complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements)514 public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 515 return new NetworkDetail(this, anqpElements); 516 } 517 queriable(List<Constants.ANQPElementType> queryElements)518 public boolean queriable(List<Constants.ANQPElementType> queryElements) { 519 return mAnt != null && 520 (Constants.hasBaseANQPElements(queryElements) || 521 Constants.hasR2Elements(queryElements) && mHSRelease == HSRelease.R2); 522 } 523 has80211uInfo()524 public boolean has80211uInfo() { 525 return mAnt != null || mRoamingConsortiums != null || mHSRelease != null; 526 } 527 hasInterworking()528 public boolean hasInterworking() { 529 return mAnt != null; 530 } 531 getSSID()532 public String getSSID() { 533 return mSSID; 534 } 535 getTrimmedSSID()536 public String getTrimmedSSID() { 537 if (mSSID != null) { 538 for (int n = 0; n < mSSID.length(); n++) { 539 if (mSSID.charAt(n) != 0) { 540 return mSSID; 541 } 542 } 543 } 544 return ""; 545 } 546 getHESSID()547 public long getHESSID() { 548 return mHESSID; 549 } 550 getBSSID()551 public long getBSSID() { 552 return mBSSID; 553 } 554 getStationCount()555 public int getStationCount() { 556 return mStationCount; 557 } 558 getChannelUtilization()559 public int getChannelUtilization() { 560 return mChannelUtilization; 561 } 562 getCapacity()563 public int getCapacity() { 564 return mCapacity; 565 } 566 isInterworking()567 public boolean isInterworking() { 568 return mAnt != null; 569 } 570 getAnt()571 public Ant getAnt() { 572 return mAnt; 573 } 574 isInternet()575 public boolean isInternet() { 576 return mInternet; 577 } 578 getHSRelease()579 public HSRelease getHSRelease() { 580 return mHSRelease; 581 } 582 getAnqpDomainID()583 public int getAnqpDomainID() { 584 return mAnqpDomainID; 585 } 586 getOsuProviders()587 public byte[] getOsuProviders() { 588 if (mANQPElements == null) { 589 return null; 590 } 591 ANQPElement osuProviders = mANQPElements.get(Constants.ANQPElementType.HSOSUProviders); 592 return osuProviders != null ? ((RawByteElement) osuProviders).getPayload() : null; 593 } 594 getAnqpOICount()595 public int getAnqpOICount() { 596 return mAnqpOICount; 597 } 598 getRoamingConsortiums()599 public long[] getRoamingConsortiums() { 600 return mRoamingConsortiums; 601 } 602 getANQPElements()603 public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() { 604 return mANQPElements; 605 } 606 getChannelWidth()607 public int getChannelWidth() { 608 return mChannelWidth; 609 } 610 getCenterfreq0()611 public int getCenterfreq0() { 612 return mCenterfreq0; 613 } 614 getCenterfreq1()615 public int getCenterfreq1() { 616 return mCenterfreq1; 617 } 618 getWifiMode()619 public int getWifiMode() { 620 return mWifiMode; 621 } 622 getMaxNumberSpatialStreams()623 public int getMaxNumberSpatialStreams() { 624 return mMaxNumberSpatialStreams; 625 } 626 getDtimInterval()627 public int getDtimInterval() { 628 return mDtimInterval; 629 } 630 getCountryCode()631 public String getCountryCode() { 632 return mCountryCode; 633 } 634 is80211McResponderSupport()635 public boolean is80211McResponderSupport() { 636 return mExtendedCapabilities.is80211McRTTResponder(); 637 } 638 isSSID_UTF8()639 public boolean isSSID_UTF8() { 640 return mExtendedCapabilities.isStrictUtf8(); 641 } 642 getMldMacAddress()643 public MacAddress getMldMacAddress() { 644 return mMldMacAddress; 645 } 646 getMloLinkId()647 public int getMloLinkId() { 648 return mMloLinkId; 649 } 650 getAffiliatedMloLinks()651 public List<MloLink> getAffiliatedMloLinks() { 652 return mAffiliatedMloLinks; 653 } 654 655 @Override equals(Object thatObject)656 public boolean equals(Object thatObject) { 657 if (this == thatObject) { 658 return true; 659 } 660 if (thatObject == null || getClass() != thatObject.getClass()) { 661 return false; 662 } 663 664 NetworkDetail that = (NetworkDetail)thatObject; 665 666 return getSSID().equals(that.getSSID()) && getBSSID() == that.getBSSID(); 667 } 668 669 @Override hashCode()670 public int hashCode() { 671 return ((mSSID.hashCode() * 31) + (int)(mBSSID >>> 32)) * 31 + (int)mBSSID; 672 } 673 674 @Override toString()675 public String toString() { 676 return "NetworkInfo{SSID='" + mSSID 677 + "', HESSID=" + Utils.macToSimpleString(mHESSID) 678 + ", BSSID=" + Utils.macToSimpleString(mBSSID) 679 + ", StationCount=" + mStationCount 680 + ", ChannelUtilization=" + mChannelUtilization 681 + ", Capacity=" + mCapacity 682 + ", Ant=" + mAnt + ", Internet=" + mInternet + ", HSRelease=" 683 + mHSRelease + ", AnqpDomainID" + mAnqpDomainID + ", AnqpOICount" + mAnqpOICount 684 + ", RoamingConsortiums=" + Utils.roamingConsortiumsToString(mRoamingConsortiums) 685 + "}"; 686 } 687 toKeyString()688 public String toKeyString() { 689 return mHESSID != 0 ? 690 "'" + mSSID + "':" + Utils.macToSimpleString(mBSSID) + " (" 691 + Utils.macToSimpleString(mHESSID) + ")" 692 : "'" + mSSID + "':" + Utils.macToSimpleString(mBSSID); 693 } 694 getBSSIDString()695 public String getBSSIDString() { 696 return Utils.macToString(mBSSID); 697 } 698 699 /** 700 * Evaluates the ScanResult this NetworkDetail is built from 701 * returns true if built from a Beacon Frame 702 * returns false if built from a Probe Response 703 */ isBeaconFrame()704 public boolean isBeaconFrame() { 705 // Beacon frames have a 'Traffic Indication Map' Information element 706 // Probe Responses do not. This is indicated by a DTIM period > 0 707 return mDtimInterval > 0; 708 } 709 710 /** 711 * Evaluates the ScanResult this NetworkDetail is built from 712 * returns true if built from a hidden Beacon Frame 713 * returns false if not hidden or not a Beacon 714 */ isHiddenBeaconFrame()715 public boolean isHiddenBeaconFrame() { 716 // Hidden networks are not 80211 standard, but it is common for a hidden network beacon 717 // frame to either send zero-value bytes as the SSID, or to send no bytes at all. 718 return isBeaconFrame() && mIsHiddenSsid; 719 } 720 getMboAssociationDisallowedReasonCode()721 public int getMboAssociationDisallowedReasonCode() { 722 return mMboAssociationDisallowedReasonCode; 723 } 724 isMboSupported()725 public boolean isMboSupported() { 726 return mMboSupported; 727 } 728 isMboCellularDataAware()729 public boolean isMboCellularDataAware() { 730 return mMboCellularDataAware; 731 } 732 isOceSupported()733 public boolean isOceSupported() { 734 return mOceSupported; 735 } 736 } 737