1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.wifi.util; 17 18 import android.hardware.wifi.WifiBand; 19 import android.net.MacAddress; 20 import android.net.wifi.MloLink; 21 import android.net.wifi.ScanResult; 22 import android.net.wifi.ScanResult.InformationElement; 23 import android.net.wifi.WifiAnnotations.Cipher; 24 import android.net.wifi.WifiAnnotations.KeyMgmt; 25 import android.net.wifi.WifiAnnotations.Protocol; 26 import android.net.wifi.WifiScanner; 27 import android.net.wifi.nl80211.NativeScanResult; 28 import android.net.wifi.nl80211.WifiNl80211Manager; 29 import android.net.wifi.util.HexEncoding; 30 import android.util.Log; 31 import android.util.SparseIntArray; 32 33 import com.android.server.wifi.ByteBufferReader; 34 import com.android.server.wifi.MboOceConstants; 35 import com.android.server.wifi.hotspot2.NetworkDetail; 36 import com.android.server.wifi.hotspot2.anqp.Constants; 37 38 import java.io.ByteArrayOutputStream; 39 import java.io.IOException; 40 import java.nio.BufferUnderflowException; 41 import java.nio.ByteBuffer; 42 import java.nio.ByteOrder; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.BitSet; 46 import java.util.List; 47 import java.util.Locale; 48 49 public class InformationElementUtil { 50 private static final String TAG = "InformationElementUtil"; 51 private static final boolean DBG = false; 52 53 /** 54 * 6 GHz Access Point type as encoded in the Regulatory Info subfield in the Control field of 55 * the 6 GHz Operation Information field of the HE Operation element. 56 * Reference E.2.7 6 GHz band (IEEE Std 802.11ax-2021). 57 */ 58 public enum ApType6GHz { 59 AP_TYPE_6GHZ_UNKNOWN, 60 AP_TYPE_6GHZ_INDOOR, 61 AP_TYPE_6GHZ_STANDARD_POWER, 62 } 63 64 /** 65 * Defragment Element class 66 * 67 * IEEE Std 802.11™‐2020, Section: 10.28.11 Element fragmentation describes a fragmented sub 68 * element as, 69 * | SubEID | Len | Data | FragId | Len | Data | FragId | Len| Data ... 70 * Octets: 1 1 255 1 1 255 1 1 m 71 * Values: eid 255 fid 255 fid m 72 * 73 */ 74 private static class DefragmentElement { 75 /** Defagmented element bytes */ 76 public byte[] bytes; 77 /** Bytes read to defragment the fragmented element */ 78 public int bytesRead = 0; 79 80 public static final int FRAG_MAX_LEN = 255; 81 public static final int FRAGMENT_ELEMENT_EID = 242; 82 DefragmentElement(byte[] bytes, int start, int eid, int fid)83 DefragmentElement(byte[] bytes, int start, int eid, int fid) { 84 if (bytes == null) return; 85 ByteArrayOutputStream defrag = new ByteArrayOutputStream(); 86 ByteBuffer element = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 87 element.position(start); 88 try { 89 if ((element.get() & Constants.BYTE_MASK) != eid) return; 90 // Add EID, 255 as the parser expects the element header. 91 defrag.write(eid); 92 defrag.write(255); 93 int fragLen, fragId; 94 do { 95 fragLen = element.get() & Constants.BYTE_MASK; 96 byte[] b = new byte[fragLen]; 97 element.get(b); 98 defrag.write(b); 99 // Mark the position to undo the extra read. 100 element.mark(); 101 if (element.remaining() <= 0) break; 102 fragId = element.get() & Constants.BYTE_MASK; 103 } while (fragLen == FRAG_MAX_LEN && fragId == fid); 104 // Reset the extra get. 105 element.reset(); 106 } catch (IOException e) { 107 if (DBG) { 108 Log.w(TAG, "Failed to defragment sub element: " + e.getMessage()); 109 } 110 return; 111 } catch (IndexOutOfBoundsException e) { 112 if (DBG) { 113 Log.e(TAG, "Failed to defragment sub element: " + e.getMessage()); 114 } 115 return; 116 } catch (BufferUnderflowException e) { 117 if (DBG) { 118 Log.w(TAG, "Failed to defragment sub element: " + e.getMessage()); 119 } 120 return; 121 } 122 123 this.bytes = defrag.toByteArray(); 124 bytesRead = element.position() - start; 125 } 126 } 127 128 /** Converts InformationElement to hex string */ toHexString(InformationElement e)129 public static String toHexString(InformationElement e) { 130 StringBuilder sb = new StringBuilder(); 131 sb.append(HexEncoding.encode(new byte[]{(byte) e.id})); 132 if (e.id == InformationElement.EID_EXTENSION_PRESENT) { 133 sb.append(HexEncoding.encode(new byte[]{(byte) e.idExt})); 134 } 135 sb.append(HexEncoding.encode(new byte[]{(byte) e.bytes.length})); 136 sb.append(HexEncoding.encode(e.bytes)); 137 return sb.toString(); 138 } 139 140 /** Parses information elements from hex string */ parseInformationElements(String data)141 public static InformationElement[] parseInformationElements(String data) { 142 if (data == null) { 143 return new InformationElement[0]; 144 } 145 return parseInformationElements(HexEncoding.decode(data)); 146 } 147 isFragmentable(int eid, int eidExt)148 private static boolean isFragmentable(int eid, int eidExt) { 149 // Refer IEE802.11BE D2.3, Section 9.4.2 Elements 150 return ((eid == InformationElement.EID_EXTENSION_PRESENT) 151 && (eidExt == InformationElement.EID_EXT_MULTI_LINK)); 152 } 153 parseInformationElements(byte[] bytes)154 public static InformationElement[] parseInformationElements(byte[] bytes) { 155 if (bytes == null) { 156 return new InformationElement[0]; 157 } 158 ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 159 160 ArrayList<InformationElement> infoElements = new ArrayList<>(); 161 boolean found_ssid = false; 162 while (data.remaining() > 1) { 163 // Mark the start of the data 164 data.mark(); 165 int eid = data.get() & Constants.BYTE_MASK; 166 int eidExt = 0; 167 int elementLength = data.get() & Constants.BYTE_MASK; 168 DefragmentElement defrag = null; 169 170 if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID 171 && found_ssid)) { 172 // APs often pad the data with bytes that happen to match that of the EID_SSID 173 // marker. This is not due to a known issue for APs to incorrectly send the SSID 174 // name multiple times. 175 break; 176 } 177 if (eid == InformationElement.EID_SSID) { 178 found_ssid = true; 179 } else if (eid == InformationElement.EID_EXTENSION_PRESENT) { 180 if (elementLength == 0) { 181 // Malformed IE, skipping 182 break; 183 } 184 eidExt = data.get() & Constants.BYTE_MASK; 185 if (isFragmentable(eid, eidExt) 186 && elementLength == DefragmentElement.FRAG_MAX_LEN) { 187 // Fragmented IE. Reset the position to head to defragment. 188 data.reset(); 189 defrag = 190 new DefragmentElement( 191 bytes, 192 data.position(), 193 eid, 194 DefragmentElement.FRAGMENT_ELEMENT_EID); 195 } 196 elementLength--; 197 } 198 199 InformationElement ie = new InformationElement(); 200 ie.id = eid; 201 ie.idExt = eidExt; 202 if (defrag != null) { 203 if (defrag.bytesRead == 0) { 204 // Malformed IE skipping 205 break; 206 } 207 // Skip first three bytes: eid, len, eidExt as it is already processed. 208 ie.bytes = Arrays.copyOfRange(defrag.bytes, 3, defrag.bytes.length); 209 int newPosition = data.position() + defrag.bytesRead; 210 data.position(newPosition); 211 } else { 212 ie.bytes = new byte[elementLength]; 213 data.get(ie.bytes); 214 } 215 infoElements.add(ie); 216 } 217 return infoElements.toArray(new InformationElement[infoElements.size()]); 218 } 219 220 /** 221 * Parse and retrieve the Roaming Consortium Information Element from the list of IEs. 222 * 223 * @param ies List of IEs to retrieve from 224 * @return {@link RoamingConsortium} 225 */ getRoamingConsortiumIE(InformationElement[] ies)226 public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) { 227 RoamingConsortium roamingConsortium = new RoamingConsortium(); 228 if (ies != null) { 229 for (InformationElement ie : ies) { 230 if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) { 231 try { 232 roamingConsortium.from(ie); 233 } catch (RuntimeException e) { 234 Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage()); 235 } 236 } 237 } 238 } 239 return roamingConsortium; 240 } 241 242 /** 243 * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs. 244 * 245 * @param ies List of IEs to retrieve from 246 * @return {@link Vsa} 247 */ getHS2VendorSpecificIE(InformationElement[] ies)248 public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) { 249 Vsa vsa = new Vsa(); 250 if (ies != null) { 251 for (InformationElement ie : ies) { 252 if (ie.id == InformationElement.EID_VSA) { 253 try { 254 vsa.from(ie); 255 } catch (RuntimeException e) { 256 Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage()); 257 } 258 } 259 } 260 } 261 return vsa; 262 } 263 264 /** 265 * Parse and retrieve all Vendor Specific Information Elements from the list of IEs. 266 * 267 * @param ies List of IEs to retrieve from 268 * @return List of {@link Vsa} 269 */ getVendorSpecificIE(InformationElement[] ies)270 public static List<Vsa> getVendorSpecificIE(InformationElement[] ies) { 271 List<Vsa> vsas = new ArrayList<>(); 272 if (ies != null) { 273 for (InformationElement ie : ies) { 274 if (ie.id == InformationElement.EID_VSA) { 275 try { 276 Vsa vsa = new Vsa(); 277 vsa.from(ie); 278 vsas.add(vsa); 279 } catch (RuntimeException e) { 280 Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage()); 281 } 282 } 283 } 284 } 285 return vsas; 286 } 287 288 /** 289 * Parse and retrieve the Interworking information element from the list of IEs. 290 * 291 * @param ies List of IEs to retrieve from 292 * @return {@link Interworking} 293 */ getInterworkingIE(InformationElement[] ies)294 public static Interworking getInterworkingIE(InformationElement[] ies) { 295 Interworking interworking = new Interworking(); 296 if (ies != null) { 297 for (InformationElement ie : ies) { 298 if (ie.id == InformationElement.EID_INTERWORKING) { 299 try { 300 interworking.from(ie); 301 } catch (RuntimeException e) { 302 Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage()); 303 } 304 } 305 } 306 } 307 return interworking; 308 } 309 310 public static class BssLoad { 311 public static final int INVALID = -1; 312 public static final int MAX_CHANNEL_UTILIZATION = 255; 313 public static final int MIN_CHANNEL_UTILIZATION = 0; 314 public static final int CHANNEL_UTILIZATION_SCALE = 256; 315 public int stationCount = INVALID; 316 public int channelUtilization = INVALID; 317 public int capacity = INVALID; 318 from(InformationElement ie)319 public void from(InformationElement ie) { 320 if (ie.id != InformationElement.EID_BSS_LOAD) { 321 throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id); 322 } 323 if (ie.bytes.length != 5) { 324 throw new IllegalArgumentException("BSS Load element length is not 5: " 325 + ie.bytes.length); 326 } 327 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 328 stationCount = data.getShort() & Constants.SHORT_MASK; 329 channelUtilization = data.get() & Constants.BYTE_MASK; 330 capacity = data.getShort() & Constants.SHORT_MASK; 331 } 332 } 333 334 /** 335 * Rnr: represents the Reduced Neighbor Report (RNR) IE 336 * As described by IEEE 802.11 Specification Section 9.4.2.170 337 */ 338 public static class Rnr { 339 private static final int TBTT_INFO_COUNT_OFFSET = 0; 340 private static final int TBTT_INFO_COUNT_MASK = 0xF0; 341 private static final int TBTT_INFO_COUNT_SHIFT = 4; 342 private static final int TBTT_INFO_LENGTH_OFFSET = 1; 343 private static final int TBTT_INFO_OP_CLASS_OFFSET = 2; 344 private static final int TBTT_INFO_CHANNEL_OFFSET = 3; 345 private static final int TBTT_INFO_SET_START_OFFSET = 4; 346 private static final int MLD_ID_START_OFFSET = 0; 347 private static final int LINK_ID_START_OFFSET = 1; 348 private static final int LINK_ID_MASK = 0x0F; 349 350 private boolean mPresent = false; 351 private List<MloLink> mAffiliatedMloLinks = new ArrayList<>(); 352 353 /** 354 * Returns whether the RNR Information Element is present. 355 */ isPresent()356 public boolean isPresent() { 357 return mPresent; 358 } 359 360 /** 361 * Returns the list of the affiliated MLO links 362 */ getAffiliatedMloLinks()363 public List<MloLink> getAffiliatedMloLinks() { 364 return mAffiliatedMloLinks; 365 } 366 367 /** 368 * Parse RNR Operation IE 369 * 370 * RNR format as described in IEEE 802.11 specs, Section 9.4.2.170 371 * 372 * | ElementID | Length | Neighbor AP Information Fields | 373 * Octets: 1 1 variable 374 * 375 * 376 * Where Neighbor AP Information Fields is one or more Neighbor AP Information Field as, 377 * 378 * | Header | Operating Class | Channel | TBTT Information Set | 379 * Octets: 2 1 1 variable 380 * 381 * 382 * The Header subfield is described as follows, 383 * 384 * | Type | Filtered AP | Reserved | Count | Length | 385 * Bits: 2 1 1 4 8 386 * 387 * 388 * Information Set is one or more TBTT Information fields, which is described as, 389 * 390 * | Offset | BSSID | Short-SSID | BSS Params | 20MHz PSD | MLD Params| 391 * Octets: 1 0 or 6 0 or 4 0 or 1 0 or 1 0 or 3 392 * 393 * 394 * The MLD Params are described as, 395 * | MLD ID | Link ID | BSS Change Count | Reserved | 396 * Bits: 8 4 8 4 397 * 398 * Note: InformationElement.bytes has 'Element ID' and 'Length' 399 * stripped off already 400 * 401 */ from(InformationElement ie)402 public void from(InformationElement ie) { 403 if (ie.id != InformationElement.EID_RNR) { 404 throw new IllegalArgumentException("Element id is not RNR"); 405 } 406 407 int startOffset = 0; 408 while (ie.bytes.length > startOffset + TBTT_INFO_SET_START_OFFSET) { 409 int tbttInfoCount = 410 ie.bytes[startOffset + TBTT_INFO_COUNT_OFFSET] & TBTT_INFO_COUNT_MASK; 411 tbttInfoCount >>= TBTT_INFO_COUNT_SHIFT; 412 tbttInfoCount++; 413 414 int tbttInfoLen = 415 ie.bytes[startOffset + TBTT_INFO_LENGTH_OFFSET] & Constants.BYTE_MASK; 416 int tbttInfoStartOffset = startOffset + TBTT_INFO_SET_START_OFFSET; 417 418 // Only handle TBTT info with MLD Info 419 if (tbttInfoLen == 4 || tbttInfoLen >= 16) { 420 // Make sure length allows for this TBTT Info 421 if (ie.bytes.length < startOffset + TBTT_INFO_SET_START_OFFSET 422 + tbttInfoLen * tbttInfoCount) { 423 if (DBG) { 424 Log.w(TAG, "Invalid RNR len, not enough for TBTT Info: " 425 + ie.bytes.length + "/" + tbttInfoLen + "/" + tbttInfoCount); 426 } 427 // Skipping parsing of the IE 428 return; 429 } 430 431 int mldStartOffset; 432 int bssidOffset; 433 434 if (tbttInfoLen == 4) { 435 mldStartOffset = 1; 436 bssidOffset = -1; 437 } else { 438 mldStartOffset = 13; 439 bssidOffset = 1; 440 } 441 442 int opClass = ie.bytes[startOffset + TBTT_INFO_OP_CLASS_OFFSET] 443 & Constants.BYTE_MASK; 444 int channel = ie.bytes[startOffset + TBTT_INFO_CHANNEL_OFFSET] 445 & Constants.BYTE_MASK; 446 int band = ScanResult.getBandFromOpClass(opClass, channel); 447 if (band == WifiScanner.WIFI_BAND_UNSPECIFIED) { 448 if (DBG) { 449 Log.w(TAG, "Invalid op class/channel in RNR TBTT Info: " 450 + opClass + "/" + channel); 451 } 452 // Skipping parsing of the IE 453 return; 454 } 455 for (int i = 0; i < tbttInfoCount; i++) { 456 int mldId = ie.bytes[tbttInfoStartOffset + mldStartOffset 457 + MLD_ID_START_OFFSET] & Constants.BYTE_MASK; 458 if (mldId == 0) { 459 //This is an affiliated link 460 int linkId = ie.bytes[tbttInfoStartOffset + mldStartOffset 461 + LINK_ID_START_OFFSET] & LINK_ID_MASK; 462 MloLink link = new MloLink(); 463 link.setLinkId(linkId); 464 link.setBand(band); 465 link.setChannel(channel); 466 if (bssidOffset != -1) { 467 int macAddressStart = tbttInfoStartOffset + bssidOffset; 468 link.setApMacAddress(MacAddress.fromBytes( 469 Arrays.copyOfRange(ie.bytes, 470 macAddressStart, macAddressStart + 6))); 471 } 472 473 mAffiliatedMloLinks.add(link); 474 } 475 tbttInfoStartOffset += tbttInfoLen; 476 } 477 } 478 479 startOffset += TBTT_INFO_SET_START_OFFSET + (tbttInfoCount * tbttInfoLen); 480 } 481 482 // Done with parsing 483 mPresent = true; 484 } 485 } 486 487 public static class HtOperation { 488 private static final int HT_OPERATION_IE_LEN = 22; 489 private boolean mPresent = false; 490 private int mSecondChannelOffset = 0; 491 492 /** 493 * returns if HT Operation IE present in the message. 494 */ isPresent()495 public boolean isPresent() { 496 return mPresent; 497 } 498 499 /** 500 * Returns channel width if it is 20 or 40MHz 501 * Results will be invalid if channel width greater than 40MHz 502 * So caller should only call this method if VHT Operation IE is not present, 503 * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. 504 */ getChannelWidth()505 public int getChannelWidth() { 506 if (mSecondChannelOffset != 0) { 507 return ScanResult.CHANNEL_WIDTH_40MHZ; 508 } else { 509 return ScanResult.CHANNEL_WIDTH_20MHZ; 510 } 511 } 512 513 /** 514 * Returns channel Center frequency (for 20/40 MHz channels only) 515 * Results will be invalid for larger channel width, 516 * So, caller should only call this method if VHT Operation IE is not present, 517 * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. 518 */ getCenterFreq0(int primaryFrequency)519 public int getCenterFreq0(int primaryFrequency) { 520 if (mSecondChannelOffset != 0) { 521 //40 MHz 522 if (mSecondChannelOffset == 1) { 523 return primaryFrequency + 10; 524 } else if (mSecondChannelOffset == 3) { 525 return primaryFrequency - 10; 526 } else { 527 Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset); 528 return 0; 529 } 530 } else { 531 //20 MHz 532 return primaryFrequency; 533 } 534 } 535 536 /** 537 * Parse the HT Operation IE to read the fields of interest. 538 */ from(InformationElement ie)539 public void from(InformationElement ie) { 540 if (ie.id != InformationElement.EID_HT_OPERATION) { 541 throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id); 542 } 543 if (ie.bytes.length < HT_OPERATION_IE_LEN) { 544 throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length); 545 } 546 mPresent = true; 547 mSecondChannelOffset = ie.bytes[1] & 0x3; 548 } 549 } 550 551 public static class VhtOperation { 552 private static final int VHT_OPERATION_IE_LEN = 5; 553 private boolean mPresent = false; 554 private int mChannelMode = 0; 555 private int mCenterFreqIndex1 = 0; 556 private int mCenterFreqIndex2 = 0; 557 558 /** 559 * returns if VHT Operation IE present in the message. 560 */ isPresent()561 public boolean isPresent() { 562 return mPresent; 563 } 564 565 /** 566 * Returns channel width if it is above 40MHz, 567 * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that 568 * channel width should be obtained from the HT Operation IE via 569 * HtOperation.getChannelWidth(). 570 */ getChannelWidth()571 public int getChannelWidth() { 572 if (mChannelMode == 0) { 573 // 20 or 40MHz 574 return ScanResult.UNSPECIFIED; 575 } else if (mCenterFreqIndex2 == 0) { 576 // No secondary channel 577 return ScanResult.CHANNEL_WIDTH_80MHZ; 578 } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) { 579 // Primary and secondary channels adjacent 580 return ScanResult.CHANNEL_WIDTH_160MHZ; 581 } else { 582 // Primary and secondary channels not adjacent 583 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; 584 } 585 } 586 587 /** 588 * Returns center frequency of primary channel (if channel width greater than 40MHz), 589 * otherwise, it returns zero to indicate that center frequency should be obtained from 590 * the HT Operation IE via HtOperation.getCenterFreq0(). 591 */ getCenterFreq0()592 public int getCenterFreq0() { 593 if (mCenterFreqIndex1 == 0 || mChannelMode == 0) { 594 return 0; 595 } else { 596 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex1, 597 WifiScanner.WIFI_BAND_5_GHZ); 598 } 599 } 600 601 /** 602 * Returns center frequency of secondary channel if exists (channel width greater than 603 * 40MHz), otherwise, it returns zero. 604 * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz 605 * channels. 606 */ getCenterFreq1()607 public int getCenterFreq1() { 608 if (mCenterFreqIndex2 == 0 || mChannelMode == 0) { 609 return 0; 610 } else { 611 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex2, 612 WifiScanner.WIFI_BAND_5_GHZ); 613 } 614 } 615 616 /** 617 * Parse the VHT Operation IE to read the fields of interest. 618 */ from(InformationElement ie)619 public void from(InformationElement ie) { 620 if (ie.id != InformationElement.EID_VHT_OPERATION) { 621 throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id); 622 } 623 if (ie.bytes.length < VHT_OPERATION_IE_LEN) { 624 throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length); 625 } 626 mPresent = true; 627 mChannelMode = ie.bytes[0] & Constants.BYTE_MASK; 628 mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; 629 mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; 630 } 631 } 632 633 /** 634 * HeOperation: represents the HE Operation IE 635 */ 636 public static class HeOperation { 637 638 private static final int HE_OPERATION_BASIC_LENGTH = 6; 639 private static final int TWT_REQUIRED_MASK = 0x08; 640 private static final int VHT_OPERATION_INFO_PRESENT_MASK = 0x40; 641 private static final int HE_6GHZ_INFO_PRESENT_MASK = 0x02; 642 private static final int HE_6GHZ_CH_WIDTH_MASK = 0x03; 643 private static final int HE_6GHZ_REG_INFO_MASK = 0x38; 644 private static final int HE_6GHZ_REG_INFO_SHIFT = 3; 645 private static final int CO_HOSTED_BSS_PRESENT_MASK = 0x80; 646 private static final int VHT_OPERATION_INFO_START_INDEX = 6; 647 private static final int HE_BW_80_80_160 = 3; 648 649 private boolean mPresent = false; 650 private boolean mTwtRequired = false; 651 private boolean mVhtInfoPresent = false; 652 private boolean m6GhzInfoPresent = false; 653 private int mChannelWidth; 654 private ApType6GHz mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_UNKNOWN; 655 private int mPrimaryChannel; 656 private int mCenterFreqSeg0; 657 private int mCenterFreqSeg1; 658 private InformationElement mVhtInfo = null; 659 660 /** 661 * Returns whether the HE Information Element is present. 662 */ isPresent()663 public boolean isPresent() { 664 return mPresent; 665 } 666 667 /** 668 * Return whether the AP requires HE stations to participate either in individual TWT 669 * agreements or Broadcast TWT operation. Reference 9.4.2.249 HE Operation element (IEEE 670 * Std 802.11ax™-2021). 671 **/ isTwtRequired()672 public boolean isTwtRequired() { 673 return mTwtRequired; 674 } 675 676 /** 677 * Return 6Ghz AP type as defined in {@link ApType6GHz} 678 **/ getApType6GHz()679 public ApType6GHz getApType6GHz() { 680 return mApType6GHz; 681 } 682 /** 683 * Returns whether VHT Information field is present. 684 */ isVhtInfoPresent()685 public boolean isVhtInfoPresent() { 686 return mVhtInfoPresent; 687 } 688 689 /** 690 * Returns the VHT Information Element if it exists 691 * otherwise, return null. 692 */ getVhtInfoElement()693 public InformationElement getVhtInfoElement() { 694 return mVhtInfo; 695 } 696 697 /** 698 * Returns whether the 6GHz information field is present. 699 */ is6GhzInfoPresent()700 public boolean is6GhzInfoPresent() { 701 return m6GhzInfoPresent; 702 } 703 704 /** 705 * Returns the Channel BW 706 * Only applicable to 6GHz band 707 */ getChannelWidth()708 public int getChannelWidth() { 709 if (!m6GhzInfoPresent) { 710 return ScanResult.UNSPECIFIED; 711 } else if (mChannelWidth == 0) { 712 return ScanResult.CHANNEL_WIDTH_20MHZ; 713 } else if (mChannelWidth == 1) { 714 return ScanResult.CHANNEL_WIDTH_40MHZ; 715 } else if (mChannelWidth == 2) { 716 return ScanResult.CHANNEL_WIDTH_80MHZ; 717 } else if (Math.abs(mCenterFreqSeg1 - mCenterFreqSeg0) == 8) { 718 return ScanResult.CHANNEL_WIDTH_160MHZ; 719 } else { 720 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; 721 } 722 } 723 724 /** 725 * Returns the primary channel frequency 726 * Only applicable for 6GHz channels 727 */ getPrimaryFreq()728 public int getPrimaryFreq() { 729 return ScanResult.convertChannelToFrequencyMhzIfSupported(mPrimaryChannel, 730 WifiScanner.WIFI_BAND_6_GHZ); 731 } 732 733 /** 734 * Returns the center frequency for the primary channel 735 * Only applicable to 6GHz channels 736 */ getCenterFreq0()737 public int getCenterFreq0() { 738 if (m6GhzInfoPresent) { 739 if (mCenterFreqSeg0 == 0) { 740 return 0; 741 } else { 742 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg0, 743 WifiScanner.WIFI_BAND_6_GHZ); 744 } 745 } else { 746 return 0; 747 } 748 } 749 750 /** 751 * Returns the center frequency for the secondary channel 752 * Only applicable to 6GHz channels 753 */ getCenterFreq1()754 public int getCenterFreq1() { 755 if (m6GhzInfoPresent) { 756 if (mCenterFreqSeg1 == 0) { 757 return 0; 758 } else { 759 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg1, 760 WifiScanner.WIFI_BAND_6_GHZ); 761 } 762 } else { 763 return 0; 764 } 765 } 766 767 /** Parse HE Operation IE */ from(InformationElement ie)768 public void from(InformationElement ie) { 769 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 770 || ie.idExt != InformationElement.EID_EXT_HE_OPERATION) { 771 throw new IllegalArgumentException("Element id is not HE_OPERATION"); 772 } 773 774 // Make sure the byte array length is at least the fixed size 775 if (ie.bytes.length < HE_OPERATION_BASIC_LENGTH) { 776 if (DBG) { 777 Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length); 778 } 779 // Skipping parsing of the IE 780 return; 781 } 782 783 mTwtRequired = (ie.bytes[0] & TWT_REQUIRED_MASK) != 0; 784 mVhtInfoPresent = (ie.bytes[1] & VHT_OPERATION_INFO_PRESENT_MASK) != 0; 785 m6GhzInfoPresent = (ie.bytes[2] & HE_6GHZ_INFO_PRESENT_MASK) != 0; 786 boolean coHostedBssPresent = (ie.bytes[1] & CO_HOSTED_BSS_PRESENT_MASK) != 0; 787 int expectedLen = HE_OPERATION_BASIC_LENGTH + (mVhtInfoPresent ? 3 : 0) 788 + (coHostedBssPresent ? 1 : 0) + (m6GhzInfoPresent ? 5 : 0); 789 790 // Make sure the byte array length is at least fitting the known parameters 791 if (ie.bytes.length < expectedLen) { 792 if (DBG) { 793 Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length); 794 } 795 // Skipping parsing of the IE 796 return; 797 } 798 799 // Passed all checks, IE is ready for decoding 800 mPresent = true; 801 802 if (mVhtInfoPresent) { 803 mVhtInfo = new InformationElement(); 804 mVhtInfo.id = InformationElement.EID_VHT_OPERATION; 805 mVhtInfo.bytes = new byte[5]; 806 System.arraycopy(ie.bytes, VHT_OPERATION_INFO_START_INDEX, mVhtInfo.bytes, 0, 3); 807 } 808 809 if (m6GhzInfoPresent) { 810 int startIndx = VHT_OPERATION_INFO_START_INDEX + (mVhtInfoPresent ? 3 : 0) 811 + (coHostedBssPresent ? 1 : 0); 812 813 mChannelWidth = ie.bytes[startIndx + 1] & HE_6GHZ_CH_WIDTH_MASK; 814 int regInfo = (ie.bytes[startIndx + 1] & HE_6GHZ_REG_INFO_MASK) 815 >> HE_6GHZ_REG_INFO_SHIFT; 816 if (regInfo == 0) { 817 mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_INDOOR; 818 } else if (regInfo == 1) { 819 mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_STANDARD_POWER; 820 } 821 mPrimaryChannel = ie.bytes[startIndx] & Constants.BYTE_MASK; 822 mCenterFreqSeg0 = ie.bytes[startIndx + 2] & Constants.BYTE_MASK; 823 mCenterFreqSeg1 = ie.bytes[startIndx + 3] & Constants.BYTE_MASK; 824 } 825 } 826 } 827 828 /** 829 * EhtOperation: represents the EHT Operation IE 830 */ 831 public static class EhtOperation { 832 private static final int EHT_OPERATION_BASIC_LENGTH = 5; 833 private static final int EHT_OPERATION_INFO_PRESENT_MASK = 0x01; 834 private static final int DISABLED_SUBCHANNEL_BITMAP_PRESENT_MASK = 0x02; 835 private static final int EHT_OPERATION_INFO_START_INDEX = EHT_OPERATION_BASIC_LENGTH; 836 private static final int DISABLED_SUBCHANNEL_BITMAP_START_INDEX = 837 EHT_OPERATION_INFO_START_INDEX + 3; 838 private static final int CHANNEL_WIDTH_INDEX = EHT_OPERATION_INFO_START_INDEX + 0; 839 private static final int CHANNEL_WIDTH_MASK = 0xF; 840 private static final int CHANNEL_CENTER_FREQ_SEG0_INDEX = 841 EHT_OPERATION_INFO_START_INDEX + 1; 842 private static final int CHANNEL_CENTER_FREQ_SEG_MASK = 0xFF; 843 private static final int CHANNEL_CENTER_FREQ_SEG1_INDEX = 844 EHT_OPERATION_INFO_START_INDEX + 2; 845 846 private boolean mPresent = false; 847 private boolean mEhtOperationInfoPresent = false; 848 private boolean mDisabledSubchannelBitmapPresent = false; 849 private byte[] mDisabledSubchannelBitmap; 850 private int mChannelWidth; 851 private int mCenterFreqSeg0; 852 private int mCenterFreqSeg1; 853 854 /** 855 * Returns whether the EHT Information Element is present. 856 */ isPresent()857 public boolean isPresent() { 858 return mPresent; 859 } 860 861 /** 862 * Returns whether EHT Operation Information field is present. 863 * Reference 9.4.2.311 EHT Operation element (IEEEStd 802.11be™ Draft2.0). 864 */ isEhtOperationInfoPresent()865 public boolean isEhtOperationInfoPresent() { 866 return mEhtOperationInfoPresent; 867 } 868 869 /** 870 * Returns whether the Disabled Subchannel Bitmap field is present. 871 */ isDisabledSubchannelBitmapPresent()872 public boolean isDisabledSubchannelBitmapPresent() { 873 return mDisabledSubchannelBitmapPresent; 874 } 875 876 /** 877 * Returns the Disabled Subchannel Bitmap field if it exists. Otherwise, returns null. 878 */ getDisabledSubchannelBitmap()879 public byte[] getDisabledSubchannelBitmap() { 880 return mDisabledSubchannelBitmap; 881 } 882 883 /** 884 * @return Channel width if EHT Operation Information Present. 885 */ getChannelWidth()886 public int getChannelWidth() { 887 /* 888 * Channel width in EHT operation Info is set, 889 * 0 for 20 MHz EHT BSS bandwidth. 890 * 1 for 40 MHz EHT BSS bandwidth. 891 * 2 for 80 MHz EHT BSS bandwidth. 892 * 3 for 160 MHz EHT BSS bandwidth. 893 * 4 for 320 MHz EHT BSS bandwidth. 894 * Values in the ranges 5 to 7 are reserved. 895 */ 896 switch(mChannelWidth) { 897 case 0: return ScanResult.CHANNEL_WIDTH_20MHZ; 898 case 1: return ScanResult.CHANNEL_WIDTH_40MHZ; 899 case 2: return ScanResult.CHANNEL_WIDTH_80MHZ; 900 case 3: return ScanResult.CHANNEL_WIDTH_160MHZ; 901 case 4: return ScanResult.CHANNEL_WIDTH_320MHZ; 902 default: 903 return ScanResult.UNSPECIFIED; 904 } 905 } 906 907 /** 908 * Returns Channel Center Frequency Segment 0 (CCFS0). 909 * 910 * - For 20, 40 or 80 MHz BSS bandwidth, indicates the channel center frequency for the 911 * 20, 40 or 80 MHz channel on which the EHT BSS operates. 912 * - For 160 MHz BSS bandwidth, indicates the channel center frequency of the primary 80 913 * MHz channel. 914 * - For 320 MHz BSS bandwidth, indicates the channel center frequency of the primary 160 915 * MHz channel. 916 * 917 * @param band Operating band 918 * @return Center frequency. 919 */ getCenterFreq0(@canResult.WifiBand int band)920 public int getCenterFreq0(@ScanResult.WifiBand int band) { 921 if (mCenterFreqSeg0 == 0 || band == WifiBand.BAND_UNSPECIFIED) { 922 return ScanResult.UNSPECIFIED; 923 } 924 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg0, band); 925 } 926 927 /** 928 * Returns Channel Center Frequency Segment 1 (CCFS1) 929 * 930 * - For a 20, 40 or 80 MHz BSS bandwidth, returns {@link ScanResult#UNSPECIFIED} . 931 * - For a 160 MHz BSS bandwidth, returns the channel center frequency of the 160 MHz 932 * channel on which the EHT BSS operates. 933 * - For a 320 MHz BSS bandwidth, returns the channel center frequency of the 320 MHz 934 * channel on which the EHT BSS operates 935 * 936 * @param band Operating band 937 * @return Center frequency. 938 */ getCenterFreq1(@canResult.WifiBand int band)939 public int getCenterFreq1(@ScanResult.WifiBand int band) { 940 if (mCenterFreqSeg1 == 0 || band == WifiBand.BAND_UNSPECIFIED) { 941 return ScanResult.UNSPECIFIED; 942 } 943 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg1, band); 944 } 945 946 /** 947 * Parse EHT Operation IE 948 */ from(InformationElement ie)949 public void from(InformationElement ie) { 950 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 951 || ie.idExt != InformationElement.EID_EXT_EHT_OPERATION) { 952 throw new IllegalArgumentException("Element id is not EHT_OPERATION"); 953 } 954 // Make sure the byte array length is at least the fixed size 955 if (ie.bytes.length < EHT_OPERATION_BASIC_LENGTH) { 956 if (DBG) { 957 Log.w(TAG, "Invalid EHT_OPERATION IE len: " + ie.bytes.length); 958 } 959 // Skipping parsing of the IE 960 return; 961 } 962 963 mEhtOperationInfoPresent = (ie.bytes[0] & EHT_OPERATION_INFO_PRESENT_MASK) != 0; 964 mDisabledSubchannelBitmapPresent = 965 (ie.bytes[0] & DISABLED_SUBCHANNEL_BITMAP_PRESENT_MASK) != 0; 966 int expectedLen = EHT_OPERATION_BASIC_LENGTH + (mEhtOperationInfoPresent ? ( 967 mDisabledSubchannelBitmapPresent ? 5 : 3) : 0); 968 // Make sure the byte array length is at least fitting the known parameters 969 if (ie.bytes.length < expectedLen) { 970 if (DBG) { 971 Log.w(TAG, "Invalid EHT_OPERATION info len: " + ie.bytes.length); 972 } 973 // Skipping parsing of the IE 974 return; 975 } 976 mPresent = true; 977 978 if (mEhtOperationInfoPresent) { 979 mChannelWidth = ie.bytes[CHANNEL_WIDTH_INDEX] & CHANNEL_WIDTH_MASK; 980 mCenterFreqSeg0 = 981 ie.bytes[CHANNEL_CENTER_FREQ_SEG0_INDEX] & CHANNEL_CENTER_FREQ_SEG_MASK; 982 mCenterFreqSeg1 = 983 ie.bytes[CHANNEL_CENTER_FREQ_SEG1_INDEX] & CHANNEL_CENTER_FREQ_SEG_MASK; 984 } 985 986 if (mDisabledSubchannelBitmapPresent) { 987 mDisabledSubchannelBitmap = new byte[2]; 988 System.arraycopy(ie.bytes, DISABLED_SUBCHANNEL_BITMAP_START_INDEX, 989 mDisabledSubchannelBitmap, 0, 2); 990 } 991 992 //TODO put more functionality for parsing the IE 993 } 994 } 995 996 /** 997 * HtCapabilities: represents the HT Capabilities IE 998 */ 999 public static class HtCapabilities { 1000 private int mMaxNumberSpatialStreams = 1; 1001 private boolean mPresent = false; 1002 /** Returns whether HT Capabilities IE is present */ isPresent()1003 public boolean isPresent() { 1004 return mPresent; 1005 } 1006 /** 1007 * Returns max number of spatial streams if HT Capabilities IE is found and parsed, 1008 * or 1 otherwise 1009 */ getMaxNumberSpatialStreams()1010 public int getMaxNumberSpatialStreams() { 1011 return mMaxNumberSpatialStreams; 1012 } 1013 1014 /** Parse HT Capabilities IE */ from(InformationElement ie)1015 public void from(InformationElement ie) { 1016 if (ie.id != InformationElement.EID_HT_CAPABILITIES) { 1017 throw new IllegalArgumentException("Element id is not HT_CAPABILITIES: " + ie.id); 1018 } 1019 if (ie.bytes.length < 26) { 1020 if (DBG) { 1021 Log.w(TAG, "Invalid HtCapabilities len: " + ie.bytes.length); 1022 } 1023 return; 1024 } 1025 int stream1 = ie.bytes[3] & Constants.BYTE_MASK; 1026 int stream2 = ie.bytes[4] & Constants.BYTE_MASK; 1027 int stream3 = ie.bytes[5] & Constants.BYTE_MASK; 1028 int stream4 = ie.bytes[6] & Constants.BYTE_MASK; 1029 if (DBG) { 1030 Log.d(TAG, "HT Rx MCS set4: " + Integer.toHexString(stream4)); 1031 Log.d(TAG, "HT Rx MCS set3: " + Integer.toHexString(stream3)); 1032 Log.d(TAG, "HT Rx MCS set2: " + Integer.toHexString(stream2)); 1033 Log.d(TAG, "HT Rx MCS set1: " + Integer.toHexString(stream1)); 1034 } 1035 mMaxNumberSpatialStreams = (stream4 > 0) ? 4 : 1036 ((stream3 > 0) ? 3 : 1037 ((stream2 > 0) ? 2 : 1)); 1038 mPresent = true; 1039 } 1040 } 1041 1042 /** 1043 * VhtCapabilities: represents the VHT Capabilities IE 1044 */ 1045 public static class VhtCapabilities { 1046 private int mMaxNumberSpatialStreams = 1; 1047 private boolean mPresent = false; 1048 /** Returns whether VHT Capabilities IE is present */ isPresent()1049 public boolean isPresent() { 1050 return mPresent; 1051 } 1052 /** 1053 * Returns max number of spatial streams if VHT Capabilities IE is found and parsed, 1054 * or 1 otherwise 1055 */ getMaxNumberSpatialStreams()1056 public int getMaxNumberSpatialStreams() { 1057 return mMaxNumberSpatialStreams; 1058 } 1059 /** Parse VHT Capabilities IE */ from(InformationElement ie)1060 public void from(InformationElement ie) { 1061 if (ie.id != InformationElement.EID_VHT_CAPABILITIES) { 1062 throw new IllegalArgumentException("Element id is not VHT_CAPABILITIES: " + ie.id); 1063 } 1064 if (ie.bytes.length < 12) { 1065 if (DBG) { 1066 Log.w(TAG, "Invalid VHT_CAPABILITIES len: " + ie.bytes.length); 1067 } 1068 return; 1069 } 1070 int mcsMap = ((ie.bytes[5] & Constants.BYTE_MASK) << 8) 1071 + (ie.bytes[4] & Constants.BYTE_MASK); 1072 mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap); 1073 mPresent = true; 1074 } 1075 } 1076 1077 /** 1078 * HeCapabilities: represents the HE Capabilities IE 1079 */ 1080 public static class HeCapabilities { 1081 1082 /** 1083 * Represents HE MAC Capabilities Information field. Reference 9.4.2.248.2 HE MAC 1084 * Capabilities Information field (IEEE Std 802.11ax-2021). 1085 */ 1086 private static class HeMacCapabilitiesInformation { 1087 public static int startOffset = 0; 1088 public static int endOffset = 5; 1089 public static final int TWT_REQUESTER_SUPPORT_BIT = 1; 1090 public static final int TWT_RESPONDER_SUPPORT_BIT = 2; 1091 public static final int BROADCAST_TWT_SUPPORT_BIT = 20; 1092 public boolean isTwtRequesterSupported = false; 1093 public boolean isTwtResponderSupported = false; 1094 public boolean isBroadcastTwtSupported = false; 1095 private BitSet mBitSet = new BitSet(); 1096 1097 /** Parse HE MAC capabilities Information from the byte array. */ from(byte[] bytes)1098 public void from(byte[] bytes) { 1099 mBitSet = BitSet.valueOf(bytes); 1100 isTwtRequesterSupported = mBitSet.get(TWT_REQUESTER_SUPPORT_BIT); 1101 isTwtResponderSupported = mBitSet.get(TWT_RESPONDER_SUPPORT_BIT); 1102 isBroadcastTwtSupported = mBitSet.get(BROADCAST_TWT_SUPPORT_BIT); 1103 } 1104 } 1105 1106 private HeMacCapabilitiesInformation mHeMacCapabilitiesInformation = 1107 new HeMacCapabilitiesInformation(); 1108 private int mMaxNumberSpatialStreams = 1; 1109 private boolean mPresent = false; 1110 1111 /** 1112 * Return whether TWT requester is supported. Set by HE Stations to indicate TWT support 1113 */ isTwtRequesterSupported()1114 public boolean isTwtRequesterSupported() { 1115 return mHeMacCapabilitiesInformation.isTwtRequesterSupported; 1116 } 1117 /** Return whether TWT responder is supported. Set by HE AP to indicate TWT support. */ isTwtResponderSupported()1118 public boolean isTwtResponderSupported() { 1119 return mHeMacCapabilitiesInformation.isTwtResponderSupported; 1120 } 1121 1122 /** Return whether broadcast TWT is supported */ isBroadcastTwtSupported()1123 public boolean isBroadcastTwtSupported() { 1124 return mHeMacCapabilitiesInformation.isBroadcastTwtSupported; 1125 } 1126 1127 /** Returns whether HE Capabilities IE is present */ isPresent()1128 public boolean isPresent() { 1129 return mPresent; 1130 } 1131 /** 1132 * Returns max number of spatial streams if HE Capabilities IE is found and parsed, 1133 * or 1 otherwise 1134 */ getMaxNumberSpatialStreams()1135 public int getMaxNumberSpatialStreams() { 1136 return mMaxNumberSpatialStreams; 1137 } 1138 /** Parse HE Capabilities IE */ from(InformationElement ie)1139 public void from(InformationElement ie) { 1140 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 1141 || ie.idExt != InformationElement.EID_EXT_HE_CAPABILITIES) { 1142 throw new IllegalArgumentException("Element id is not HE_CAPABILITIES: " + ie.id); 1143 } 1144 if (ie.bytes.length < 21) { 1145 if (DBG) { 1146 Log.w(TAG, "Invalid HE_CAPABILITIES len: " + ie.bytes.length); 1147 } 1148 return; 1149 } 1150 int mcsMap = ((ie.bytes[18] & Constants.BYTE_MASK) << 8) 1151 + (ie.bytes[17] & Constants.BYTE_MASK); 1152 mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap); 1153 mPresent = true; 1154 mHeMacCapabilitiesInformation.from(Arrays.copyOfRange(ie.bytes, 1155 mHeMacCapabilitiesInformation.startOffset, 1156 mHeMacCapabilitiesInformation.endOffset)); 1157 } 1158 } 1159 1160 /** 1161 * EhtCapabilities: represents the EHT Capabilities IE. Reference 9.4.2.313 EHT Capabilities 1162 * element (IEEE P802.11be/D3.1). 1163 */ 1164 public static class EhtCapabilities { 1165 /** 1166 * EhtMacCapabilitiesInformation: represents the EHT MAC Capabilities Information element. 1167 * Reference 9.4.2.313.2 EHT MAC Capabilities Information field (IEEE P802.11be/D3.1). 1168 */ 1169 public static class EhtMacCapabilitiesInformation { 1170 public static int startOffset = 0; 1171 public static int endOffset = 1; 1172 public static final int EPCS_PRIORITY_ACCESS_SUPPORT_BIT = 0; 1173 public static final int RESTRICTED_TWT_SUPPORT_BIT = 4; 1174 public boolean isEpcsPriorityAccessSupported = false; 1175 public boolean isRestrictedTwtSupported = false; 1176 private BitSet mBitSet = new BitSet(); 1177 1178 /** Parse EHT MAC Capabilities Information from the bytes. **/ from(byte[] bytes)1179 public void from(byte[] bytes) { 1180 mBitSet = BitSet.valueOf(bytes); 1181 isEpcsPriorityAccessSupported = mBitSet.get(EPCS_PRIORITY_ACCESS_SUPPORT_BIT); 1182 isRestrictedTwtSupported = mBitSet.get(RESTRICTED_TWT_SUPPORT_BIT); 1183 } 1184 } 1185 private boolean mPresent = false; 1186 1187 private EhtMacCapabilitiesInformation mEhtMacCapabilitiesInformation = 1188 new EhtMacCapabilitiesInformation(); 1189 /** Returns whether HE Capabilities IE is present. */ isPresent()1190 public boolean isPresent() { 1191 return mPresent; 1192 } 1193 1194 /** 1195 * Returns whether restricted TWT is supported or not. It enables enhanced medium access 1196 * protection and resource reservation mechanisms for delivery of latency sensitive 1197 * traffic. 1198 */ isRestrictedTwtSupported()1199 public boolean isRestrictedTwtSupported() { 1200 return mEhtMacCapabilitiesInformation.isRestrictedTwtSupported; 1201 } 1202 1203 /** 1204 * Returns whether EPCS priority access supported or not. EPCS priority access is a 1205 * mechanism that provides prioritized access to the wireless medium for authorized users to 1206 * increase their probability of successful communication during periods of network 1207 * congestion. 1208 */ isEpcsPriorityAccessSupported()1209 public boolean isEpcsPriorityAccessSupported() { 1210 return mEhtMacCapabilitiesInformation.isEpcsPriorityAccessSupported; 1211 } 1212 1213 /** Parse EHT Capabilities IE */ from(InformationElement ie)1214 public void from(InformationElement ie) { 1215 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 1216 || ie.idExt != InformationElement.EID_EXT_EHT_CAPABILITIES) { 1217 throw new IllegalArgumentException("Element id is not EHT_CAPABILITIES: " + ie.id); 1218 } 1219 mPresent = true; 1220 mEhtMacCapabilitiesInformation.from(Arrays.copyOfRange(ie.bytes, 1221 mEhtMacCapabilitiesInformation.startOffset, 1222 mEhtMacCapabilitiesInformation.endOffset)); 1223 } 1224 } 1225 1226 /** 1227 * MultiLink: represents the Multi-Link IE 1228 * as described in IEEE 802.11be Specification Section 9.4.2.312 1229 */ 1230 public static class MultiLink { 1231 private static final int CONTROL_FIELD_LEN = 2; 1232 private static final int BASIC_COMMON_INFO_FIELD_MIN_LEN = 7; 1233 private static final int BASIC_LINK_INFO_FIELD_MIN_LEN = 0; 1234 private static final int BASIC_IE_MIN_LEN = CONTROL_FIELD_LEN 1235 + BASIC_COMMON_INFO_FIELD_MIN_LEN 1236 + BASIC_LINK_INFO_FIELD_MIN_LEN; 1237 1238 // Control field constants 1239 private static final int IE_TYPE_OFFSET = 0; 1240 private static final int IE_TYPE_MASK = 0x07; 1241 public static final int TYPE_BASIC = 0; 1242 public static final int LINK_ID_PRESENT_OFFSET = 0; 1243 public static final int LINK_ID_PRESENT_MASK = 0x10; 1244 1245 1246 // Common info field constants 1247 private static final int COMMON_FIELD_START_INDEX = CONTROL_FIELD_LEN; 1248 private static final int BASIC_IE_COMMON_INFO_LEN_OFFSET = 0; 1249 private static final int BASIC_IE_COMMON_MLD_MAC_ADDRESS_OFFSET = 1; 1250 private static final int BASIC_IE_COMMOM_LINK_ID_OFFSET = 7; 1251 private static final int BASIC_IE_COMMOM_LINK_ID_MASK = 0x0F; 1252 1253 // Per-STA sub-element constants 1254 private static final int PER_STA_SUB_ELEMENT_ID = 0; 1255 private static final int PER_STA_SUB_ELEMENT_MIN_LEN = 5; 1256 private static final int PER_STA_SUB_ELEMENT_LINK_ID_OFFSET = 2; 1257 private static final int PER_STA_SUB_ELEMENT_LINK_ID_MASK = 0x0F; 1258 private static final int PER_STA_SUB_ELEMENT_STA_INFO_OFFSET = 4; 1259 private static final int PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET = 2; 1260 private static final int PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK = 0x20; 1261 private static final int PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET = 1; 1262 1263 private boolean mPresent = false; 1264 private int mLinkId = MloLink.INVALID_MLO_LINK_ID; 1265 private MacAddress mMldMacAddress = null; 1266 private List<MloLink> mAffiliatedLinks = new ArrayList<>(); 1267 1268 /** Returns whether Multi-Link IE is present */ isPresent()1269 public boolean isPresent() { 1270 return mPresent; 1271 } 1272 1273 /** Returns the MLD MAC Address */ getMldMacAddress()1274 public MacAddress getMldMacAddress() { 1275 return mMldMacAddress; 1276 } 1277 1278 /** Return the link id */ getLinkId()1279 public int getLinkId() { 1280 return mLinkId; 1281 } 1282 1283 /** Return the affiliated links */ getAffiliatedLinks()1284 public List<MloLink> getAffiliatedLinks() { 1285 return new ArrayList<MloLink>(mAffiliatedLinks); 1286 } 1287 1288 /** 1289 * Parse Common Info field in Multi-Link Operation IE 1290 * 1291 * Common Info filed as described in IEEE 802.11 specs, Section 9.4.2.312, 1292 * 1293 * | Len | MLD Address | Link Id | BSS Change count | MedSync | EML Cap | MLD Cap | 1294 * Octets: 1 6 0 or 1 0 or 1 0 or 2 0 or 2 0 or 2 1295 * 1296 */ parseCommonInfoField(InformationElement ie)1297 private int parseCommonInfoField(InformationElement ie) { 1298 int commonInfoLength = ie.bytes[COMMON_FIELD_START_INDEX 1299 + BASIC_IE_COMMON_INFO_LEN_OFFSET] & Constants.BYTE_MASK; 1300 if (commonInfoLength < BASIC_COMMON_INFO_FIELD_MIN_LEN) { 1301 if (DBG) { 1302 Log.w(TAG, "Invalid Common Info field length: " + commonInfoLength); 1303 } 1304 // Skipping parsing of the IE 1305 return 0; 1306 } 1307 1308 boolean isLinkIdInfoPresent = (ie.bytes[LINK_ID_PRESENT_OFFSET] 1309 & LINK_ID_PRESENT_MASK) != 0; 1310 if (isLinkIdInfoPresent) { 1311 if (ie.bytes.length < BASIC_IE_MIN_LEN + 1 /*Link Id info */) { 1312 if (DBG) { 1313 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1314 } 1315 // Skipping parsing of the IE 1316 return 0; 1317 } 1318 1319 mLinkId = ie.bytes[COMMON_FIELD_START_INDEX 1320 + BASIC_IE_COMMOM_LINK_ID_OFFSET] & BASIC_IE_COMMOM_LINK_ID_MASK; 1321 } 1322 1323 int macAddressStart = COMMON_FIELD_START_INDEX + BASIC_IE_COMMON_MLD_MAC_ADDRESS_OFFSET; 1324 mMldMacAddress = MacAddress.fromBytes( 1325 Arrays.copyOfRange(ie.bytes, macAddressStart, macAddressStart + 6)); 1326 1327 return commonInfoLength; 1328 } 1329 1330 /** Parse per STA sub element (not fragmented) of Multi link element. */ parsePerStaSubElement(byte[] bytes, int start, int len)1331 private Boolean parsePerStaSubElement(byte[] bytes, int start, int len) { 1332 MloLink link = new MloLink(); 1333 link.setLinkId( 1334 bytes[start + PER_STA_SUB_ELEMENT_LINK_ID_OFFSET] 1335 & PER_STA_SUB_ELEMENT_LINK_ID_MASK); 1336 1337 int staInfoLength = 1338 bytes[start + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET] & Constants.BYTE_MASK; 1339 if (len < PER_STA_SUB_ELEMENT_STA_INFO_OFFSET + staInfoLength) { 1340 if (DBG) { 1341 Log.w(TAG, "Invalid sta info length: " + staInfoLength); 1342 } 1343 // Skipping parsing of the IE 1344 return false; 1345 } 1346 1347 // Check if MAC Address is present 1348 if ((bytes[start + PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET] 1349 & PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK) 1350 != 0) { 1351 if (staInfoLength < 1 /*length*/ + 6 /*mac address*/) { 1352 if (DBG) { 1353 Log.w(TAG, "Invalid sta info length: " + staInfoLength); 1354 } 1355 // Skipping parsing of the IE 1356 return false; 1357 } 1358 1359 int macAddressOffset = 1360 start 1361 + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET 1362 + PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET; 1363 link.setApMacAddress( 1364 MacAddress.fromBytes( 1365 Arrays.copyOfRange(bytes, macAddressOffset, macAddressOffset + 6))); 1366 } 1367 mAffiliatedLinks.add(link); 1368 return true; 1369 } 1370 1371 /** 1372 * Parse Link Info field in Multi-Link Operation IE 1373 * 1374 * Link Info filed as described in IEEE 802.11 specs, Section 9.4.2.312, 1375 * 1376 * | ID | Len | STA Control | STA Info | STA Profile | 1377 * Octets: 1 1 2 variable variable 1378 * 1379 * where STA Control subfield is described as, 1380 * 1381 * | LinkId | Complete | MAC | Beacon Interval | DTIM | NSTR Link | NSTR Bitmap | R | 1382 * Bits: 4 1 1 1 1 1 1 6 1383 * 1384 */ parseLinkInfoField(InformationElement ie, int startOffset)1385 private boolean parseLinkInfoField(InformationElement ie, int startOffset) { 1386 // Check if Link Info field is present 1387 while (ie.bytes.length >= startOffset + PER_STA_SUB_ELEMENT_MIN_LEN) { 1388 int subElementId = ie.bytes[startOffset] & Constants.BYTE_MASK; 1389 int subElementLen = ie.bytes[startOffset + 1] & Constants.BYTE_MASK; 1390 // Expectation here is IE has enough length to parse and non-zero sub-element 1391 // length. 1392 if (ie.bytes.length < startOffset + subElementLen || subElementLen == 0) { 1393 if (DBG) { 1394 Log.w(TAG, "Invalid sub-element length: " + subElementLen); 1395 } 1396 // Skipping parsing of the IE 1397 return false; 1398 } 1399 if (subElementId != PER_STA_SUB_ELEMENT_ID) { 1400 // Skip this subelement, could be an unsupported one 1401 startOffset += subElementLen; 1402 continue; 1403 } 1404 1405 int bytesRead; 1406 // Check for fragmentation before parsing per sta profile sub element 1407 if (subElementLen == DefragmentElement.FRAG_MAX_LEN) { 1408 DefragmentElement defragment = 1409 new DefragmentElement( 1410 ie.bytes, 1411 startOffset, 1412 PER_STA_SUB_ELEMENT_ID, 1413 InformationElement.EID_FRAGMENT_SUB_ELEMENT_MULTI_LINK); 1414 bytesRead = defragment.bytesRead; 1415 if (defragment.bytesRead == 0 || defragment.bytes == null) { 1416 return false; 1417 } 1418 if (!parsePerStaSubElement(defragment.bytes, 0, defragment.bytes.length)) { 1419 return false; 1420 } 1421 } else { 1422 bytesRead = subElementLen; 1423 if (!parsePerStaSubElement(ie.bytes, startOffset, subElementLen)) { 1424 return false; 1425 } 1426 } 1427 // Done with this sub-element 1428 startOffset += bytesRead; 1429 } 1430 1431 return true; 1432 } 1433 1434 /** 1435 * Parse Multi-Link Operation IE 1436 * 1437 * Multi-Link IE format as described in IEEE 802.11 specs, Section 9.4.2.312 1438 * 1439 * | ElementID | Length | ExtendedID | Control | Common Info | Link Info | 1440 * Octets: 1 1 1 2 Variable variable 1441 * 1442 * 1443 * Where Control field is described as, 1444 * 1445 * | Type | Reserved | Presence Bitmap | 1446 * Bits: 3 1 12 1447 * 1448 * Where the Presence Bitmap subfield is described as, 1449 * 1450 * | LinkId | BSS change count | MedSync | EML cap | MLD cap | Reserved | 1451 * Bits: 1 1 1 1 1 7 1452 * 1453 * 1454 * 1455 * Note: InformationElement.bytes has 'Element ID', 'Length', and 'Extended ID' 1456 * stripped off already 1457 * 1458 */ from(InformationElement ie)1459 public void from(InformationElement ie) { 1460 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 1461 || ie.idExt != InformationElement.EID_EXT_MULTI_LINK) { 1462 throw new IllegalArgumentException("Element id is not Multi-Link: " + ie.id); 1463 } 1464 1465 // Make sure the byte array length is at least the Control field size 1466 if (ie.bytes.length < CONTROL_FIELD_LEN) { 1467 if (DBG) { 1468 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1469 } 1470 // Skipping parsing of the IE 1471 return; 1472 } 1473 1474 // Check on IE type 1475 // Note only the BASIC type is allowed to be received from AP 1476 int type = ie.bytes[IE_TYPE_OFFSET] & IE_TYPE_MASK; 1477 if (type != TYPE_BASIC) { 1478 if (DBG) { 1479 Log.w(TAG, "Invalid/Unsupported Multi-Link IE type: " + type); 1480 } 1481 // Skipping parsing of the IE 1482 return; 1483 } 1484 1485 // Check length 1486 if (ie.bytes.length < BASIC_IE_MIN_LEN) { 1487 if (DBG) { 1488 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1489 } 1490 // Skipping parsing of the IE 1491 return; 1492 } 1493 1494 int commonInfoLength = parseCommonInfoField(ie); 1495 if (commonInfoLength == 0) { 1496 return; 1497 } 1498 1499 if (!parseLinkInfoField(ie, CONTROL_FIELD_LEN + commonInfoLength)) { 1500 return; 1501 } 1502 1503 mPresent = true; 1504 } 1505 } 1506 parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap)1507 private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) { 1508 int maxNumberSpatialStreams = 1; 1509 for (int i = 8; i >= 1; --i) { 1510 int streamMap = mcsMapToStreamMap(mcsMap, i); 1511 // 3 means unsupported 1512 if (streamMap != 3) { 1513 maxNumberSpatialStreams = i; 1514 break; 1515 } 1516 } 1517 if (DBG) { 1518 for (int i = 8; i >= 1; --i) { 1519 int streamMap = mcsMapToStreamMap(mcsMap, i); 1520 Log.d(TAG, "Rx MCS set " + i + " : " + streamMap); 1521 } 1522 } 1523 return maxNumberSpatialStreams; 1524 } 1525 mcsMapToStreamMap(int mcsMap, int i)1526 private static int mcsMapToStreamMap(int mcsMap, int i) { 1527 return (mcsMap >> ((i - 1) * 2)) & 0x3; 1528 } 1529 1530 public static class Interworking { 1531 public NetworkDetail.Ant ant = null; 1532 public boolean internet = false; 1533 public long hessid = 0L; 1534 from(InformationElement ie)1535 public void from(InformationElement ie) { 1536 if (ie.id != InformationElement.EID_INTERWORKING) { 1537 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id); 1538 } 1539 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1540 int anOptions = data.get() & Constants.BYTE_MASK; 1541 ant = NetworkDetail.Ant.values()[anOptions & 0x0f]; 1542 internet = (anOptions & 0x10) != 0; 1543 // There are only three possible lengths for the Interworking IE: 1544 // Len 1: Access Network Options only 1545 // Len 3: Access Network Options & Venue Info 1546 // Len 7: Access Network Options & HESSID 1547 // Len 9: Access Network Options, Venue Info, & HESSID 1548 if (ie.bytes.length != 1 1549 && ie.bytes.length != 3 1550 && ie.bytes.length != 7 1551 && ie.bytes.length != 9) { 1552 throw new IllegalArgumentException( 1553 "Bad Interworking element length: " + ie.bytes.length); 1554 } 1555 1556 if (ie.bytes.length == 3 || ie.bytes.length == 9) { 1557 int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2); 1558 } 1559 1560 if (ie.bytes.length == 7 || ie.bytes.length == 9) { 1561 hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6); 1562 } 1563 } 1564 } 1565 1566 public static class RoamingConsortium { 1567 public int anqpOICount = 0; 1568 1569 private long[] roamingConsortiums = null; 1570 getRoamingConsortiums()1571 public long[] getRoamingConsortiums() { 1572 return roamingConsortiums; 1573 } 1574 from(InformationElement ie)1575 public void from(InformationElement ie) { 1576 if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) { 1577 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " 1578 + ie.id); 1579 } 1580 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1581 anqpOICount = data.get() & Constants.BYTE_MASK; 1582 1583 int oi12Length = data.get() & Constants.BYTE_MASK; 1584 int oi1Length = oi12Length & Constants.NIBBLE_MASK; 1585 int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK; 1586 int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length; 1587 int oiCount = 0; 1588 if (oi1Length > 0) { 1589 oiCount++; 1590 if (oi2Length > 0) { 1591 oiCount++; 1592 if (oi3Length > 0) { 1593 oiCount++; 1594 } 1595 } 1596 } 1597 roamingConsortiums = new long[oiCount]; 1598 if (oi1Length > 0 && roamingConsortiums.length > 0) { 1599 roamingConsortiums[0] = 1600 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); 1601 } 1602 if (oi2Length > 0 && roamingConsortiums.length > 1) { 1603 roamingConsortiums[1] = 1604 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); 1605 } 1606 if (oi3Length > 0 && roamingConsortiums.length > 2) { 1607 roamingConsortiums[2] = 1608 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); 1609 } 1610 } 1611 1612 @Override toString()1613 public String toString() { 1614 StringBuilder stringBuilder = new StringBuilder(); 1615 stringBuilder.append("RoamingConsortium ["); 1616 stringBuilder.append("anqpOICount: " + anqpOICount); 1617 stringBuilder.append(", roamingConsortiums: " + (roamingConsortiums == null ? "null" 1618 : Arrays.toString(roamingConsortiums))); 1619 stringBuilder.append("]"); 1620 return stringBuilder.toString(); 1621 } 1622 } 1623 1624 public static class Vsa { 1625 private static final int ANQP_DOMAIN_ID_PRESENT_BIT = 0x04; 1626 private static final int ANQP_PPS_MO_ID_BIT = 0x02; 1627 private static final int OUI_WFA_ALLIANCE = 0x506F9a; 1628 private static final int OUI_TYPE_HS20 = 0x10; 1629 private static final int OUI_TYPE_MBO_OCE = 0x16; 1630 1631 public NetworkDetail.HSRelease hsRelease = null; 1632 public int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP. 1633 1634 public boolean IsMboCapable = false; 1635 public boolean IsMboApCellularDataAware = false; 1636 public boolean IsOceCapable = false; 1637 public int mboAssociationDisallowedReasonCode = 1638 MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT; 1639 public byte[] oui; 1640 parseVsaMboOce(InformationElement ie)1641 private void parseVsaMboOce(InformationElement ie) { 1642 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1643 1644 // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying 1645 // MBO-OCE OUI type. 1646 data.getInt(); 1647 1648 while (data.remaining() > 1) { 1649 int attrId = data.get() & Constants.BYTE_MASK; 1650 int attrLen = data.get() & Constants.BYTE_MASK; 1651 1652 if ((attrLen == 0) || (attrLen > data.remaining())) { 1653 return; 1654 } 1655 byte[] attrBytes = new byte[attrLen]; 1656 data.get(attrBytes); 1657 switch (attrId) { 1658 case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION: 1659 IsMboCapable = true; 1660 IsMboApCellularDataAware = (attrBytes[0] 1661 & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0; 1662 break; 1663 case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED: 1664 mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK; 1665 break; 1666 case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION: 1667 IsOceCapable = true; 1668 break; 1669 default: 1670 break; 1671 } 1672 } 1673 if (DBG) { 1674 Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: " 1675 + IsMboApCellularDataAware + " AssocDisAllowRC: " 1676 + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable); 1677 } 1678 } 1679 parseVsaHs20(InformationElement ie)1680 private void parseVsaHs20(InformationElement ie) { 1681 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1682 if (ie.bytes.length >= 5) { 1683 // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying 1684 // HS20 OUI type. 1685 data.getInt(); 1686 1687 int hsConf = data.get() & Constants.BYTE_MASK; 1688 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) { 1689 case 0: 1690 hsRelease = NetworkDetail.HSRelease.R1; 1691 break; 1692 case 1: 1693 hsRelease = NetworkDetail.HSRelease.R2; 1694 break; 1695 case 2: 1696 hsRelease = NetworkDetail.HSRelease.R3; 1697 break; 1698 default: 1699 hsRelease = NetworkDetail.HSRelease.Unknown; 1700 break; 1701 } 1702 if ((hsConf & ANQP_DOMAIN_ID_PRESENT_BIT) != 0) { 1703 // According to Hotspot 2.0 Specification v3.0 section 3.1.1 HS2.0 Indication 1704 // element, the size of the element is 5 bytes, and 2 bytes are optionally added 1705 // for each optional field; ANQP PPS MO ID and ANQP Domain ID present. 1706 int expectedSize = 7; 1707 if ((hsConf & ANQP_PPS_MO_ID_BIT) != 0) { 1708 expectedSize += 2; 1709 if (ie.bytes.length < expectedSize) { 1710 throw new IllegalArgumentException( 1711 "HS20 indication element too short: " + ie.bytes.length); 1712 } 1713 data.getShort(); // Skip 2 bytes 1714 } 1715 if (ie.bytes.length < expectedSize) { 1716 throw new IllegalArgumentException( 1717 "HS20 indication element too short: " + ie.bytes.length); 1718 } 1719 anqpDomainID = data.getShort() & Constants.SHORT_MASK; 1720 } 1721 } 1722 } 1723 1724 /** 1725 * Parse the vendor specific information element to build 1726 * InformationElemmentUtil.vsa object. 1727 * 1728 * @param ie -- Information Element 1729 */ from(InformationElement ie)1730 public void from(InformationElement ie) { 1731 if (ie.bytes.length < 3) { 1732 if (DBG) { 1733 Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length); 1734 } 1735 return; 1736 } 1737 1738 oui = Arrays.copyOfRange(ie.bytes, 0, 3); 1739 int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16) 1740 | ((ie.bytes[1] & Constants.BYTE_MASK) << 8) 1741 | ((ie.bytes[2] & Constants.BYTE_MASK))); 1742 1743 if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) { 1744 int ouiType = ie.bytes[3]; 1745 switch (ouiType) { 1746 case OUI_TYPE_HS20: 1747 parseVsaHs20(ie); 1748 break; 1749 case OUI_TYPE_MBO_OCE: 1750 parseVsaMboOce(ie); 1751 break; 1752 default: 1753 break; 1754 } 1755 } 1756 } 1757 } 1758 1759 /** 1760 * This IE contained a bit field indicating the capabilities being advertised by the STA. 1761 * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE. 1762 * 1763 * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each 1764 * bit. 1765 * 1766 * Here is the wire format of this IE: 1767 * | Element ID | Length | Capabilities | 1768 * 1 1 n 1769 */ 1770 public static class ExtendedCapabilities { 1771 private static final int RTT_RESP_ENABLE_BIT = 70; 1772 private static final int SSID_UTF8_BIT = 48; 1773 private static final int FILS_CAPABILITY_BIT = 72; 1774 private static final int TWT_REQUESTER_CAPABILITY_BIT = 77; 1775 private static final int TWT_RESPONDER_CAPABILITY_BIT = 78; 1776 private static final int NON_TB_RANGING_RESPONDER = 90; 1777 private static final int TB_RANGING_RESPONDER = 91; 1778 1779 public BitSet capabilitiesBitSet; 1780 1781 /** 1782 * @return true if Trigger based ranging responder supported. Refer P802.11az/D7.0, 1783 * September 2022, section 9.4.2.26 Extended Capabilities element. 1784 */ is80211azTbResponder()1785 public boolean is80211azTbResponder() { 1786 return capabilitiesBitSet.get(TB_RANGING_RESPONDER); 1787 } 1788 1789 /** 1790 * @return true if Non trigger based ranging responder supported. Refer P802.11az/D7.0, 1791 * September 2022, section 9.4.2.26 Extended Capabilities element. 1792 */ is80211azNtbResponder()1793 public boolean is80211azNtbResponder() { 1794 return capabilitiesBitSet.get(NON_TB_RANGING_RESPONDER); 1795 } 1796 1797 /** 1798 * @return true if TWT Requester capability is set 1799 */ isTwtRequesterSupported()1800 public boolean isTwtRequesterSupported() { 1801 return capabilitiesBitSet.get(TWT_REQUESTER_CAPABILITY_BIT); 1802 } 1803 1804 /** 1805 * @return true if TWT Responder capability is set 1806 */ isTwtResponderSupported()1807 public boolean isTwtResponderSupported() { 1808 return capabilitiesBitSet.get(TWT_RESPONDER_CAPABILITY_BIT); 1809 } 1810 /** 1811 * @return true if Fast Initial Link Setup (FILS) capable 1812 */ isFilsCapable()1813 public boolean isFilsCapable() { 1814 return capabilitiesBitSet.get(FILS_CAPABILITY_BIT); 1815 } 1816 1817 /** 1818 * @return true if SSID should be interpreted using UTF-8 encoding 1819 */ isStrictUtf8()1820 public boolean isStrictUtf8() { 1821 return capabilitiesBitSet.get(SSID_UTF8_BIT); 1822 } 1823 1824 /** 1825 * @return true if 802.11 MC RTT Response is enabled 1826 */ is80211McRTTResponder()1827 public boolean is80211McRTTResponder() { 1828 return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT); 1829 } 1830 ExtendedCapabilities()1831 public ExtendedCapabilities() { 1832 capabilitiesBitSet = new BitSet(); 1833 } 1834 ExtendedCapabilities(ExtendedCapabilities other)1835 public ExtendedCapabilities(ExtendedCapabilities other) { 1836 capabilitiesBitSet = other.capabilitiesBitSet; 1837 } 1838 1839 /** 1840 * Parse an ExtendedCapabilities from the IE containing raw bytes. 1841 * 1842 * @param ie The Information element data 1843 */ from(InformationElement ie)1844 public void from(InformationElement ie) { 1845 capabilitiesBitSet = BitSet.valueOf(ie.bytes); 1846 } 1847 } 1848 1849 /** 1850 * parse beacon to build the capabilities 1851 * 1852 * This class is used to build the capabilities string of the scan results coming 1853 * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, 1854 * and builds the ScanResult.capabilities String in a way that mirrors the values returned 1855 * by wpa_supplicant. 1856 */ 1857 public static class Capabilities { 1858 private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000; 1859 private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000; 1860 private static final short WPA_VENDOR_OUI_VERSION = 0x0001; 1861 private static final int OWE_VENDOR_OUI_TYPE = 0x1c9a6f50; 1862 private static final int RSNE_OVERRIDE_VENDOR_OUI_TYPE = 0x299a6f50; 1863 private static final int RSNE_OVERRIDE2_VENDOR_OUI_TYPE = 0x2A9a6f50; 1864 private static final short RSNE_VERSION = 0x0001; 1865 1866 private static final int WPA_AKM_EAP = 0x01f25000; 1867 private static final int WPA_AKM_PSK = 0x02f25000; 1868 1869 private static final int RSN_AKM_EAP = 0x01ac0f00; 1870 private static final int RSN_AKM_PSK = 0x02ac0f00; 1871 private static final int RSN_AKM_FT_EAP = 0x03ac0f00; 1872 private static final int RSN_AKM_FT_PSK = 0x04ac0f00; 1873 private static final int RSN_AKM_FT_PSK_SHA384 = 0x13ac0f00; 1874 private static final int RSN_AKM_EAP_FT_SHA384 = 0x0dac0f00; 1875 private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00; 1876 private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00; 1877 private static final int RSN_AKM_SAE = 0x08ac0f00; 1878 private static final int RSN_AKM_FT_SAE = 0x09ac0f00; 1879 private static final int RSN_AKM_OWE = 0x12ac0f00; 1880 private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; 1881 private static final int RSN_OSEN = 0x019a6f50; 1882 private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00; 1883 private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00; 1884 private static final int RSN_AKM_SAE_EXT_KEY = 0x18ac0f00; 1885 private static final int RSN_AKM_FT_SAE_EXT_KEY = 0x19ac0f00; 1886 private static final int RSN_AKM_DPP = 0x029a6f50; 1887 private static final int RSN_AKM_PASN = 0x15ac0f00; 1888 1889 private static final int WPA_CIPHER_NONE = 0x00f25000; 1890 private static final int WPA_CIPHER_TKIP = 0x02f25000; 1891 private static final int WPA_CIPHER_CCMP = 0x04f25000; 1892 1893 private static final int RSN_CIPHER_NONE = 0x00ac0f00; 1894 private static final int RSN_CIPHER_TKIP = 0x02ac0f00; 1895 private static final int RSN_CIPHER_CCMP = 0x04ac0f00; 1896 private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00; 1897 private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00; 1898 private static final int RSN_CIPHER_CCMP_256 = 0x0aac0f00; 1899 private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00; 1900 private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00; 1901 private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00; 1902 private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00; 1903 1904 // RSN capability bit definition 1905 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6; 1906 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7; 1907 1908 public List<Integer> protocol; 1909 public List<List<Integer>> keyManagement; 1910 public List<List<Integer>> pairwiseCipher; 1911 public List<Integer> groupCipher; 1912 public List<Integer> groupManagementCipher; 1913 public boolean isESS; 1914 public boolean isIBSS; 1915 public boolean isPrivacy; 1916 public boolean isWPS; 1917 public boolean isManagementFrameProtectionRequired; 1918 public boolean isManagementFrameProtectionCapable; 1919 private boolean mHasPmfRequiredBitSetToFalseOccurred; 1920 public boolean isRSNEOverrideElementPresent; 1921 Capabilities()1922 public Capabilities() { 1923 } 1924 isRsneOverrideElement(InformationElement ie)1925 private static boolean isRsneOverrideElement(InformationElement ie) { 1926 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1927 try { 1928 int vendorOuiType = buf.getInt(); 1929 // Wi-Fi Alliance specific OUI and OUI type identifying RSNE Override element. 1930 return (vendorOuiType == RSNE_OVERRIDE_VENDOR_OUI_TYPE 1931 || vendorOuiType == RSNE_OVERRIDE2_VENDOR_OUI_TYPE); 1932 } catch (BufferUnderflowException e) { 1933 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1934 return false; 1935 } 1936 } 1937 1938 // RSNE format (size unit: byte) 1939 // 1940 // | Element ID | Length | Version | Group Data Cipher Suite | 1941 // 1 1 2 4 1942 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1943 // 2 4 * m 1944 // | AKM Suite Count | AKM Suite List | RSN Capabilities | 1945 // 2 4 * n 2 1946 // | PMKID Count | PMKID List | Group Management Cipher Suite | 1947 // 2 16 * s 4 1948 // 1949 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1950 // stripped off already parseRsnElement(ByteBuffer buf, SparseIntArray unknownAkmMap)1951 private void parseRsnElement(ByteBuffer buf, SparseIntArray unknownAkmMap) { 1952 1953 try { 1954 // version 1955 if (buf.getShort() != RSNE_VERSION) { 1956 // incorrect version 1957 return; 1958 } 1959 1960 // found the RSNE IE, hence start building the capability string 1961 protocol.add(ScanResult.PROTOCOL_RSN); 1962 1963 // group data cipher suite 1964 groupCipher.add(parseRsnCipher(buf.getInt())); 1965 1966 // pairwise cipher suite count 1967 short cipherCount = buf.getShort(); 1968 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>(); 1969 // pairwise cipher suite list 1970 for (int i = 0; i < cipherCount; i++) { 1971 rsnPairwiseCipher.add(parseRsnCipher(buf.getInt())); 1972 } 1973 pairwiseCipher.add(rsnPairwiseCipher); 1974 1975 // AKM 1976 // AKM suite count 1977 short akmCount = buf.getShort(); 1978 ArrayList<Integer> rsnKeyManagement = new ArrayList<>(); 1979 1980 for (int i = 0; i < akmCount; i++) { 1981 int akm = buf.getInt(); 1982 switch (akm) { 1983 case RSN_AKM_EAP: 1984 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1985 break; 1986 case RSN_AKM_PSK: 1987 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1988 break; 1989 case RSN_AKM_FT_EAP: 1990 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP); 1991 break; 1992 case RSN_AKM_FT_PSK: 1993 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK); 1994 break; 1995 case RSN_AKM_EAP_SHA256: 1996 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256); 1997 break; 1998 case RSN_AKM_PSK_SHA256: 1999 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256); 2000 break; 2001 case RSN_AKM_SAE: 2002 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE); 2003 break; 2004 case RSN_AKM_FT_SAE: 2005 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE); 2006 break; 2007 case RSN_AKM_SAE_EXT_KEY: 2008 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE_EXT_KEY); 2009 break; 2010 case RSN_AKM_FT_SAE_EXT_KEY: 2011 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE_EXT_KEY); 2012 break; 2013 case RSN_AKM_OWE: 2014 rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE); 2015 break; 2016 case RSN_AKM_EAP_SUITE_B_192: 2017 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); 2018 break; 2019 case RSN_OSEN: 2020 rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); 2021 break; 2022 case RSN_AKM_EAP_FILS_SHA256: 2023 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); 2024 break; 2025 case RSN_AKM_EAP_FILS_SHA384: 2026 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); 2027 break; 2028 case RSN_AKM_DPP: 2029 rsnKeyManagement.add(ScanResult.KEY_MGMT_DPP); 2030 break; 2031 case RSN_AKM_FT_PSK_SHA384: 2032 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK_SHA384); 2033 break; 2034 case RSN_AKM_EAP_FT_SHA384: 2035 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_FT_SHA384); 2036 break; 2037 case RSN_AKM_PASN: 2038 rsnKeyManagement.add(ScanResult.KEY_MGMT_PASN); 2039 break; 2040 default: { 2041 int akmScheme = 2042 getScanResultAkmSchemeOfUnknownAkmIfConfigured( 2043 akm, unknownAkmMap); 2044 rsnKeyManagement.add(akmScheme); 2045 break; 2046 } 2047 } 2048 } 2049 // Default AKM 2050 if (rsnKeyManagement.isEmpty()) { 2051 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 2052 } 2053 keyManagement.add(rsnKeyManagement); 2054 2055 // RSN capabilities (optional), 2056 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016 2057 if (buf.remaining() < 2) return; 2058 int rsnCaps = buf.getShort(); 2059 // This method gets called multiple times if the AP and STA supports RSN overriding. 2060 // The PMF required bit should be set to false if one of the RSN fields PMF 2061 // required bit is ever false. The PMF capable bit should be set to true if one of 2062 // the RSN fields PMF capable bit is ever true. 2063 if ((RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps) == 0) { 2064 mHasPmfRequiredBitSetToFalseOccurred = true; 2065 isManagementFrameProtectionRequired = false; 2066 } else if (!mHasPmfRequiredBitSetToFalseOccurred) { 2067 isManagementFrameProtectionRequired = true; 2068 } 2069 isManagementFrameProtectionCapable |= 2070 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps); 2071 2072 if (buf.remaining() < 2) return; 2073 // PMKID, it's not used, drop it if exists (optional). 2074 int rsnPmkIdCount = buf.getShort(); 2075 for (int i = 0; i < rsnPmkIdCount; i++) { 2076 // Each PMKID element length in the PMKID List is 16 bytes 2077 byte[] tmp = new byte[16]; 2078 buf.get(tmp); 2079 } 2080 2081 // Group management cipher suite (optional). 2082 if (buf.remaining() < 4) return; 2083 groupManagementCipher.add(parseRsnCipher(buf.getInt())); 2084 } catch (BufferUnderflowException e) { 2085 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow"); 2086 } 2087 } 2088 2089 /** 2090 * Get the ScanResult security key management scheme (ScanResult.KEY_MGMT_XX) corresponding 2091 * to the unknown AKMs configured in overlay config item 2092 * config_wifiUnknownAkmToKnownAkmMapping 2093 * 2094 * @param unknownAkm unknown AKM seen in the received beacon or probe response. 2095 * @param unknownAkmMap unknownAkmMap Mapping of unknown AKMs configured in overlay config 2096 * item config_wifiUnknownAkmToKnownAkmMapping to ScanResult security key management 2097 * scheme (ScanResult.KEY_MGMT_XX). 2098 * @return A valid ScanResult.KEY_MGMT_XX if unknownAkm is configured in the overlay, 2099 * ScanResult.KEY_MGMT_UNKNOWN otherwise 2100 */ getScanResultAkmSchemeOfUnknownAkmIfConfigured( int unknownAkm, SparseIntArray unknownAkmMap)2101 private int getScanResultAkmSchemeOfUnknownAkmIfConfigured( 2102 int unknownAkm, SparseIntArray unknownAkmMap) { 2103 if (unknownAkmMap != null) { 2104 return unknownAkmMap.get(unknownAkm, ScanResult.KEY_MGMT_UNKNOWN); 2105 } else { 2106 return ScanResult.KEY_MGMT_UNKNOWN; 2107 } 2108 } 2109 parseWpaCipher(int cipher)2110 private static @Cipher int parseWpaCipher(int cipher) { 2111 switch (cipher) { 2112 case WPA_CIPHER_NONE: 2113 return ScanResult.CIPHER_NONE; 2114 case WPA_CIPHER_TKIP: 2115 return ScanResult.CIPHER_TKIP; 2116 case WPA_CIPHER_CCMP: 2117 return ScanResult.CIPHER_CCMP; 2118 default: 2119 Log.w("IE_Capabilities", "Unknown WPA cipher suite: " 2120 + Integer.toHexString(cipher)); 2121 return ScanResult.CIPHER_NONE; 2122 } 2123 } 2124 parseRsnCipher(int cipher)2125 private static @Cipher int parseRsnCipher(int cipher) { 2126 switch (cipher) { 2127 case RSN_CIPHER_NONE: 2128 return ScanResult.CIPHER_NONE; 2129 case RSN_CIPHER_TKIP: 2130 return ScanResult.CIPHER_TKIP; 2131 case RSN_CIPHER_CCMP: 2132 return ScanResult.CIPHER_CCMP; 2133 case RSN_CIPHER_CCMP_256: 2134 return ScanResult.CIPHER_CCMP_256; 2135 case RSN_CIPHER_GCMP_256: 2136 return ScanResult.CIPHER_GCMP_256; 2137 case RSN_CIPHER_NO_GROUP_ADDRESSED: 2138 return ScanResult.CIPHER_NO_GROUP_ADDRESSED; 2139 case RSN_CIPHER_GCMP_128: 2140 return ScanResult.CIPHER_GCMP_128; 2141 case RSN_CIPHER_BIP_GMAC_128: 2142 return ScanResult.CIPHER_BIP_GMAC_128; 2143 case RSN_CIPHER_BIP_GMAC_256: 2144 return ScanResult.CIPHER_BIP_GMAC_256; 2145 case RSN_CIPHER_BIP_CMAC_256: 2146 return ScanResult.CIPHER_BIP_CMAC_256; 2147 default: 2148 Log.w("IE_Capabilities", "Unknown RSN cipher suite: " 2149 + Integer.toHexString(cipher)); 2150 return ScanResult.CIPHER_NONE; 2151 } 2152 } 2153 isWpsElement(InformationElement ie)2154 private static boolean isWpsElement(InformationElement ie) { 2155 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2156 try { 2157 // WPS OUI and type 2158 return (buf.getInt() == WPS_VENDOR_OUI_TYPE); 2159 } catch (BufferUnderflowException e) { 2160 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 2161 return false; 2162 } 2163 } 2164 isWpaOneElement(InformationElement ie)2165 private static boolean isWpaOneElement(InformationElement ie) { 2166 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2167 2168 try { 2169 // WPA OUI and type 2170 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE); 2171 } catch (BufferUnderflowException e) { 2172 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 2173 return false; 2174 } 2175 } 2176 2177 // WPA type 1 format (size unit: byte) 2178 // 2179 // | Element ID | Length | OUI | Type | Version | 2180 // 1 1 3 1 2 2181 // | Group Data Cipher Suite | 2182 // 4 2183 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 2184 // 2 4 * m 2185 // | AKM Suite Count | AKM Suite List | 2186 // 2 4 * n 2187 // 2188 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2189 // stripped off already 2190 // parseWpaOneElement(InformationElement ie, SparseIntArray unknownAkmMap)2191 private void parseWpaOneElement(InformationElement ie, SparseIntArray unknownAkmMap) { 2192 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2193 2194 try { 2195 // skip WPA OUI and type parsing. isWpaOneElement() should have 2196 // been called for verification before we reach here. 2197 buf.getInt(); 2198 2199 // version 2200 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) { 2201 // incorrect version 2202 return; 2203 } 2204 2205 // start building the string 2206 protocol.add(ScanResult.PROTOCOL_WPA); 2207 2208 // group data cipher suite 2209 groupCipher.add(parseWpaCipher(buf.getInt())); 2210 2211 // pairwise cipher suite count 2212 short cipherCount = buf.getShort(); 2213 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>(); 2214 // pairwise chipher suite list 2215 for (int i = 0; i < cipherCount; i++) { 2216 wpaPairwiseCipher.add(parseWpaCipher(buf.getInt())); 2217 } 2218 pairwiseCipher.add(wpaPairwiseCipher); 2219 2220 // AKM 2221 // AKM suite count 2222 short akmCount = buf.getShort(); 2223 ArrayList<Integer> wpaKeyManagement = new ArrayList<>(); 2224 2225 // AKM suite list 2226 for (int i = 0; i < akmCount; i++) { 2227 int akm = buf.getInt(); 2228 switch (akm) { 2229 case WPA_AKM_EAP: 2230 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 2231 break; 2232 case WPA_AKM_PSK: 2233 wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK); 2234 break; 2235 default: 2236 int akmScheme = 2237 getScanResultAkmSchemeOfUnknownAkmIfConfigured( 2238 akm, unknownAkmMap); 2239 wpaKeyManagement.add(akmScheme); 2240 break; 2241 } 2242 } 2243 // Default AKM 2244 if (wpaKeyManagement.isEmpty()) { 2245 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 2246 } 2247 keyManagement.add(wpaKeyManagement); 2248 } catch (BufferUnderflowException e) { 2249 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow"); 2250 } 2251 } 2252 2253 /** 2254 * Parse the Information Element and the 16-bit Capability Information field to build the 2255 * InformationElemmentUtil.capabilities object. 2256 * 2257 * @param ies -- Information Element array 2258 * @param beaconCap -- 16-bit Beacon Capability Information field 2259 * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device 2260 * @param isRsnOverridingSupported -- Boolean flag to indicate if RSN Overriding is 2261 * supported by the device 2262 * @param freq -- Frequency on which frame/beacon was transmitted. Some parsing may be 2263 * affected such as DMG parameters in DMG (60GHz) beacon. 2264 * @param unknownAkmMap -- unknown AKM to known AKM mapping (Internally converted to 2265 * security key management scheme(ScanResult.KEY_MGMT_XX)) configured in overlay config 2266 * item config_wifiUnknownAkmToKnownAkmMapping. 2267 */ from( InformationElement[] ies, int beaconCap, boolean isOweSupported, boolean isRsnOverridingSupported, int freq, SparseIntArray unknownAkmMap)2268 public void from( 2269 InformationElement[] ies, 2270 int beaconCap, 2271 boolean isOweSupported, 2272 boolean isRsnOverridingSupported, 2273 int freq, 2274 SparseIntArray unknownAkmMap) { 2275 protocol = new ArrayList<>(); 2276 keyManagement = new ArrayList<>(); 2277 groupCipher = new ArrayList<>(); 2278 pairwiseCipher = new ArrayList<>(); 2279 groupManagementCipher = new ArrayList<>(); 2280 2281 if (ies == null) { 2282 return; 2283 } 2284 isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0; 2285 if (ScanResult.is60GHz(freq)) { 2286 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */ 2287 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS) 2288 == NativeScanResult.BSS_CAPABILITY_DMG_ESS) { 2289 isESS = true; 2290 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) { 2291 isIBSS = true; 2292 } 2293 } else { 2294 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0; 2295 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0; 2296 } 2297 for (InformationElement ie : ies) { 2298 WifiNl80211Manager.OemSecurityType oemSecurityType = 2299 WifiNl80211Manager.parseOemSecurityTypeElement( 2300 ie.id, ie.idExt, ie.bytes); 2301 if (oemSecurityType != null 2302 && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) { 2303 protocol.add(oemSecurityType.protocol); 2304 keyManagement.add(oemSecurityType.keyManagement); 2305 pairwiseCipher.add(oemSecurityType.pairwiseCipher); 2306 groupCipher.add(oemSecurityType.groupCipher); 2307 } 2308 2309 if (ie.id == InformationElement.EID_RSN) { 2310 parseRsnElement(ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN), 2311 unknownAkmMap); 2312 } 2313 2314 if (ie.id == InformationElement.EID_VSA) { 2315 if (isRsnOverridingSupported && isRsneOverrideElement(ie)) { 2316 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2317 // RSN Override and RSN Override 2 vendor specific element begins 2318 // with 3 bytes of Wi-Fi Alliance specific OUI and 1 byte of OUI type. 2319 // The Payload field of the RSNE Override element and the RSNE Override 2 2320 // element uses the same format as the Information field of the RSNE. 2321 // So skip the 4 byte OUI field and proceed to parse the RSN element. 2322 buf.getInt(); 2323 parseRsnElement(buf, unknownAkmMap); 2324 isRSNEOverrideElementPresent = true; 2325 } 2326 if (isWpaOneElement(ie)) { 2327 parseWpaOneElement(ie, unknownAkmMap); 2328 } 2329 if (isWpsElement(ie)) { 2330 // TODO(b/62134557): parse WPS IE to provide finer granularity information. 2331 isWPS = true; 2332 } 2333 if (isOweSupported && isOweElement(ie)) { 2334 /* From RFC 8110: Once the client and AP have finished 802.11 association, 2335 they then complete the Diffie-Hellman key exchange and create a Pairwise 2336 Master Key (PMK) and its associated identifier, PMKID [IEEE802.11]. 2337 Upon completion of 802.11 association, the AP initiates the 4-way 2338 handshake to the client using the PMK generated above. The 4-way 2339 handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation 2340 Key (KCK), and a Message Integrity Code (MIC) to use for protection 2341 of the frames that define the 4-way handshake. 2342 2343 We check if OWE is supported here because we are adding the OWE 2344 capabilities to the Open BSS. Non-supporting devices need to see this 2345 open network and ignore this element. Supporting devices need to hide 2346 the Open BSS of OWE in transition mode and connect to the Hidden one. 2347 */ 2348 protocol.add(ScanResult.PROTOCOL_RSN); 2349 groupCipher.add(ScanResult.CIPHER_CCMP); 2350 ArrayList<Integer> owePairwiseCipher = new ArrayList<>(); 2351 owePairwiseCipher.add(ScanResult.CIPHER_CCMP); 2352 pairwiseCipher.add(owePairwiseCipher); 2353 ArrayList<Integer> oweKeyManagement = new ArrayList<>(); 2354 oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION); 2355 keyManagement.add(oweKeyManagement); 2356 } 2357 } 2358 } 2359 } 2360 2361 /** Convert the AKM suite selector to scan result Security key management scheme */ akmToScanResultKeyManagementScheme(int akm)2362 public static int akmToScanResultKeyManagementScheme(int akm) { 2363 switch (akm) { 2364 case RSN_AKM_EAP: 2365 case WPA_AKM_EAP: 2366 return ScanResult.KEY_MGMT_EAP; 2367 case RSN_AKM_PSK: 2368 case WPA_AKM_PSK: 2369 return ScanResult.KEY_MGMT_PSK; 2370 case RSN_AKM_FT_EAP: 2371 return ScanResult.KEY_MGMT_FT_EAP; 2372 case RSN_AKM_FT_PSK: 2373 return ScanResult.KEY_MGMT_FT_PSK; 2374 case RSN_AKM_EAP_SHA256: 2375 return ScanResult.KEY_MGMT_EAP_SHA256; 2376 case RSN_AKM_PSK_SHA256: 2377 return ScanResult.KEY_MGMT_PSK_SHA256; 2378 case RSN_AKM_SAE: 2379 return ScanResult.KEY_MGMT_SAE; 2380 case RSN_AKM_FT_SAE: 2381 return ScanResult.KEY_MGMT_FT_SAE; 2382 case RSN_AKM_SAE_EXT_KEY: 2383 return ScanResult.KEY_MGMT_SAE_EXT_KEY; 2384 case RSN_AKM_FT_SAE_EXT_KEY: 2385 return ScanResult.KEY_MGMT_FT_SAE_EXT_KEY; 2386 case RSN_AKM_OWE: 2387 return ScanResult.KEY_MGMT_OWE; 2388 case RSN_AKM_EAP_SUITE_B_192: 2389 return ScanResult.KEY_MGMT_EAP_SUITE_B_192; 2390 case RSN_OSEN: 2391 return ScanResult.KEY_MGMT_OSEN; 2392 case RSN_AKM_EAP_FILS_SHA256: 2393 return ScanResult.KEY_MGMT_FILS_SHA256; 2394 case RSN_AKM_EAP_FILS_SHA384: 2395 return ScanResult.KEY_MGMT_FILS_SHA384; 2396 case RSN_AKM_DPP: 2397 return ScanResult.KEY_MGMT_DPP; 2398 case RSN_AKM_FT_PSK_SHA384: 2399 return ScanResult.KEY_MGMT_FT_PSK_SHA384; 2400 case RSN_AKM_EAP_FT_SHA384: 2401 return ScanResult.KEY_MGMT_EAP_FT_SHA384; 2402 case RSN_AKM_PASN: 2403 return ScanResult.KEY_MGMT_PASN; 2404 default: 2405 return ScanResult.KEY_MGMT_UNKNOWN; 2406 } 2407 } 2408 isOweElement(InformationElement ie)2409 private static boolean isOweElement(InformationElement ie) { 2410 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2411 try { 2412 // OWE OUI and type 2413 return (buf.getInt() == OWE_VENDOR_OUI_TYPE); 2414 } catch (BufferUnderflowException e) { 2415 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 2416 return false; 2417 } 2418 } 2419 protocolToString(@rotocol int protocol)2420 private String protocolToString(@Protocol int protocol) { 2421 switch (protocol) { 2422 case ScanResult.PROTOCOL_NONE: 2423 return "None"; 2424 case ScanResult.PROTOCOL_WPA: 2425 return "WPA"; 2426 case ScanResult.PROTOCOL_RSN: 2427 return "RSN"; 2428 case ScanResult.PROTOCOL_OSEN: 2429 return "OSEN"; 2430 case ScanResult.PROTOCOL_WAPI: 2431 return "WAPI"; 2432 default: 2433 return "?"; 2434 } 2435 } 2436 keyManagementToString(@eyMgmt int akm)2437 private String keyManagementToString(@KeyMgmt int akm) { 2438 switch (akm) { 2439 case ScanResult.KEY_MGMT_NONE: 2440 return "None"; 2441 case ScanResult.KEY_MGMT_PSK: 2442 return "PSK"; 2443 case ScanResult.KEY_MGMT_EAP: 2444 return "EAP/SHA1"; 2445 case ScanResult.KEY_MGMT_FT_EAP: 2446 return "FT/EAP"; 2447 case ScanResult.KEY_MGMT_FT_PSK: 2448 return "FT/PSK"; 2449 case ScanResult.KEY_MGMT_EAP_SHA256: 2450 return "EAP/SHA256"; 2451 case ScanResult.KEY_MGMT_PSK_SHA256: 2452 return "PSK-SHA256"; 2453 case ScanResult.KEY_MGMT_OWE: 2454 return "OWE"; 2455 case ScanResult.KEY_MGMT_OWE_TRANSITION: 2456 return "OWE_TRANSITION"; 2457 case ScanResult.KEY_MGMT_SAE: 2458 return "SAE"; 2459 case ScanResult.KEY_MGMT_FT_SAE: 2460 return "FT/SAE"; 2461 case ScanResult.KEY_MGMT_SAE_EXT_KEY: 2462 return "SAE_EXT_KEY"; 2463 case ScanResult.KEY_MGMT_FT_SAE_EXT_KEY: 2464 return "FT/SAE_EXT_KEY"; 2465 case ScanResult.KEY_MGMT_EAP_SUITE_B_192: 2466 return "EAP_SUITE_B_192"; 2467 case ScanResult.KEY_MGMT_OSEN: 2468 return "OSEN"; 2469 case ScanResult.KEY_MGMT_WAPI_PSK: 2470 return "WAPI-PSK"; 2471 case ScanResult.KEY_MGMT_WAPI_CERT: 2472 return "WAPI-CERT"; 2473 case ScanResult.KEY_MGMT_FILS_SHA256: 2474 return "EAP-FILS-SHA256"; 2475 case ScanResult.KEY_MGMT_FILS_SHA384: 2476 return "EAP-FILS-SHA384"; 2477 case ScanResult.KEY_MGMT_DPP: 2478 return "DPP"; 2479 case ScanResult.KEY_MGMT_FT_PSK_SHA384: 2480 return "FT/PSK-SHA384"; 2481 case ScanResult.KEY_MGMT_EAP_FT_SHA384: 2482 return "EAP-FT-SHA384"; 2483 case ScanResult.KEY_MGMT_PASN: 2484 return "PASN"; 2485 default: 2486 return "?"; 2487 } 2488 } 2489 cipherToString(@ipher int cipher)2490 private String cipherToString(@Cipher int cipher) { 2491 switch (cipher) { 2492 case ScanResult.CIPHER_NONE: 2493 return "None"; 2494 case ScanResult.CIPHER_CCMP: 2495 return "CCMP-128"; 2496 case ScanResult.CIPHER_CCMP_256: 2497 return "CCMP-256"; 2498 case ScanResult.CIPHER_GCMP_128: 2499 return "GCMP-128"; 2500 case ScanResult.CIPHER_GCMP_256: 2501 return "GCMP-256"; 2502 case ScanResult.CIPHER_TKIP: 2503 return "TKIP"; 2504 case ScanResult.CIPHER_SMS4: 2505 return "SMS4"; 2506 default: 2507 return "?"; 2508 } 2509 } 2510 2511 /** 2512 * Build the ScanResult.capabilities String. 2513 * 2514 * @return security string that mirrors what wpa_supplicant generates 2515 */ generateCapabilitiesString()2516 public String generateCapabilitiesString() { 2517 StringBuilder capabilities = new StringBuilder(); 2518 // private Beacon without an RSNE or WPA IE, hence WEP0 2519 boolean isWEP = (protocol.isEmpty()) && isPrivacy; 2520 2521 if (isWEP) { 2522 capabilities.append("[WEP]"); 2523 } 2524 for (int i = 0; i < protocol.size(); i++) { 2525 String capability = generateCapabilitiesStringPerProtocol(i); 2526 // add duplicate capabilities for WPA2 for backward compatibility: 2527 // duplicate "RSN" entries as "WPA2" 2528 String capWpa2 = generateWPA2CapabilitiesString(capability, i); 2529 capabilities.append(capWpa2); 2530 capabilities.append(capability); 2531 } 2532 if (isESS) { 2533 capabilities.append("[ESS]"); 2534 } 2535 if (isIBSS) { 2536 capabilities.append("[IBSS]"); 2537 } 2538 if (isWPS) { 2539 capabilities.append("[WPS]"); 2540 } 2541 if (isManagementFrameProtectionRequired) { 2542 capabilities.append("[MFPR]"); 2543 } 2544 if (isManagementFrameProtectionCapable) { 2545 capabilities.append("[MFPC]"); 2546 } 2547 if (isRSNEOverrideElementPresent) { 2548 capabilities.append("[RSNO]"); 2549 } 2550 2551 return capabilities.toString(); 2552 } 2553 2554 /** 2555 * Build the Capability String for one protocol 2556 * @param index: index number of the protocol 2557 * @return security string for one protocol 2558 */ generateCapabilitiesStringPerProtocol(int index)2559 private String generateCapabilitiesStringPerProtocol(int index) { 2560 StringBuilder capability = new StringBuilder(); 2561 capability.append("[").append(protocolToString(protocol.get(index))); 2562 2563 if (index < keyManagement.size()) { 2564 for (int j = 0; j < keyManagement.get(index).size(); j++) { 2565 capability.append((j == 0) ? "-" : "+").append( 2566 keyManagementToString(keyManagement.get(index).get(j))); 2567 } 2568 } 2569 if (index < pairwiseCipher.size()) { 2570 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 2571 capability.append((j == 0) ? "-" : "+").append( 2572 cipherToString(pairwiseCipher.get(index).get(j))); 2573 } 2574 } 2575 capability.append("]"); 2576 return capability.toString(); 2577 } 2578 2579 /** 2580 * Build the duplicate Capability String for WPA2 2581 * @param cap: original capability String 2582 * @param index: index number of the protocol 2583 * @return security string for WPA2, empty String if protocol is not WPA2 2584 */ generateWPA2CapabilitiesString(String cap, int index)2585 private String generateWPA2CapabilitiesString(String cap, int index) { 2586 StringBuilder capWpa2 = new StringBuilder(); 2587 // if not WPA2, return empty String 2588 if (cap.contains("EAP_SUITE_B_192") 2589 || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP") 2590 && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) { 2591 return ""; 2592 } 2593 capWpa2.append("[").append("WPA2"); 2594 if (index < keyManagement.size()) { 2595 for (int j = 0; j < keyManagement.get(index).size(); j++) { 2596 capWpa2.append((j == 0) ? "-" : "+").append( 2597 keyManagementToString(keyManagement.get(index).get(j))); 2598 // WPA3/WPA2 transition mode 2599 if (cap.contains("SAE")) { 2600 break; 2601 } 2602 } 2603 } 2604 if (index < pairwiseCipher.size()) { 2605 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 2606 capWpa2.append((j == 0) ? "-" : "+").append( 2607 cipherToString(pairwiseCipher.get(index).get(j))); 2608 } 2609 } 2610 capWpa2.append("]"); 2611 return capWpa2.toString(); 2612 } 2613 } 2614 2615 2616 /** 2617 * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will 2618 * only be present in scan results that are derived from a Beacon Frame, not from the more 2619 * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct. 2620 */ 2621 public static class TrafficIndicationMap { 2622 private static final int MAX_TIM_LENGTH = 254; 2623 private boolean mValid = false; 2624 public int mLength = 0; 2625 public int mDtimCount = -1; 2626 //Negative DTIM Period means no TIM element was given this frame. 2627 public int mDtimPeriod = -1; 2628 public int mBitmapControl = 0; 2629 2630 /** 2631 * Is this a valid TIM information element. 2632 */ isValid()2633 public boolean isValid() { 2634 return mValid; 2635 } 2636 2637 // Traffic Indication Map format (size unit: byte) 2638 // 2639 //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap | 2640 // 1 1 1 1 1 1 - 251 2641 // 2642 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2643 // stripped off already 2644 // from(InformationElement ie)2645 public void from(InformationElement ie) { 2646 mValid = false; 2647 if (ie == null || ie.bytes == null) return; 2648 mLength = ie.bytes.length; 2649 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2650 try { 2651 mDtimCount = data.get() & Constants.BYTE_MASK; 2652 mDtimPeriod = data.get() & Constants.BYTE_MASK; 2653 mBitmapControl = data.get() & Constants.BYTE_MASK; 2654 //A valid TIM element must have atleast one more byte 2655 data.get(); 2656 } catch (BufferUnderflowException e) { 2657 return; 2658 } 2659 if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { 2660 mValid = true; 2661 } 2662 } 2663 } 2664 2665 /** 2666 * This util class determines the 802.11 standard (a/b/g/n/ac/ax/be) being used 2667 */ 2668 public static class WifiMode { 2669 public static final int MODE_UNDEFINED = 0; // Unknown/undefined 2670 public static final int MODE_11A = 1; // 802.11a 2671 public static final int MODE_11B = 2; // 802.11b 2672 public static final int MODE_11G = 3; // 802.11g 2673 public static final int MODE_11N = 4; // 802.11n 2674 public static final int MODE_11AC = 5; // 802.11ac 2675 public static final int MODE_11AX = 6; // 802.11ax 2676 public static final int MODE_11BE = 7; // 802.11be 2677 //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A 2678 2679 /** 2680 * Use frequency, max supported rate, and the existence of EHT, HE, VHT, HT & ERP fields in 2681 * scan result to determine the 802.11 Wifi standard being used. 2682 */ determineMode(int frequency, int maxRate, boolean foundEht, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)2683 public static int determineMode(int frequency, int maxRate, boolean foundEht, 2684 boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp) { 2685 if (foundEht) { 2686 return MODE_11BE; 2687 } else if (foundHe) { 2688 return MODE_11AX; 2689 } else if (!ScanResult.is24GHz(frequency) && foundVht) { 2690 // Do not include subset of VHT on 2.4 GHz vendor extension 2691 // in consideration for reporting VHT. 2692 return MODE_11AC; 2693 } else if (foundHt) { 2694 return MODE_11N; 2695 } else if (foundErp) { 2696 return MODE_11G; 2697 } else if (ScanResult.is24GHz(frequency)) { 2698 if (maxRate < 24000000) { 2699 return MODE_11B; 2700 } else { 2701 return MODE_11G; 2702 } 2703 } else { 2704 return MODE_11A; 2705 } 2706 } 2707 2708 /** 2709 * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC/AX/BE> 2710 */ toString(int mode)2711 public static String toString(int mode) { 2712 switch(mode) { 2713 case MODE_11A: 2714 return "MODE_11A"; 2715 case MODE_11B: 2716 return "MODE_11B"; 2717 case MODE_11G: 2718 return "MODE_11G"; 2719 case MODE_11N: 2720 return "MODE_11N"; 2721 case MODE_11AC: 2722 return "MODE_11AC"; 2723 case MODE_11AX: 2724 return "MODE_11AX"; 2725 case MODE_11BE: 2726 return "MODE_11BE"; 2727 default: 2728 return "MODE_UNDEFINED"; 2729 } 2730 } 2731 } 2732 2733 /** 2734 * Parser for both the Supported Rates & Extended Supported Rates Information Elements 2735 */ 2736 public static class SupportedRates { 2737 public static final int MASK = 0x7F; // 0111 1111 2738 public boolean mValid = false; 2739 public ArrayList<Integer> mRates; 2740 SupportedRates()2741 public SupportedRates() { 2742 mRates = new ArrayList<Integer>(); 2743 } 2744 2745 /** 2746 * Is this a valid Supported Rates information element. 2747 */ isValid()2748 public boolean isValid() { 2749 return mValid; 2750 } 2751 2752 /** 2753 * get the Rate in bits/s from associated byteval 2754 */ getRateFromByte(int byteVal)2755 public static int getRateFromByte(int byteVal) { 2756 byteVal &= MASK; 2757 switch(byteVal) { 2758 case 2: 2759 return 1000000; 2760 case 4: 2761 return 2000000; 2762 case 11: 2763 return 5500000; 2764 case 12: 2765 return 6000000; 2766 case 18: 2767 return 9000000; 2768 case 22: 2769 return 11000000; 2770 case 24: 2771 return 12000000; 2772 case 36: 2773 return 18000000; 2774 case 44: 2775 return 22000000; 2776 case 48: 2777 return 24000000; 2778 case 66: 2779 return 33000000; 2780 case 72: 2781 return 36000000; 2782 case 96: 2783 return 48000000; 2784 case 108: 2785 return 54000000; 2786 default: 2787 //ERROR UNKNOWN RATE 2788 return -1; 2789 } 2790 } 2791 2792 // Supported Rates format (size unit: byte) 2793 // 2794 //| ElementID | Length | Supported Rates [7 Little Endian Info bits - 1 Flag bit] 2795 // 1 1 1 - 8 2796 // 2797 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2798 // stripped off already 2799 // from(InformationElement ie)2800 public void from(InformationElement ie) { 2801 mValid = false; 2802 if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1) { 2803 return; 2804 } 2805 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2806 try { 2807 for (int i = 0; i < ie.bytes.length; i++) { 2808 int rate = getRateFromByte(data.get()); 2809 if (rate > 0) { 2810 mRates.add(rate); 2811 } else { 2812 return; 2813 } 2814 } 2815 } catch (BufferUnderflowException e) { 2816 return; 2817 } 2818 mValid = true; 2819 return; 2820 } 2821 2822 /** 2823 * Lists the rates in a human readable string 2824 */ toString()2825 public String toString() { 2826 StringBuilder sbuf = new StringBuilder(); 2827 for (Integer rate : mRates) { 2828 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", "); 2829 } 2830 return sbuf.toString(); 2831 } 2832 } 2833 2834 /** 2835 * This util class determines country related information in beacon/probe response 2836 */ 2837 public static class Country { 2838 private boolean mValid = false; 2839 public String mCountryCode = "00"; 2840 2841 /** 2842 * Parse the Information Element Country Information field. Note that element ID and length 2843 * fields are already removed. 2844 * 2845 * Country IE format (size unit: byte) 2846 * 2847 * ElementID | Length | country string | triplet | padding 2848 * 1 1 3 Q*x 0 or 1 2849 * First two bytes of country string are country code 2850 * See 802.11 spec dot11CountryString definition. 2851 */ from(InformationElement ie)2852 public void from(InformationElement ie) { 2853 mValid = false; 2854 if (ie == null || ie.bytes == null || ie.bytes.length < 3) return; 2855 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2856 try { 2857 char letter1 = (char) (data.get() & Constants.BYTE_MASK); 2858 char letter2 = (char) (data.get() & Constants.BYTE_MASK); 2859 char letter3 = (char) (data.get() & Constants.BYTE_MASK); 2860 // See 802.11 spec dot11CountryString definition. 2861 // ' ', 'O', 'I' are for all operation, outdoor, indoor environments, respectively. 2862 mValid = (letter3 == ' ' || letter3 == 'O' || letter3 == 'I') 2863 && Character.isLetterOrDigit((int) letter1) 2864 && Character.isLetterOrDigit((int) letter2); 2865 if (mValid) { 2866 mCountryCode = (String.valueOf(letter1) + letter2).toUpperCase(Locale.US); 2867 } 2868 } catch (BufferUnderflowException e) { 2869 return; 2870 } 2871 } 2872 2873 /** 2874 * Is this a valid country information element. 2875 */ isValid()2876 public boolean isValid() { 2877 return mValid; 2878 } 2879 2880 /** 2881 * @return country code indicated in beacon/probe response frames 2882 */ getCountryCode()2883 public String getCountryCode() { 2884 return mCountryCode; 2885 } 2886 } 2887 2888 /* 2889 * RSNXE (Robust Security Network Extended) Field 2890 * 2891 * RSNXE is a field within Wi-Fi beacon frames that provides extra information about the 2892 * access point's security capabilities, going beyond the basics of RSN (Robust Security 2893 * Network). 2894 */ 2895 public static class Rsnxe { 2896 private static final int SECURE_HE_LTF_SUPPORT_BIT = 8; 2897 private static final int URNM_MFPR_BIT = 15; 2898 private boolean mIsSecureHeLtfSupported; 2899 private boolean mIsRangingFrameProtectionRequired; 2900 2901 /** 2902 * Parse RSN extension element 2903 * @param ie Information element 2904 */ from(InformationElement ie)2905 public void from(InformationElement ie) { 2906 if (ie == null || ie.id != InformationElement.EID_RSN_EXTENSION) return; 2907 BitSet rsnxBitset = BitSet.valueOf( 2908 ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN)); 2909 mIsSecureHeLtfSupported = rsnxBitset.get(SECURE_HE_LTF_SUPPORT_BIT); 2910 mIsRangingFrameProtectionRequired = rsnxBitset.get(URNM_MFPR_BIT); 2911 } 2912 2913 /** 2914 * The secure HE-LTF is a security enhancement for ranging measurements where the HE-LTF 2915 * sequence is randomized using cryptographic keys derived from the security association. 2916 * 2917 * @return Whether the secure HE-LTF is supported or not. 2918 */ isSecureHeLtfSupported()2919 public boolean isSecureHeLtfSupported() { 2920 return mIsSecureHeLtfSupported; 2921 } 2922 2923 /** 2924 * A security policy that specifies whether ranging frames are required to be protected 2925 * without association. 2926 * @return Whether ranging frames are required to be protected or not. 2927 */ isRangingFrameProtectionRequired()2928 public boolean isRangingFrameProtectionRequired() { 2929 return mIsRangingFrameProtectionRequired; 2930 } 2931 } 2932 } 2933