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 short RSNE_VERSION = 0x0001; 1863 1864 private static final int WPA_AKM_EAP = 0x01f25000; 1865 private static final int WPA_AKM_PSK = 0x02f25000; 1866 1867 private static final int RSN_AKM_EAP = 0x01ac0f00; 1868 private static final int RSN_AKM_PSK = 0x02ac0f00; 1869 private static final int RSN_AKM_FT_EAP = 0x03ac0f00; 1870 private static final int RSN_AKM_FT_PSK = 0x04ac0f00; 1871 private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00; 1872 private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00; 1873 private static final int RSN_AKM_SAE = 0x08ac0f00; 1874 private static final int RSN_AKM_FT_SAE = 0x09ac0f00; 1875 private static final int RSN_AKM_OWE = 0x12ac0f00; 1876 private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; 1877 private static final int RSN_OSEN = 0x019a6f50; 1878 private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00; 1879 private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00; 1880 private static final int RSN_AKM_SAE_EXT_KEY = 0x18ac0f00; 1881 private static final int RSN_AKM_FT_SAE_EXT_KEY = 0x19ac0f00; 1882 private static final int RSN_AKM_DPP = 0x029a6f50; 1883 1884 private static final int WPA_CIPHER_NONE = 0x00f25000; 1885 private static final int WPA_CIPHER_TKIP = 0x02f25000; 1886 private static final int WPA_CIPHER_CCMP = 0x04f25000; 1887 1888 private static final int RSN_CIPHER_NONE = 0x00ac0f00; 1889 private static final int RSN_CIPHER_TKIP = 0x02ac0f00; 1890 private static final int RSN_CIPHER_CCMP = 0x04ac0f00; 1891 private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00; 1892 private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00; 1893 private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00; 1894 private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00; 1895 private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00; 1896 private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00; 1897 1898 // RSN capability bit definition 1899 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6; 1900 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7; 1901 1902 public List<Integer> protocol; 1903 public List<List<Integer>> keyManagement; 1904 public List<List<Integer>> pairwiseCipher; 1905 public List<Integer> groupCipher; 1906 public List<Integer> groupManagementCipher; 1907 public boolean isESS; 1908 public boolean isIBSS; 1909 public boolean isPrivacy; 1910 public boolean isWPS; 1911 public boolean isManagementFrameProtectionRequired; 1912 public boolean isManagementFrameProtectionCapable; 1913 Capabilities()1914 public Capabilities() { 1915 } 1916 1917 // RSNE format (size unit: byte) 1918 // 1919 // | Element ID | Length | Version | Group Data Cipher Suite | 1920 // 1 1 2 4 1921 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1922 // 2 4 * m 1923 // | AKM Suite Count | AKM Suite List | RSN Capabilities | 1924 // 2 4 * n 2 1925 // | PMKID Count | PMKID List | Group Management Cipher Suite | 1926 // 2 16 * s 4 1927 // 1928 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1929 // stripped off already parseRsnElement(InformationElement ie, SparseIntArray unknownAkmMap)1930 private void parseRsnElement(InformationElement ie, SparseIntArray unknownAkmMap) { 1931 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1932 1933 try { 1934 // version 1935 if (buf.getShort() != RSNE_VERSION) { 1936 // incorrect version 1937 return; 1938 } 1939 1940 // found the RSNE IE, hence start building the capability string 1941 protocol.add(ScanResult.PROTOCOL_RSN); 1942 1943 // group data cipher suite 1944 groupCipher.add(parseRsnCipher(buf.getInt())); 1945 1946 // pairwise cipher suite count 1947 short cipherCount = buf.getShort(); 1948 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>(); 1949 // pairwise cipher suite list 1950 for (int i = 0; i < cipherCount; i++) { 1951 rsnPairwiseCipher.add(parseRsnCipher(buf.getInt())); 1952 } 1953 pairwiseCipher.add(rsnPairwiseCipher); 1954 1955 // AKM 1956 // AKM suite count 1957 short akmCount = buf.getShort(); 1958 ArrayList<Integer> rsnKeyManagement = new ArrayList<>(); 1959 1960 for (int i = 0; i < akmCount; i++) { 1961 int akm = buf.getInt(); 1962 switch (akm) { 1963 case RSN_AKM_EAP: 1964 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1965 break; 1966 case RSN_AKM_PSK: 1967 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1968 break; 1969 case RSN_AKM_FT_EAP: 1970 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP); 1971 break; 1972 case RSN_AKM_FT_PSK: 1973 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK); 1974 break; 1975 case RSN_AKM_EAP_SHA256: 1976 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256); 1977 break; 1978 case RSN_AKM_PSK_SHA256: 1979 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256); 1980 break; 1981 case RSN_AKM_SAE: 1982 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE); 1983 break; 1984 case RSN_AKM_FT_SAE: 1985 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE); 1986 break; 1987 case RSN_AKM_SAE_EXT_KEY: 1988 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE_EXT_KEY); 1989 break; 1990 case RSN_AKM_FT_SAE_EXT_KEY: 1991 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE_EXT_KEY); 1992 break; 1993 case RSN_AKM_OWE: 1994 rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE); 1995 break; 1996 case RSN_AKM_EAP_SUITE_B_192: 1997 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); 1998 break; 1999 case RSN_OSEN: 2000 rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); 2001 break; 2002 case RSN_AKM_EAP_FILS_SHA256: 2003 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); 2004 break; 2005 case RSN_AKM_EAP_FILS_SHA384: 2006 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); 2007 break; 2008 case RSN_AKM_DPP: 2009 rsnKeyManagement.add(ScanResult.KEY_MGMT_DPP); 2010 break; 2011 default: { 2012 int akmScheme = 2013 getScanResultAkmSchemeOfUnknownAkmIfConfigured( 2014 akm, unknownAkmMap); 2015 rsnKeyManagement.add(akmScheme); 2016 break; 2017 } 2018 } 2019 } 2020 // Default AKM 2021 if (rsnKeyManagement.isEmpty()) { 2022 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 2023 } 2024 keyManagement.add(rsnKeyManagement); 2025 2026 // RSN capabilities (optional), 2027 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016 2028 if (buf.remaining() < 2) return; 2029 int rsnCaps = buf.getShort(); 2030 isManagementFrameProtectionRequired = 2031 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps); 2032 isManagementFrameProtectionCapable = 2033 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps); 2034 2035 if (buf.remaining() < 2) return; 2036 // PMKID, it's not used, drop it if exists (optional). 2037 int rsnPmkIdCount = buf.getShort(); 2038 for (int i = 0; i < rsnPmkIdCount; i++) { 2039 // Each PMKID element length in the PMKID List is 16 bytes 2040 byte[] tmp = new byte[16]; 2041 buf.get(tmp); 2042 } 2043 2044 // Group management cipher suite (optional). 2045 if (buf.remaining() < 4) return; 2046 groupManagementCipher.add(parseRsnCipher(buf.getInt())); 2047 } catch (BufferUnderflowException e) { 2048 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow"); 2049 } 2050 } 2051 2052 /** 2053 * Get the ScanResult security key management scheme (ScanResult.KEY_MGMT_XX) corresponding 2054 * to the unknown AKMs configured in overlay config item 2055 * config_wifiUnknownAkmToKnownAkmMapping 2056 * 2057 * @param unknownAkm unknown AKM seen in the received beacon or probe response. 2058 * @param unknownAkmMap unknownAkmMap Mapping of unknown AKMs configured in overlay config 2059 * item config_wifiUnknownAkmToKnownAkmMapping to ScanResult security key management 2060 * scheme (ScanResult.KEY_MGMT_XX). 2061 * @return A valid ScanResult.KEY_MGMT_XX if unknownAkm is configured in the overlay, 2062 * ScanResult.KEY_MGMT_UNKNOWN otherwise 2063 */ getScanResultAkmSchemeOfUnknownAkmIfConfigured( int unknownAkm, SparseIntArray unknownAkmMap)2064 private int getScanResultAkmSchemeOfUnknownAkmIfConfigured( 2065 int unknownAkm, SparseIntArray unknownAkmMap) { 2066 if (unknownAkmMap != null) { 2067 return unknownAkmMap.get(unknownAkm, ScanResult.KEY_MGMT_UNKNOWN); 2068 } else { 2069 return ScanResult.KEY_MGMT_UNKNOWN; 2070 } 2071 } 2072 parseWpaCipher(int cipher)2073 private static @Cipher int parseWpaCipher(int cipher) { 2074 switch (cipher) { 2075 case WPA_CIPHER_NONE: 2076 return ScanResult.CIPHER_NONE; 2077 case WPA_CIPHER_TKIP: 2078 return ScanResult.CIPHER_TKIP; 2079 case WPA_CIPHER_CCMP: 2080 return ScanResult.CIPHER_CCMP; 2081 default: 2082 Log.w("IE_Capabilities", "Unknown WPA cipher suite: " 2083 + Integer.toHexString(cipher)); 2084 return ScanResult.CIPHER_NONE; 2085 } 2086 } 2087 parseRsnCipher(int cipher)2088 private static @Cipher int parseRsnCipher(int cipher) { 2089 switch (cipher) { 2090 case RSN_CIPHER_NONE: 2091 return ScanResult.CIPHER_NONE; 2092 case RSN_CIPHER_TKIP: 2093 return ScanResult.CIPHER_TKIP; 2094 case RSN_CIPHER_CCMP: 2095 return ScanResult.CIPHER_CCMP; 2096 case RSN_CIPHER_GCMP_256: 2097 return ScanResult.CIPHER_GCMP_256; 2098 case RSN_CIPHER_NO_GROUP_ADDRESSED: 2099 return ScanResult.CIPHER_NO_GROUP_ADDRESSED; 2100 case RSN_CIPHER_GCMP_128: 2101 return ScanResult.CIPHER_GCMP_128; 2102 case RSN_CIPHER_BIP_GMAC_128: 2103 return ScanResult.CIPHER_BIP_GMAC_128; 2104 case RSN_CIPHER_BIP_GMAC_256: 2105 return ScanResult.CIPHER_BIP_GMAC_256; 2106 case RSN_CIPHER_BIP_CMAC_256: 2107 return ScanResult.CIPHER_BIP_CMAC_256; 2108 default: 2109 Log.w("IE_Capabilities", "Unknown RSN cipher suite: " 2110 + Integer.toHexString(cipher)); 2111 return ScanResult.CIPHER_NONE; 2112 } 2113 } 2114 isWpsElement(InformationElement ie)2115 private static boolean isWpsElement(InformationElement ie) { 2116 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2117 try { 2118 // WPS OUI and type 2119 return (buf.getInt() == WPS_VENDOR_OUI_TYPE); 2120 } catch (BufferUnderflowException e) { 2121 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 2122 return false; 2123 } 2124 } 2125 isWpaOneElement(InformationElement ie)2126 private static boolean isWpaOneElement(InformationElement ie) { 2127 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2128 2129 try { 2130 // WPA OUI and type 2131 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE); 2132 } catch (BufferUnderflowException e) { 2133 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 2134 return false; 2135 } 2136 } 2137 2138 // WPA type 1 format (size unit: byte) 2139 // 2140 // | Element ID | Length | OUI | Type | Version | 2141 // 1 1 3 1 2 2142 // | Group Data Cipher Suite | 2143 // 4 2144 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 2145 // 2 4 * m 2146 // | AKM Suite Count | AKM Suite List | 2147 // 2 4 * n 2148 // 2149 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2150 // stripped off already 2151 // parseWpaOneElement(InformationElement ie, SparseIntArray unknownAkmMap)2152 private void parseWpaOneElement(InformationElement ie, SparseIntArray unknownAkmMap) { 2153 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2154 2155 try { 2156 // skip WPA OUI and type parsing. isWpaOneElement() should have 2157 // been called for verification before we reach here. 2158 buf.getInt(); 2159 2160 // version 2161 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) { 2162 // incorrect version 2163 return; 2164 } 2165 2166 // start building the string 2167 protocol.add(ScanResult.PROTOCOL_WPA); 2168 2169 // group data cipher suite 2170 groupCipher.add(parseWpaCipher(buf.getInt())); 2171 2172 // pairwise cipher suite count 2173 short cipherCount = buf.getShort(); 2174 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>(); 2175 // pairwise chipher suite list 2176 for (int i = 0; i < cipherCount; i++) { 2177 wpaPairwiseCipher.add(parseWpaCipher(buf.getInt())); 2178 } 2179 pairwiseCipher.add(wpaPairwiseCipher); 2180 2181 // AKM 2182 // AKM suite count 2183 short akmCount = buf.getShort(); 2184 ArrayList<Integer> wpaKeyManagement = new ArrayList<>(); 2185 2186 // AKM suite list 2187 for (int i = 0; i < akmCount; i++) { 2188 int akm = buf.getInt(); 2189 switch (akm) { 2190 case WPA_AKM_EAP: 2191 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 2192 break; 2193 case WPA_AKM_PSK: 2194 wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK); 2195 break; 2196 default: 2197 int akmScheme = 2198 getScanResultAkmSchemeOfUnknownAkmIfConfigured( 2199 akm, unknownAkmMap); 2200 wpaKeyManagement.add(akmScheme); 2201 break; 2202 } 2203 } 2204 // Default AKM 2205 if (wpaKeyManagement.isEmpty()) { 2206 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 2207 } 2208 keyManagement.add(wpaKeyManagement); 2209 } catch (BufferUnderflowException e) { 2210 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow"); 2211 } 2212 } 2213 2214 /** 2215 * Parse the Information Element and the 16-bit Capability Information field to build the 2216 * InformationElemmentUtil.capabilities object. 2217 * 2218 * @param ies -- Information Element array 2219 * @param beaconCap -- 16-bit Beacon Capability Information field 2220 * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device 2221 * @param freq -- Frequency on which frame/beacon was transmitted. Some parsing may be 2222 * affected such as DMG parameters in DMG (60GHz) beacon. 2223 * @param unknownAkmMap -- unknown AKM to known AKM mapping (Internally converted to 2224 * security key management scheme(ScanResult.KEY_MGMT_XX)) configured in overlay config 2225 * item config_wifiUnknownAkmToKnownAkmMapping. 2226 */ from( InformationElement[] ies, int beaconCap, boolean isOweSupported, int freq, SparseIntArray unknownAkmMap)2227 public void from( 2228 InformationElement[] ies, 2229 int beaconCap, 2230 boolean isOweSupported, 2231 int freq, 2232 SparseIntArray unknownAkmMap) { 2233 protocol = new ArrayList<>(); 2234 keyManagement = new ArrayList<>(); 2235 groupCipher = new ArrayList<>(); 2236 pairwiseCipher = new ArrayList<>(); 2237 groupManagementCipher = new ArrayList<>(); 2238 2239 if (ies == null) { 2240 return; 2241 } 2242 isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0; 2243 if (ScanResult.is60GHz(freq)) { 2244 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */ 2245 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS) 2246 == NativeScanResult.BSS_CAPABILITY_DMG_ESS) { 2247 isESS = true; 2248 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) { 2249 isIBSS = true; 2250 } 2251 } else { 2252 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0; 2253 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0; 2254 } 2255 for (InformationElement ie : ies) { 2256 WifiNl80211Manager.OemSecurityType oemSecurityType = 2257 WifiNl80211Manager.parseOemSecurityTypeElement( 2258 ie.id, ie.idExt, ie.bytes); 2259 if (oemSecurityType != null 2260 && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) { 2261 protocol.add(oemSecurityType.protocol); 2262 keyManagement.add(oemSecurityType.keyManagement); 2263 pairwiseCipher.add(oemSecurityType.pairwiseCipher); 2264 groupCipher.add(oemSecurityType.groupCipher); 2265 } 2266 2267 if (ie.id == InformationElement.EID_RSN) { 2268 parseRsnElement(ie, unknownAkmMap); 2269 } 2270 2271 if (ie.id == InformationElement.EID_VSA) { 2272 if (isWpaOneElement(ie)) { 2273 parseWpaOneElement(ie, unknownAkmMap); 2274 } 2275 if (isWpsElement(ie)) { 2276 // TODO(b/62134557): parse WPS IE to provide finer granularity information. 2277 isWPS = true; 2278 } 2279 if (isOweSupported && isOweElement(ie)) { 2280 /* From RFC 8110: Once the client and AP have finished 802.11 association, 2281 they then complete the Diffie-Hellman key exchange and create a Pairwise 2282 Master Key (PMK) and its associated identifier, PMKID [IEEE802.11]. 2283 Upon completion of 802.11 association, the AP initiates the 4-way 2284 handshake to the client using the PMK generated above. The 4-way 2285 handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation 2286 Key (KCK), and a Message Integrity Code (MIC) to use for protection 2287 of the frames that define the 4-way handshake. 2288 2289 We check if OWE is supported here because we are adding the OWE 2290 capabilities to the Open BSS. Non-supporting devices need to see this 2291 open network and ignore this element. Supporting devices need to hide 2292 the Open BSS of OWE in transition mode and connect to the Hidden one. 2293 */ 2294 protocol.add(ScanResult.PROTOCOL_RSN); 2295 groupCipher.add(ScanResult.CIPHER_CCMP); 2296 ArrayList<Integer> owePairwiseCipher = new ArrayList<>(); 2297 owePairwiseCipher.add(ScanResult.CIPHER_CCMP); 2298 pairwiseCipher.add(owePairwiseCipher); 2299 ArrayList<Integer> oweKeyManagement = new ArrayList<>(); 2300 oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION); 2301 keyManagement.add(oweKeyManagement); 2302 } 2303 } 2304 } 2305 } 2306 2307 /** Convert the AKM suite selector to scan result Security key management scheme */ akmToScanResultKeyManagementScheme(int akm)2308 public static int akmToScanResultKeyManagementScheme(int akm) { 2309 switch (akm) { 2310 case RSN_AKM_EAP: 2311 case WPA_AKM_EAP: 2312 return ScanResult.KEY_MGMT_EAP; 2313 case RSN_AKM_PSK: 2314 case WPA_AKM_PSK: 2315 return ScanResult.KEY_MGMT_PSK; 2316 case RSN_AKM_FT_EAP: 2317 return ScanResult.KEY_MGMT_FT_EAP; 2318 case RSN_AKM_FT_PSK: 2319 return ScanResult.KEY_MGMT_FT_PSK; 2320 case RSN_AKM_EAP_SHA256: 2321 return ScanResult.KEY_MGMT_EAP_SHA256; 2322 case RSN_AKM_PSK_SHA256: 2323 return ScanResult.KEY_MGMT_PSK_SHA256; 2324 case RSN_AKM_SAE: 2325 return ScanResult.KEY_MGMT_SAE; 2326 case RSN_AKM_FT_SAE: 2327 return ScanResult.KEY_MGMT_FT_SAE; 2328 case RSN_AKM_SAE_EXT_KEY: 2329 return ScanResult.KEY_MGMT_SAE_EXT_KEY; 2330 case RSN_AKM_FT_SAE_EXT_KEY: 2331 return ScanResult.KEY_MGMT_FT_SAE_EXT_KEY; 2332 case RSN_AKM_OWE: 2333 return ScanResult.KEY_MGMT_OWE; 2334 case RSN_AKM_EAP_SUITE_B_192: 2335 return ScanResult.KEY_MGMT_EAP_SUITE_B_192; 2336 case RSN_OSEN: 2337 return ScanResult.KEY_MGMT_OSEN; 2338 case RSN_AKM_EAP_FILS_SHA256: 2339 return ScanResult.KEY_MGMT_FILS_SHA256; 2340 case RSN_AKM_EAP_FILS_SHA384: 2341 return ScanResult.KEY_MGMT_FILS_SHA384; 2342 case RSN_AKM_DPP: 2343 return ScanResult.KEY_MGMT_DPP; 2344 default: 2345 return ScanResult.KEY_MGMT_UNKNOWN; 2346 } 2347 } 2348 isOweElement(InformationElement ie)2349 private static boolean isOweElement(InformationElement ie) { 2350 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2351 try { 2352 // OWE OUI and type 2353 return (buf.getInt() == OWE_VENDOR_OUI_TYPE); 2354 } catch (BufferUnderflowException e) { 2355 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 2356 return false; 2357 } 2358 } 2359 protocolToString(@rotocol int protocol)2360 private String protocolToString(@Protocol int protocol) { 2361 switch (protocol) { 2362 case ScanResult.PROTOCOL_NONE: 2363 return "None"; 2364 case ScanResult.PROTOCOL_WPA: 2365 return "WPA"; 2366 case ScanResult.PROTOCOL_RSN: 2367 return "RSN"; 2368 case ScanResult.PROTOCOL_OSEN: 2369 return "OSEN"; 2370 case ScanResult.PROTOCOL_WAPI: 2371 return "WAPI"; 2372 default: 2373 return "?"; 2374 } 2375 } 2376 keyManagementToString(@eyMgmt int akm)2377 private String keyManagementToString(@KeyMgmt int akm) { 2378 switch (akm) { 2379 case ScanResult.KEY_MGMT_NONE: 2380 return "None"; 2381 case ScanResult.KEY_MGMT_PSK: 2382 return "PSK"; 2383 case ScanResult.KEY_MGMT_EAP: 2384 return "EAP/SHA1"; 2385 case ScanResult.KEY_MGMT_FT_EAP: 2386 return "FT/EAP"; 2387 case ScanResult.KEY_MGMT_FT_PSK: 2388 return "FT/PSK"; 2389 case ScanResult.KEY_MGMT_EAP_SHA256: 2390 return "EAP/SHA256"; 2391 case ScanResult.KEY_MGMT_PSK_SHA256: 2392 return "PSK-SHA256"; 2393 case ScanResult.KEY_MGMT_OWE: 2394 return "OWE"; 2395 case ScanResult.KEY_MGMT_OWE_TRANSITION: 2396 return "OWE_TRANSITION"; 2397 case ScanResult.KEY_MGMT_SAE: 2398 return "SAE"; 2399 case ScanResult.KEY_MGMT_FT_SAE: 2400 return "FT/SAE"; 2401 case ScanResult.KEY_MGMT_SAE_EXT_KEY: 2402 return "SAE_EXT_KEY"; 2403 case ScanResult.KEY_MGMT_FT_SAE_EXT_KEY: 2404 return "FT/SAE_EXT_KEY"; 2405 case ScanResult.KEY_MGMT_EAP_SUITE_B_192: 2406 return "EAP_SUITE_B_192"; 2407 case ScanResult.KEY_MGMT_OSEN: 2408 return "OSEN"; 2409 case ScanResult.KEY_MGMT_WAPI_PSK: 2410 return "WAPI-PSK"; 2411 case ScanResult.KEY_MGMT_WAPI_CERT: 2412 return "WAPI-CERT"; 2413 case ScanResult.KEY_MGMT_FILS_SHA256: 2414 return "EAP-FILS-SHA256"; 2415 case ScanResult.KEY_MGMT_FILS_SHA384: 2416 return "EAP-FILS-SHA384"; 2417 case ScanResult.KEY_MGMT_DPP: 2418 return "DPP"; 2419 default: 2420 return "?"; 2421 } 2422 } 2423 cipherToString(@ipher int cipher)2424 private String cipherToString(@Cipher int cipher) { 2425 switch (cipher) { 2426 case ScanResult.CIPHER_NONE: 2427 return "None"; 2428 case ScanResult.CIPHER_CCMP: 2429 return "CCMP"; 2430 case ScanResult.CIPHER_GCMP_256: 2431 return "GCMP-256"; 2432 case ScanResult.CIPHER_TKIP: 2433 return "TKIP"; 2434 case ScanResult.CIPHER_SMS4: 2435 return "SMS4"; 2436 default: 2437 return "?"; 2438 } 2439 } 2440 2441 /** 2442 * Build the ScanResult.capabilities String. 2443 * 2444 * @return security string that mirrors what wpa_supplicant generates 2445 */ generateCapabilitiesString()2446 public String generateCapabilitiesString() { 2447 StringBuilder capabilities = new StringBuilder(); 2448 // private Beacon without an RSNE or WPA IE, hence WEP0 2449 boolean isWEP = (protocol.isEmpty()) && isPrivacy; 2450 2451 if (isWEP) { 2452 capabilities.append("[WEP]"); 2453 } 2454 for (int i = 0; i < protocol.size(); i++) { 2455 String capability = generateCapabilitiesStringPerProtocol(i); 2456 // add duplicate capabilities for WPA2 for backward compatibility: 2457 // duplicate "RSN" entries as "WPA2" 2458 String capWpa2 = generateWPA2CapabilitiesString(capability, i); 2459 capabilities.append(capWpa2); 2460 capabilities.append(capability); 2461 } 2462 if (isESS) { 2463 capabilities.append("[ESS]"); 2464 } 2465 if (isIBSS) { 2466 capabilities.append("[IBSS]"); 2467 } 2468 if (isWPS) { 2469 capabilities.append("[WPS]"); 2470 } 2471 if (isManagementFrameProtectionRequired) { 2472 capabilities.append("[MFPR]"); 2473 } 2474 if (isManagementFrameProtectionCapable) { 2475 capabilities.append("[MFPC]"); 2476 } 2477 2478 return capabilities.toString(); 2479 } 2480 2481 /** 2482 * Build the Capability String for one protocol 2483 * @param index: index number of the protocol 2484 * @return security string for one protocol 2485 */ generateCapabilitiesStringPerProtocol(int index)2486 private String generateCapabilitiesStringPerProtocol(int index) { 2487 StringBuilder capability = new StringBuilder(); 2488 capability.append("[").append(protocolToString(protocol.get(index))); 2489 2490 if (index < keyManagement.size()) { 2491 for (int j = 0; j < keyManagement.get(index).size(); j++) { 2492 capability.append((j == 0) ? "-" : "+").append( 2493 keyManagementToString(keyManagement.get(index).get(j))); 2494 } 2495 } 2496 if (index < pairwiseCipher.size()) { 2497 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 2498 capability.append((j == 0) ? "-" : "+").append( 2499 cipherToString(pairwiseCipher.get(index).get(j))); 2500 } 2501 } 2502 capability.append("]"); 2503 return capability.toString(); 2504 } 2505 2506 /** 2507 * Build the duplicate Capability String for WPA2 2508 * @param cap: original capability String 2509 * @param index: index number of the protocol 2510 * @return security string for WPA2, empty String if protocol is not WPA2 2511 */ generateWPA2CapabilitiesString(String cap, int index)2512 private String generateWPA2CapabilitiesString(String cap, int index) { 2513 StringBuilder capWpa2 = new StringBuilder(); 2514 // if not WPA2, return empty String 2515 if (cap.contains("EAP_SUITE_B_192") 2516 || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP") 2517 && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) { 2518 return ""; 2519 } 2520 capWpa2.append("[").append("WPA2"); 2521 if (index < keyManagement.size()) { 2522 for (int j = 0; j < keyManagement.get(index).size(); j++) { 2523 capWpa2.append((j == 0) ? "-" : "+").append( 2524 keyManagementToString(keyManagement.get(index).get(j))); 2525 // WPA3/WPA2 transition mode 2526 if (cap.contains("SAE")) { 2527 break; 2528 } 2529 } 2530 } 2531 if (index < pairwiseCipher.size()) { 2532 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 2533 capWpa2.append((j == 0) ? "-" : "+").append( 2534 cipherToString(pairwiseCipher.get(index).get(j))); 2535 } 2536 } 2537 capWpa2.append("]"); 2538 return capWpa2.toString(); 2539 } 2540 } 2541 2542 2543 /** 2544 * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will 2545 * only be present in scan results that are derived from a Beacon Frame, not from the more 2546 * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct. 2547 */ 2548 public static class TrafficIndicationMap { 2549 private static final int MAX_TIM_LENGTH = 254; 2550 private boolean mValid = false; 2551 public int mLength = 0; 2552 public int mDtimCount = -1; 2553 //Negative DTIM Period means no TIM element was given this frame. 2554 public int mDtimPeriod = -1; 2555 public int mBitmapControl = 0; 2556 2557 /** 2558 * Is this a valid TIM information element. 2559 */ isValid()2560 public boolean isValid() { 2561 return mValid; 2562 } 2563 2564 // Traffic Indication Map format (size unit: byte) 2565 // 2566 //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap | 2567 // 1 1 1 1 1 1 - 251 2568 // 2569 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2570 // stripped off already 2571 // from(InformationElement ie)2572 public void from(InformationElement ie) { 2573 mValid = false; 2574 if (ie == null || ie.bytes == null) return; 2575 mLength = ie.bytes.length; 2576 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2577 try { 2578 mDtimCount = data.get() & Constants.BYTE_MASK; 2579 mDtimPeriod = data.get() & Constants.BYTE_MASK; 2580 mBitmapControl = data.get() & Constants.BYTE_MASK; 2581 //A valid TIM element must have atleast one more byte 2582 data.get(); 2583 } catch (BufferUnderflowException e) { 2584 return; 2585 } 2586 if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { 2587 mValid = true; 2588 } 2589 } 2590 } 2591 2592 /** 2593 * This util class determines the 802.11 standard (a/b/g/n/ac/ax/be) being used 2594 */ 2595 public static class WifiMode { 2596 public static final int MODE_UNDEFINED = 0; // Unknown/undefined 2597 public static final int MODE_11A = 1; // 802.11a 2598 public static final int MODE_11B = 2; // 802.11b 2599 public static final int MODE_11G = 3; // 802.11g 2600 public static final int MODE_11N = 4; // 802.11n 2601 public static final int MODE_11AC = 5; // 802.11ac 2602 public static final int MODE_11AX = 6; // 802.11ax 2603 public static final int MODE_11BE = 7; // 802.11be 2604 //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A 2605 2606 /** 2607 * Use frequency, max supported rate, and the existence of EHT, HE, VHT, HT & ERP fields in 2608 * scan result to determine the 802.11 Wifi standard being used. 2609 */ determineMode(int frequency, int maxRate, boolean foundEht, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)2610 public static int determineMode(int frequency, int maxRate, boolean foundEht, 2611 boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp) { 2612 if (foundEht) { 2613 return MODE_11BE; 2614 } else if (foundHe) { 2615 return MODE_11AX; 2616 } else if (!ScanResult.is24GHz(frequency) && foundVht) { 2617 // Do not include subset of VHT on 2.4 GHz vendor extension 2618 // in consideration for reporting VHT. 2619 return MODE_11AC; 2620 } else if (foundHt) { 2621 return MODE_11N; 2622 } else if (foundErp) { 2623 return MODE_11G; 2624 } else if (ScanResult.is24GHz(frequency)) { 2625 if (maxRate < 24000000) { 2626 return MODE_11B; 2627 } else { 2628 return MODE_11G; 2629 } 2630 } else { 2631 return MODE_11A; 2632 } 2633 } 2634 2635 /** 2636 * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC/AX/BE> 2637 */ toString(int mode)2638 public static String toString(int mode) { 2639 switch(mode) { 2640 case MODE_11A: 2641 return "MODE_11A"; 2642 case MODE_11B: 2643 return "MODE_11B"; 2644 case MODE_11G: 2645 return "MODE_11G"; 2646 case MODE_11N: 2647 return "MODE_11N"; 2648 case MODE_11AC: 2649 return "MODE_11AC"; 2650 case MODE_11AX: 2651 return "MODE_11AX"; 2652 case MODE_11BE: 2653 return "MODE_11BE"; 2654 default: 2655 return "MODE_UNDEFINED"; 2656 } 2657 } 2658 } 2659 2660 /** 2661 * Parser for both the Supported Rates & Extended Supported Rates Information Elements 2662 */ 2663 public static class SupportedRates { 2664 public static final int MASK = 0x7F; // 0111 1111 2665 public boolean mValid = false; 2666 public ArrayList<Integer> mRates; 2667 SupportedRates()2668 public SupportedRates() { 2669 mRates = new ArrayList<Integer>(); 2670 } 2671 2672 /** 2673 * Is this a valid Supported Rates information element. 2674 */ isValid()2675 public boolean isValid() { 2676 return mValid; 2677 } 2678 2679 /** 2680 * get the Rate in bits/s from associated byteval 2681 */ getRateFromByte(int byteVal)2682 public static int getRateFromByte(int byteVal) { 2683 byteVal &= MASK; 2684 switch(byteVal) { 2685 case 2: 2686 return 1000000; 2687 case 4: 2688 return 2000000; 2689 case 11: 2690 return 5500000; 2691 case 12: 2692 return 6000000; 2693 case 18: 2694 return 9000000; 2695 case 22: 2696 return 11000000; 2697 case 24: 2698 return 12000000; 2699 case 36: 2700 return 18000000; 2701 case 44: 2702 return 22000000; 2703 case 48: 2704 return 24000000; 2705 case 66: 2706 return 33000000; 2707 case 72: 2708 return 36000000; 2709 case 96: 2710 return 48000000; 2711 case 108: 2712 return 54000000; 2713 default: 2714 //ERROR UNKNOWN RATE 2715 return -1; 2716 } 2717 } 2718 2719 // Supported Rates format (size unit: byte) 2720 // 2721 //| ElementID | Length | Supported Rates [7 Little Endian Info bits - 1 Flag bit] 2722 // 1 1 1 - 8 2723 // 2724 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2725 // stripped off already 2726 // from(InformationElement ie)2727 public void from(InformationElement ie) { 2728 mValid = false; 2729 if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1) { 2730 return; 2731 } 2732 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2733 try { 2734 for (int i = 0; i < ie.bytes.length; i++) { 2735 int rate = getRateFromByte(data.get()); 2736 if (rate > 0) { 2737 mRates.add(rate); 2738 } else { 2739 return; 2740 } 2741 } 2742 } catch (BufferUnderflowException e) { 2743 return; 2744 } 2745 mValid = true; 2746 return; 2747 } 2748 2749 /** 2750 * Lists the rates in a human readable string 2751 */ toString()2752 public String toString() { 2753 StringBuilder sbuf = new StringBuilder(); 2754 for (Integer rate : mRates) { 2755 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", "); 2756 } 2757 return sbuf.toString(); 2758 } 2759 } 2760 2761 /** 2762 * This util class determines country related information in beacon/probe response 2763 */ 2764 public static class Country { 2765 private boolean mValid = false; 2766 public String mCountryCode = "00"; 2767 2768 /** 2769 * Parse the Information Element Country Information field. Note that element ID and length 2770 * fields are already removed. 2771 * 2772 * Country IE format (size unit: byte) 2773 * 2774 * ElementID | Length | country string | triplet | padding 2775 * 1 1 3 Q*x 0 or 1 2776 * First two bytes of country string are country code 2777 * See 802.11 spec dot11CountryString definition. 2778 */ from(InformationElement ie)2779 public void from(InformationElement ie) { 2780 mValid = false; 2781 if (ie == null || ie.bytes == null || ie.bytes.length < 3) return; 2782 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2783 try { 2784 char letter1 = (char) (data.get() & Constants.BYTE_MASK); 2785 char letter2 = (char) (data.get() & Constants.BYTE_MASK); 2786 char letter3 = (char) (data.get() & Constants.BYTE_MASK); 2787 // See 802.11 spec dot11CountryString definition. 2788 // ' ', 'O', 'I' are for all operation, outdoor, indoor environments, respectively. 2789 mValid = (letter3 == ' ' || letter3 == 'O' || letter3 == 'I') 2790 && Character.isLetterOrDigit((int) letter1) 2791 && Character.isLetterOrDigit((int) letter2); 2792 if (mValid) { 2793 mCountryCode = (String.valueOf(letter1) + letter2).toUpperCase(Locale.US); 2794 } 2795 } catch (BufferUnderflowException e) { 2796 return; 2797 } 2798 } 2799 2800 /** 2801 * Is this a valid country information element. 2802 */ isValid()2803 public boolean isValid() { 2804 return mValid; 2805 } 2806 2807 /** 2808 * @return country code indicated in beacon/probe response frames 2809 */ getCountryCode()2810 public String getCountryCode() { 2811 return mCountryCode; 2812 } 2813 } 2814 } 2815