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.net.MacAddress; 19 import android.net.wifi.MloLink; 20 import android.net.wifi.ScanResult; 21 import android.net.wifi.ScanResult.InformationElement; 22 import android.net.wifi.WifiAnnotations.Cipher; 23 import android.net.wifi.WifiAnnotations.KeyMgmt; 24 import android.net.wifi.WifiAnnotations.Protocol; 25 import android.net.wifi.WifiScanner; 26 import android.net.wifi.nl80211.NativeScanResult; 27 import android.net.wifi.nl80211.WifiNl80211Manager; 28 import android.net.wifi.util.HexEncoding; 29 import android.util.Log; 30 31 import com.android.server.wifi.ByteBufferReader; 32 import com.android.server.wifi.MboOceConstants; 33 import com.android.server.wifi.hotspot2.NetworkDetail; 34 import com.android.server.wifi.hotspot2.anqp.Constants; 35 36 import java.nio.BufferUnderflowException; 37 import java.nio.ByteBuffer; 38 import java.nio.ByteOrder; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.BitSet; 42 import java.util.List; 43 import java.util.Locale; 44 45 public class InformationElementUtil { 46 private static final String TAG = "InformationElementUtil"; 47 private static final boolean DBG = false; 48 49 /** Converts InformationElement to hex string */ toHexString(InformationElement e)50 public static String toHexString(InformationElement e) { 51 StringBuilder sb = new StringBuilder(); 52 sb.append(HexEncoding.encode(new byte[]{(byte) e.id})); 53 if (e.id == InformationElement.EID_EXTENSION_PRESENT) { 54 sb.append(HexEncoding.encode(new byte[]{(byte) e.idExt})); 55 } 56 sb.append(HexEncoding.encode(new byte[]{(byte) e.bytes.length})); 57 sb.append(HexEncoding.encode(e.bytes)); 58 return sb.toString(); 59 } 60 61 /** Parses information elements from hex string */ parseInformationElements(String data)62 public static InformationElement[] parseInformationElements(String data) { 63 if (data == null) { 64 return new InformationElement[0]; 65 } 66 return parseInformationElements(HexEncoding.decode(data)); 67 } 68 parseInformationElements(byte[] bytes)69 public static InformationElement[] parseInformationElements(byte[] bytes) { 70 if (bytes == null) { 71 return new InformationElement[0]; 72 } 73 ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 74 75 ArrayList<InformationElement> infoElements = new ArrayList<>(); 76 boolean found_ssid = false; 77 while (data.remaining() > 1) { 78 int eid = data.get() & Constants.BYTE_MASK; 79 int eidExt = 0; 80 int elementLength = data.get() & Constants.BYTE_MASK; 81 82 if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID 83 && found_ssid)) { 84 // APs often pad the data with bytes that happen to match that of the EID_SSID 85 // marker. This is not due to a known issue for APs to incorrectly send the SSID 86 // name multiple times. 87 break; 88 } 89 if (eid == InformationElement.EID_SSID) { 90 found_ssid = true; 91 } else if (eid == InformationElement.EID_EXTENSION_PRESENT) { 92 if (elementLength == 0) { 93 // Malformed IE, skipping 94 break; 95 } 96 eidExt = data.get() & Constants.BYTE_MASK; 97 elementLength--; 98 } 99 100 InformationElement ie = new InformationElement(); 101 ie.id = eid; 102 ie.idExt = eidExt; 103 ie.bytes = new byte[elementLength]; 104 data.get(ie.bytes); 105 infoElements.add(ie); 106 } 107 return infoElements.toArray(new InformationElement[infoElements.size()]); 108 } 109 110 /** 111 * Parse and retrieve the Roaming Consortium Information Element from the list of IEs. 112 * 113 * @param ies List of IEs to retrieve from 114 * @return {@link RoamingConsortium} 115 */ getRoamingConsortiumIE(InformationElement[] ies)116 public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) { 117 RoamingConsortium roamingConsortium = new RoamingConsortium(); 118 if (ies != null) { 119 for (InformationElement ie : ies) { 120 if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) { 121 try { 122 roamingConsortium.from(ie); 123 } catch (RuntimeException e) { 124 Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage()); 125 } 126 } 127 } 128 } 129 return roamingConsortium; 130 } 131 132 /** 133 * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs. 134 * 135 * @param ies List of IEs to retrieve from 136 * @return {@link Vsa} 137 */ getHS2VendorSpecificIE(InformationElement[] ies)138 public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) { 139 Vsa vsa = new Vsa(); 140 if (ies != null) { 141 for (InformationElement ie : ies) { 142 if (ie.id == InformationElement.EID_VSA) { 143 try { 144 vsa.from(ie); 145 } catch (RuntimeException e) { 146 Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage()); 147 } 148 } 149 } 150 } 151 return vsa; 152 } 153 154 /** 155 * Parse and retrieve all Vendor Specific Information Elements from the list of IEs. 156 * 157 * @param ies List of IEs to retrieve from 158 * @return List of {@link Vsa} 159 */ getVendorSpecificIE(InformationElement[] ies)160 public static List<Vsa> getVendorSpecificIE(InformationElement[] ies) { 161 List<Vsa> vsas = new ArrayList<>(); 162 if (ies != null) { 163 for (InformationElement ie : ies) { 164 if (ie.id == InformationElement.EID_VSA) { 165 try { 166 Vsa vsa = new Vsa(); 167 vsa.from(ie); 168 vsas.add(vsa); 169 } catch (RuntimeException e) { 170 Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage()); 171 } 172 } 173 } 174 } 175 return vsas; 176 } 177 178 /** 179 * Parse and retrieve the Interworking information element from the list of IEs. 180 * 181 * @param ies List of IEs to retrieve from 182 * @return {@link Interworking} 183 */ getInterworkingIE(InformationElement[] ies)184 public static Interworking getInterworkingIE(InformationElement[] ies) { 185 Interworking interworking = new Interworking(); 186 if (ies != null) { 187 for (InformationElement ie : ies) { 188 if (ie.id == InformationElement.EID_INTERWORKING) { 189 try { 190 interworking.from(ie); 191 } catch (RuntimeException e) { 192 Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage()); 193 } 194 } 195 } 196 } 197 return interworking; 198 } 199 200 public static class BssLoad { 201 public static final int INVALID = -1; 202 public static final int MAX_CHANNEL_UTILIZATION = 255; 203 public static final int MIN_CHANNEL_UTILIZATION = 0; 204 public static final int CHANNEL_UTILIZATION_SCALE = 256; 205 public int stationCount = INVALID; 206 public int channelUtilization = INVALID; 207 public int capacity = INVALID; 208 from(InformationElement ie)209 public void from(InformationElement ie) { 210 if (ie.id != InformationElement.EID_BSS_LOAD) { 211 throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id); 212 } 213 if (ie.bytes.length != 5) { 214 throw new IllegalArgumentException("BSS Load element length is not 5: " 215 + ie.bytes.length); 216 } 217 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 218 stationCount = data.getShort() & Constants.SHORT_MASK; 219 channelUtilization = data.get() & Constants.BYTE_MASK; 220 capacity = data.getShort() & Constants.SHORT_MASK; 221 } 222 } 223 224 /** 225 * Rnr: represents the Reduced Neighbor Report (RNR) IE 226 * As described by IEEE 802.11 Specification Section 9.4.2.170 227 */ 228 public static class Rnr { 229 private static final int TBTT_INFO_COUNT_OFFSET = 0; 230 private static final int TBTT_INFO_COUNT_MASK = 0xF0; 231 private static final int TBTT_INFO_COUNT_SHIFT = 4; 232 private static final int TBTT_INFO_LENGTH_OFFSET = 1; 233 private static final int TBTT_INFO_OP_CLASS_OFFSET = 2; 234 private static final int TBTT_INFO_CHANNEL_OFFSET = 3; 235 private static final int TBTT_INFO_SET_START_OFFSET = 4; 236 private static final int MLD_ID_START_OFFSET = 0; 237 private static final int LINK_ID_START_OFFSET = 1; 238 private static final int LINK_ID_MASK = 0x0F; 239 240 private boolean mPresent = false; 241 private List<MloLink> mAffiliatedMloLinks = new ArrayList<>(); 242 243 /** 244 * Returns whether the RNR Information Element is present. 245 */ isPresent()246 public boolean isPresent() { 247 return mPresent; 248 } 249 250 /** 251 * Returns the list of the affiliated MLO links 252 */ getAffiliatedMloLinks()253 public List<MloLink> getAffiliatedMloLinks() { 254 return mAffiliatedMloLinks; 255 } 256 257 /** 258 * Parse RNR Operation IE 259 * 260 * RNR format as described in IEEE 802.11 specs, Section 9.4.2.170 261 * 262 * | ElementID | Length | Neighbor AP Information Fields | 263 * Octets: 1 1 variable 264 * 265 * 266 * Where Neighbor AP Information Fields is one or more Neighbor AP Information Field as, 267 * 268 * | Header | Operating Class | Channel | TBTT Information Set | 269 * Octets: 2 1 1 variable 270 * 271 * 272 * The Header subfield is described as follows, 273 * 274 * | Type | Filtered AP | Reserved | Count | Length | 275 * Bits: 2 1 1 4 8 276 * 277 * 278 * Information Set is one or more TBTT Information fields, which is described as, 279 * 280 * | Offset | BSSID | Short-SSID | BSS Params | 20MHz PSD | MLD Params| 281 * Octets: 1 0 or 6 0 or 4 0 or 1 0 or 1 0 or 3 282 * 283 * 284 * The MLD Params are described as, 285 * | MLD ID | Link ID | BSS Change Count | Reserved | 286 * Bits: 8 4 8 4 287 * 288 * Note: InformationElement.bytes has 'Element ID' and 'Length' 289 * stripped off already 290 * 291 */ from(InformationElement ie)292 public void from(InformationElement ie) { 293 if (ie.id != InformationElement.EID_RNR) { 294 throw new IllegalArgumentException("Element id is not RNR"); 295 } 296 297 int startOffset = 0; 298 while (ie.bytes.length > startOffset + TBTT_INFO_SET_START_OFFSET) { 299 int tbttInfoCount = 300 ie.bytes[startOffset + TBTT_INFO_COUNT_OFFSET] & TBTT_INFO_COUNT_MASK; 301 tbttInfoCount >>= TBTT_INFO_COUNT_SHIFT; 302 tbttInfoCount++; 303 304 int tbttInfoLen = 305 ie.bytes[startOffset + TBTT_INFO_LENGTH_OFFSET] & Constants.BYTE_MASK; 306 int tbttInfoStartOffset = startOffset + TBTT_INFO_SET_START_OFFSET; 307 308 // Only handle TBTT info with MLD Info 309 if (tbttInfoLen == 4 || tbttInfoLen >= 16) { 310 // Make sure length allows for this TBTT Info 311 if (ie.bytes.length < startOffset + TBTT_INFO_SET_START_OFFSET 312 + tbttInfoLen * tbttInfoCount) { 313 if (DBG) { 314 Log.w(TAG, "Invalid RNR len, not enough for TBTT Info: " 315 + ie.bytes.length + "/" + tbttInfoLen + "/" + tbttInfoCount); 316 } 317 // Skipping parsing of the IE 318 return; 319 } 320 321 int mldStartOffset; 322 int bssidOffset; 323 324 if (tbttInfoLen == 4) { 325 mldStartOffset = 1; 326 bssidOffset = -1; 327 } else { 328 mldStartOffset = 13; 329 bssidOffset = 1; 330 } 331 332 int opClass = ie.bytes[startOffset + TBTT_INFO_OP_CLASS_OFFSET] 333 & Constants.BYTE_MASK; 334 int channel = ie.bytes[startOffset + TBTT_INFO_CHANNEL_OFFSET] 335 & Constants.BYTE_MASK; 336 int band = ScanResult.getBandFromOpClass(opClass, channel); 337 if (band == WifiScanner.WIFI_BAND_UNSPECIFIED) { 338 if (DBG) { 339 Log.w(TAG, "Invalid op class/channel in RNR TBTT Info: " 340 + opClass + "/" + channel); 341 } 342 // Skipping parsing of the IE 343 return; 344 } 345 for (int i = 0; i < tbttInfoCount; i++) { 346 int mldId = ie.bytes[tbttInfoStartOffset + mldStartOffset 347 + MLD_ID_START_OFFSET] & Constants.BYTE_MASK; 348 if (mldId == 0) { 349 //This is an affiliated link 350 int linkId = ie.bytes[tbttInfoStartOffset + mldStartOffset 351 + LINK_ID_START_OFFSET] & LINK_ID_MASK; 352 MloLink link = new MloLink(); 353 link.setLinkId(linkId); 354 link.setBand(band); 355 link.setChannel(channel); 356 if (bssidOffset != -1) { 357 int macAddressStart = tbttInfoStartOffset + bssidOffset; 358 link.setApMacAddress(MacAddress.fromBytes( 359 Arrays.copyOfRange(ie.bytes, 360 macAddressStart, macAddressStart + 6))); 361 } 362 363 mAffiliatedMloLinks.add(link); 364 } 365 tbttInfoStartOffset += tbttInfoLen; 366 } 367 } 368 369 startOffset += TBTT_INFO_SET_START_OFFSET + (tbttInfoCount * tbttInfoLen); 370 } 371 372 // Done with parsing 373 mPresent = true; 374 } 375 } 376 377 public static class HtOperation { 378 private static final int HT_OPERATION_IE_LEN = 22; 379 private boolean mPresent = false; 380 private int mSecondChannelOffset = 0; 381 382 /** 383 * returns if HT Operation IE present in the message. 384 */ isPresent()385 public boolean isPresent() { 386 return mPresent; 387 } 388 389 /** 390 * Returns channel width if it is 20 or 40MHz 391 * Results will be invalid if channel width greater than 40MHz 392 * So caller should only call this method if VHT Operation IE is not present, 393 * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. 394 */ getChannelWidth()395 public int getChannelWidth() { 396 if (mSecondChannelOffset != 0) { 397 return ScanResult.CHANNEL_WIDTH_40MHZ; 398 } else { 399 return ScanResult.CHANNEL_WIDTH_20MHZ; 400 } 401 } 402 403 /** 404 * Returns channel Center frequency (for 20/40 MHz channels only) 405 * Results will be invalid for larger channel width, 406 * So, caller should only call this method if VHT Operation IE is not present, 407 * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. 408 */ getCenterFreq0(int primaryFrequency)409 public int getCenterFreq0(int primaryFrequency) { 410 if (mSecondChannelOffset != 0) { 411 //40 MHz 412 if (mSecondChannelOffset == 1) { 413 return primaryFrequency + 10; 414 } else if (mSecondChannelOffset == 3) { 415 return primaryFrequency - 10; 416 } else { 417 Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset); 418 return 0; 419 } 420 } else { 421 //20 MHz 422 return primaryFrequency; 423 } 424 } 425 426 /** 427 * Parse the HT Operation IE to read the fields of interest. 428 */ from(InformationElement ie)429 public void from(InformationElement ie) { 430 if (ie.id != InformationElement.EID_HT_OPERATION) { 431 throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id); 432 } 433 if (ie.bytes.length < HT_OPERATION_IE_LEN) { 434 throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length); 435 } 436 mPresent = true; 437 mSecondChannelOffset = ie.bytes[1] & 0x3; 438 } 439 } 440 441 public static class VhtOperation { 442 private static final int VHT_OPERATION_IE_LEN = 5; 443 private boolean mPresent = false; 444 private int mChannelMode = 0; 445 private int mCenterFreqIndex1 = 0; 446 private int mCenterFreqIndex2 = 0; 447 448 /** 449 * returns if VHT Operation IE present in the message. 450 */ isPresent()451 public boolean isPresent() { 452 return mPresent; 453 } 454 455 /** 456 * Returns channel width if it is above 40MHz, 457 * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that 458 * channel width should be obtained from the HT Operation IE via 459 * HtOperation.getChannelWidth(). 460 */ getChannelWidth()461 public int getChannelWidth() { 462 if (mChannelMode == 0) { 463 // 20 or 40MHz 464 return ScanResult.UNSPECIFIED; 465 } else if (mCenterFreqIndex2 == 0) { 466 // No secondary channel 467 return ScanResult.CHANNEL_WIDTH_80MHZ; 468 } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) { 469 // Primary and secondary channels adjacent 470 return ScanResult.CHANNEL_WIDTH_160MHZ; 471 } else { 472 // Primary and secondary channels not adjacent 473 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; 474 } 475 } 476 477 /** 478 * Returns center frequency of primary channel (if channel width greater than 40MHz), 479 * otherwise, it returns zero to indicate that center frequency should be obtained from 480 * the HT Operation IE via HtOperation.getCenterFreq0(). 481 */ getCenterFreq0()482 public int getCenterFreq0() { 483 if (mCenterFreqIndex1 == 0 || mChannelMode == 0) { 484 return 0; 485 } else { 486 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex1, 487 WifiScanner.WIFI_BAND_5_GHZ); 488 } 489 } 490 491 /** 492 * Returns center frequency of secondary channel if exists (channel width greater than 493 * 40MHz), otherwise, it returns zero. 494 * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz 495 * channels. 496 */ getCenterFreq1()497 public int getCenterFreq1() { 498 if (mCenterFreqIndex2 == 0 || mChannelMode == 0) { 499 return 0; 500 } else { 501 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex2, 502 WifiScanner.WIFI_BAND_5_GHZ); 503 } 504 } 505 506 /** 507 * Parse the VHT Operation IE to read the fields of interest. 508 */ from(InformationElement ie)509 public void from(InformationElement ie) { 510 if (ie.id != InformationElement.EID_VHT_OPERATION) { 511 throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id); 512 } 513 if (ie.bytes.length < VHT_OPERATION_IE_LEN) { 514 throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length); 515 } 516 mPresent = true; 517 mChannelMode = ie.bytes[0] & Constants.BYTE_MASK; 518 mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; 519 mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; 520 } 521 } 522 523 /** 524 * HeOperation: represents the HE Operation IE 525 */ 526 public static class HeOperation { 527 528 private static final int HE_OPERATION_BASIC_LENGTH = 6; 529 private static final int VHT_OPERATION_INFO_PRESENT_MASK = 0x40; 530 private static final int HE_6GHZ_INFO_PRESENT_MASK = 0x02; 531 private static final int HE_6GHZ_CH_WIDTH_MASK = 0x03; 532 private static final int CO_HOSTED_BSS_PRESENT_MASK = 0x80; 533 private static final int VHT_OPERATION_INFO_START_INDEX = 6; 534 private static final int HE_BW_80_80_160 = 3; 535 536 private boolean mPresent = false; 537 private boolean mVhtInfoPresent = false; 538 private boolean m6GhzInfoPresent = false; 539 private int mChannelWidth; 540 private int mPrimaryChannel; 541 private int mCenterFreqSeg0; 542 private int mCenterFreqSeg1; 543 private InformationElement mVhtInfo = null; 544 545 /** 546 * Returns whether the HE Information Element is present. 547 */ isPresent()548 public boolean isPresent() { 549 return mPresent; 550 } 551 552 /** 553 * Returns whether VHT Information field is present. 554 */ isVhtInfoPresent()555 public boolean isVhtInfoPresent() { 556 return mVhtInfoPresent; 557 } 558 559 /** 560 * Returns the VHT Information Element if it exists 561 * otherwise, return null. 562 */ getVhtInfoElement()563 public InformationElement getVhtInfoElement() { 564 return mVhtInfo; 565 } 566 567 /** 568 * Returns whether the 6GHz information field is present. 569 */ is6GhzInfoPresent()570 public boolean is6GhzInfoPresent() { 571 return m6GhzInfoPresent; 572 } 573 574 /** 575 * Returns the Channel BW 576 * Only applicable to 6GHz band 577 */ getChannelWidth()578 public int getChannelWidth() { 579 if (!m6GhzInfoPresent) { 580 return ScanResult.UNSPECIFIED; 581 } else if (mChannelWidth == 0) { 582 return ScanResult.CHANNEL_WIDTH_20MHZ; 583 } else if (mChannelWidth == 1) { 584 return ScanResult.CHANNEL_WIDTH_40MHZ; 585 } else if (mChannelWidth == 2) { 586 return ScanResult.CHANNEL_WIDTH_80MHZ; 587 } else if (Math.abs(mCenterFreqSeg1 - mCenterFreqSeg0) == 8) { 588 return ScanResult.CHANNEL_WIDTH_160MHZ; 589 } else { 590 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; 591 } 592 } 593 594 /** 595 * Returns the primary channel frequency 596 * Only applicable for 6GHz channels 597 */ getPrimaryFreq()598 public int getPrimaryFreq() { 599 return ScanResult.convertChannelToFrequencyMhzIfSupported(mPrimaryChannel, 600 WifiScanner.WIFI_BAND_6_GHZ); 601 } 602 603 /** 604 * Returns the center frequency for the primary channel 605 * Only applicable to 6GHz channels 606 */ getCenterFreq0()607 public int getCenterFreq0() { 608 if (m6GhzInfoPresent) { 609 if (mCenterFreqSeg0 == 0) { 610 return 0; 611 } else { 612 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg0, 613 WifiScanner.WIFI_BAND_6_GHZ); 614 } 615 } else { 616 return 0; 617 } 618 } 619 620 /** 621 * Returns the center frequency for the secondary channel 622 * Only applicable to 6GHz channels 623 */ getCenterFreq1()624 public int getCenterFreq1() { 625 if (m6GhzInfoPresent) { 626 if (mCenterFreqSeg1 == 0) { 627 return 0; 628 } else { 629 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg1, 630 WifiScanner.WIFI_BAND_6_GHZ); 631 } 632 } else { 633 return 0; 634 } 635 } 636 637 /** Parse HE Operation IE */ from(InformationElement ie)638 public void from(InformationElement ie) { 639 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 640 || ie.idExt != InformationElement.EID_EXT_HE_OPERATION) { 641 throw new IllegalArgumentException("Element id is not HE_OPERATION"); 642 } 643 644 // Make sure the byte array length is at least the fixed size 645 if (ie.bytes.length < HE_OPERATION_BASIC_LENGTH) { 646 if (DBG) { 647 Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length); 648 } 649 // Skipping parsing of the IE 650 return; 651 } 652 653 mVhtInfoPresent = (ie.bytes[1] & VHT_OPERATION_INFO_PRESENT_MASK) != 0; 654 m6GhzInfoPresent = (ie.bytes[2] & HE_6GHZ_INFO_PRESENT_MASK) != 0; 655 boolean coHostedBssPresent = (ie.bytes[1] & CO_HOSTED_BSS_PRESENT_MASK) != 0; 656 int expectedLen = HE_OPERATION_BASIC_LENGTH + (mVhtInfoPresent ? 3 : 0) 657 + (coHostedBssPresent ? 1 : 0) + (m6GhzInfoPresent ? 5 : 0); 658 659 // Make sure the byte array length is at least fitting the known parameters 660 if (ie.bytes.length < expectedLen) { 661 if (DBG) { 662 Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length); 663 } 664 // Skipping parsing of the IE 665 return; 666 } 667 668 // Passed all checks, IE is ready for decoding 669 mPresent = true; 670 671 if (mVhtInfoPresent) { 672 mVhtInfo = new InformationElement(); 673 mVhtInfo.id = InformationElement.EID_VHT_OPERATION; 674 mVhtInfo.bytes = new byte[5]; 675 System.arraycopy(ie.bytes, VHT_OPERATION_INFO_START_INDEX, mVhtInfo.bytes, 0, 3); 676 } 677 678 if (m6GhzInfoPresent) { 679 int startIndx = VHT_OPERATION_INFO_START_INDEX + (mVhtInfoPresent ? 3 : 0) 680 + (coHostedBssPresent ? 1 : 0); 681 682 mChannelWidth = ie.bytes[startIndx + 1] & HE_6GHZ_CH_WIDTH_MASK; 683 mPrimaryChannel = ie.bytes[startIndx] & Constants.BYTE_MASK; 684 mCenterFreqSeg0 = ie.bytes[startIndx + 2] & Constants.BYTE_MASK; 685 mCenterFreqSeg1 = ie.bytes[startIndx + 3] & Constants.BYTE_MASK; 686 } 687 } 688 } 689 690 /** 691 * EhtOperation: represents the EHT Operation IE 692 */ 693 public static class EhtOperation { 694 private boolean mPresent = false; 695 696 /** 697 * Returns whether the EHT Information Element is present. 698 */ isPresent()699 public boolean isPresent() { 700 return mPresent; 701 } 702 703 /** Parse EHT Operation IE */ from(InformationElement ie)704 public void from(InformationElement ie) { 705 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 706 || ie.idExt != InformationElement.EID_EXT_EHT_OPERATION) { 707 throw new IllegalArgumentException("Element id is not EHT_OPERATION"); 708 } 709 710 mPresent = true; 711 712 //TODO put more functionality for parsing the IE 713 } 714 } 715 716 /** 717 * HtCapabilities: represents the HT Capabilities IE 718 */ 719 public static class HtCapabilities { 720 private int mMaxNumberSpatialStreams = 1; 721 private boolean mPresent = false; 722 /** Returns whether HT Capabilities IE is present */ isPresent()723 public boolean isPresent() { 724 return mPresent; 725 } 726 /** 727 * Returns max number of spatial streams if HT Capabilities IE is found and parsed, 728 * or 1 otherwise 729 */ getMaxNumberSpatialStreams()730 public int getMaxNumberSpatialStreams() { 731 return mMaxNumberSpatialStreams; 732 } 733 734 /** Parse HT Capabilities IE */ from(InformationElement ie)735 public void from(InformationElement ie) { 736 if (ie.id != InformationElement.EID_HT_CAPABILITIES) { 737 throw new IllegalArgumentException("Element id is not HT_CAPABILITIES: " + ie.id); 738 } 739 if (ie.bytes.length < 26) { 740 if (DBG) { 741 Log.w(TAG, "Invalid HtCapabilities len: " + ie.bytes.length); 742 } 743 return; 744 } 745 int stream1 = ie.bytes[3] & Constants.BYTE_MASK; 746 int stream2 = ie.bytes[4] & Constants.BYTE_MASK; 747 int stream3 = ie.bytes[5] & Constants.BYTE_MASK; 748 int stream4 = ie.bytes[6] & Constants.BYTE_MASK; 749 if (DBG) { 750 Log.d(TAG, "HT Rx MCS set4: " + Integer.toHexString(stream4)); 751 Log.d(TAG, "HT Rx MCS set3: " + Integer.toHexString(stream3)); 752 Log.d(TAG, "HT Rx MCS set2: " + Integer.toHexString(stream2)); 753 Log.d(TAG, "HT Rx MCS set1: " + Integer.toHexString(stream1)); 754 } 755 mMaxNumberSpatialStreams = (stream4 > 0) ? 4 : 756 ((stream3 > 0) ? 3 : 757 ((stream2 > 0) ? 2 : 1)); 758 mPresent = true; 759 } 760 } 761 762 /** 763 * VhtCapabilities: represents the VHT Capabilities IE 764 */ 765 public static class VhtCapabilities { 766 private int mMaxNumberSpatialStreams = 1; 767 private boolean mPresent = false; 768 /** Returns whether VHT Capabilities IE is present */ isPresent()769 public boolean isPresent() { 770 return mPresent; 771 } 772 /** 773 * Returns max number of spatial streams if VHT Capabilities IE is found and parsed, 774 * or 1 otherwise 775 */ getMaxNumberSpatialStreams()776 public int getMaxNumberSpatialStreams() { 777 return mMaxNumberSpatialStreams; 778 } 779 /** Parse VHT Capabilities IE */ from(InformationElement ie)780 public void from(InformationElement ie) { 781 if (ie.id != InformationElement.EID_VHT_CAPABILITIES) { 782 throw new IllegalArgumentException("Element id is not VHT_CAPABILITIES: " + ie.id); 783 } 784 if (ie.bytes.length < 12) { 785 if (DBG) { 786 Log.w(TAG, "Invalid VHT_CAPABILITIES len: " + ie.bytes.length); 787 } 788 return; 789 } 790 int mcsMap = ((ie.bytes[5] & Constants.BYTE_MASK) << 8) 791 + (ie.bytes[4] & Constants.BYTE_MASK); 792 mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap); 793 mPresent = true; 794 } 795 } 796 797 /** 798 * HeCapabilities: represents the HE Capabilities IE 799 */ 800 public static class HeCapabilities { 801 private int mMaxNumberSpatialStreams = 1; 802 private boolean mPresent = false; 803 /** Returns whether HE Capabilities IE is present */ isPresent()804 public boolean isPresent() { 805 return mPresent; 806 } 807 /** 808 * Returns max number of spatial streams if HE Capabilities IE is found and parsed, 809 * or 1 otherwise 810 */ getMaxNumberSpatialStreams()811 public int getMaxNumberSpatialStreams() { 812 return mMaxNumberSpatialStreams; 813 } 814 /** Parse HE Capabilities IE */ from(InformationElement ie)815 public void from(InformationElement ie) { 816 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 817 || ie.idExt != InformationElement.EID_EXT_HE_CAPABILITIES) { 818 throw new IllegalArgumentException("Element id is not HE_CAPABILITIES: " + ie.id); 819 } 820 if (ie.bytes.length < 21) { 821 if (DBG) { 822 Log.w(TAG, "Invalid HE_CAPABILITIES len: " + ie.bytes.length); 823 } 824 return; 825 } 826 int mcsMap = ((ie.bytes[18] & Constants.BYTE_MASK) << 8) 827 + (ie.bytes[17] & Constants.BYTE_MASK); 828 mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap); 829 mPresent = true; 830 } 831 } 832 833 /** 834 * EhtCapabilities: represents the EHT Capabilities IE 835 */ 836 public static class EhtCapabilities { 837 private boolean mPresent = false; 838 /** Returns whether HE Capabilities IE is present */ isPresent()839 public boolean isPresent() { 840 return mPresent; 841 } 842 843 /** Parse EHT Capabilities IE */ from(InformationElement ie)844 public void from(InformationElement ie) { 845 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 846 || ie.idExt != InformationElement.EID_EXT_EHT_CAPABILITIES) { 847 throw new IllegalArgumentException("Element id is not EHT_CAPABILITIES: " + ie.id); 848 } 849 mPresent = true; 850 851 //TODO Add code to parse the IE 852 } 853 } 854 855 /** 856 * MultiLink: represents the Multi-Link IE 857 * as described in IEEE 802.11be Specification Section 9.4.2.312 858 */ 859 public static class MultiLink { 860 private static final int CONTROL_FIELD_LEN = 2; 861 private static final int BASIC_COMMON_INFO_FIELD_MIN_LEN = 7; 862 private static final int BASIC_LINK_INFO_FIELD_MIN_LEN = 0; 863 private static final int BASIC_IE_MIN_LEN = CONTROL_FIELD_LEN 864 + BASIC_COMMON_INFO_FIELD_MIN_LEN 865 + BASIC_LINK_INFO_FIELD_MIN_LEN; 866 867 // Control field constants 868 private static final int IE_TYPE_OFFSET = 0; 869 private static final int IE_TYPE_MASK = 0x07; 870 public static final int TYPE_BASIC = 0; 871 public static final int LINK_ID_PRESENT_OFFSET = 0; 872 public static final int LINK_ID_PRESENT_MASK = 0x10; 873 874 875 // Common info field constants 876 private static final int COMMON_FIELD_START_INDEX = CONTROL_FIELD_LEN; 877 private static final int BASIC_IE_COMMON_INFO_LEN_OFFSET = 0; 878 private static final int BASIC_IE_COMMON_MLD_MAC_ADDRESS_OFFSET = 1; 879 private static final int BASIC_IE_COMMOM_LINK_ID_OFFSET = 7; 880 private static final int BASIC_IE_COMMOM_LINK_ID_MASK = 0x0F; 881 882 // Per-STA sub-element constants 883 private static final int PER_STA_SUB_ELEMENT_ID = 0; 884 private static final int PER_STA_SUB_ELEMENT_MIN_LEN = 5; 885 private static final int PER_STA_SUB_ELEMENT_LINK_ID_OFFSET = 2; 886 private static final int PER_STA_SUB_ELEMENT_LINK_ID_MASK = 0x0F; 887 private static final int PER_STA_SUB_ELEMENT_STA_INFO_OFFSET = 4; 888 private static final int PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET = 2; 889 private static final int PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK = 0x20; 890 private static final int PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET = 1; 891 892 private boolean mPresent = false; 893 private int mLinkId = MloLink.INVALID_MLO_LINK_ID; 894 private MacAddress mMldMacAddress = null; 895 private List<MloLink> mAffiliatedLinks = new ArrayList<>(); 896 897 /** Returns whether Multi-Link IE is present */ isPresent()898 public boolean isPresent() { 899 return mPresent; 900 } 901 902 /** Returns the MLD MAC Address */ getMldMacAddress()903 public MacAddress getMldMacAddress() { 904 return mMldMacAddress; 905 } 906 907 /** Return the link id */ getLinkId()908 public int getLinkId() { 909 return mLinkId; 910 } 911 912 /** Return the affiliated links */ getAffiliatedLinks()913 public List<MloLink> getAffiliatedLinks() { 914 return new ArrayList<MloLink>(mAffiliatedLinks); 915 } 916 917 /** 918 * Parse Common Info field in Multi-Link Operation IE 919 * 920 * Common Info filed as described in IEEE 802.11 specs, Section 9.4.2.312, 921 * 922 * | Len | MLD Address | Link Id | BSS Change count | MedSync | EML Cap | MLD Cap | 923 * Octets: 1 6 0 or 1 0 or 1 0 or 2 0 or 2 0 or 2 924 * 925 */ parseCommonInfoField(InformationElement ie)926 private int parseCommonInfoField(InformationElement ie) { 927 int commonInfoLength = ie.bytes[COMMON_FIELD_START_INDEX 928 + BASIC_IE_COMMON_INFO_LEN_OFFSET] & Constants.BYTE_MASK; 929 if (commonInfoLength < BASIC_COMMON_INFO_FIELD_MIN_LEN) { 930 if (DBG) { 931 Log.w(TAG, "Invalid Common Info field length: " + commonInfoLength); 932 } 933 // Skipping parsing of the IE 934 return 0; 935 } 936 937 boolean isLinkIdInfoPresent = (ie.bytes[LINK_ID_PRESENT_OFFSET] 938 & LINK_ID_PRESENT_MASK) != 0; 939 if (isLinkIdInfoPresent) { 940 if (ie.bytes.length < BASIC_IE_MIN_LEN + 1 /*Link Id info */) { 941 if (DBG) { 942 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 943 } 944 // Skipping parsing of the IE 945 return 0; 946 } 947 948 mLinkId = ie.bytes[COMMON_FIELD_START_INDEX 949 + BASIC_IE_COMMOM_LINK_ID_OFFSET] & BASIC_IE_COMMOM_LINK_ID_MASK; 950 } 951 952 int macAddressStart = COMMON_FIELD_START_INDEX + BASIC_IE_COMMON_MLD_MAC_ADDRESS_OFFSET; 953 mMldMacAddress = MacAddress.fromBytes( 954 Arrays.copyOfRange(ie.bytes, macAddressStart, macAddressStart + 6)); 955 956 return commonInfoLength; 957 } 958 959 /** 960 * Parse Link Info field in Multi-Link Operation IE 961 * 962 * Link Info filed as described in IEEE 802.11 specs, Section 9.4.2.312, 963 * 964 * | ID | Len | STA Control | STA Info | STA Profile | 965 * Octets: 1 1 2 variable variable 966 * 967 * where STA Control subfield is described as, 968 * 969 * | LinkId | Complete | MAC | Beacon Interval | DTIM | NSTR Link | NSTR Bitmap | R | 970 * Bits: 4 1 1 1 1 1 1 6 971 * 972 */ parseLinkInfoField(InformationElement ie, int startOffset)973 private boolean parseLinkInfoField(InformationElement ie, int startOffset) { 974 // Check if Link Info field is present 975 while (ie.bytes.length >= startOffset + PER_STA_SUB_ELEMENT_MIN_LEN) { 976 int subElementId = ie.bytes[startOffset] & Constants.BYTE_MASK; 977 int subElementLen = ie.bytes[startOffset + 1] & Constants.BYTE_MASK; 978 // Expectation here is IE has enough length to parse and non-zero sub-element 979 // length. 980 if (ie.bytes.length < startOffset + subElementLen || subElementLen == 0) { 981 if (DBG) { 982 Log.w(TAG, "Invalid sub-element length: " + subElementLen); 983 } 984 // Skipping parsing of the IE 985 return false; 986 } 987 if (subElementId != PER_STA_SUB_ELEMENT_ID) { 988 // Skip this subelement, could be an unsupported one 989 startOffset += subElementLen; 990 continue; 991 } 992 993 MloLink link = new MloLink(); 994 link.setLinkId(ie.bytes[startOffset + PER_STA_SUB_ELEMENT_LINK_ID_OFFSET] 995 & PER_STA_SUB_ELEMENT_LINK_ID_MASK); 996 997 int staInfoLength = ie.bytes[startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET] 998 & Constants.BYTE_MASK; 999 if (subElementLen < PER_STA_SUB_ELEMENT_STA_INFO_OFFSET + staInfoLength) { 1000 if (DBG) { 1001 Log.w(TAG, "Invalid sta info length: " + staInfoLength); 1002 } 1003 // Skipping parsing of the IE 1004 return false; 1005 } 1006 1007 // Check if MAC Address is present 1008 if ((ie.bytes[startOffset + PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET] 1009 & PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK) != 0) { 1010 if (staInfoLength < 1 /*length*/ + 6 /*mac address*/) { 1011 if (DBG) { 1012 Log.w(TAG, "Invalid sta info length: " + staInfoLength); 1013 } 1014 // Skipping parsing of the IE 1015 return false; 1016 } 1017 1018 int macAddressOffset = startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET 1019 + PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET; 1020 link.setApMacAddress(MacAddress.fromBytes(Arrays.copyOfRange(ie.bytes, 1021 macAddressOffset, macAddressOffset + 6))); 1022 } 1023 1024 mAffiliatedLinks.add(link); 1025 1026 // Done with this sub-element 1027 startOffset += subElementLen; 1028 } 1029 1030 return true; 1031 } 1032 1033 /** 1034 * Parse Multi-Link Operation IE 1035 * 1036 * Multi-Link IE format as described in IEEE 802.11 specs, Section 9.4.2.312 1037 * 1038 * | ElementID | Length | ExtendedID | Control | Common Info | Link Info | 1039 * Octets: 1 1 1 2 Variable variable 1040 * 1041 * 1042 * Where Control field is described as, 1043 * 1044 * | Type | Reserved | Presence Bitmap | 1045 * Bits: 3 1 12 1046 * 1047 * Where the Presence Bitmap subfield is described as, 1048 * 1049 * | LinkId | BSS change count | MedSync | EML cap | MLD cap | Reserved | 1050 * Bits: 1 1 1 1 1 7 1051 * 1052 * 1053 * 1054 * Note: InformationElement.bytes has 'Element ID', 'Length', and 'Extended ID' 1055 * stripped off already 1056 * 1057 */ from(InformationElement ie)1058 public void from(InformationElement ie) { 1059 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 1060 || ie.idExt != InformationElement.EID_EXT_MULTI_LINK) { 1061 throw new IllegalArgumentException("Element id is not Multi-Link: " + ie.id); 1062 } 1063 1064 // Make sure the byte array length is at least the Control field size 1065 if (ie.bytes.length < CONTROL_FIELD_LEN) { 1066 if (DBG) { 1067 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1068 } 1069 // Skipping parsing of the IE 1070 return; 1071 } 1072 1073 // Check on IE type 1074 // Note only the BASIC type is allowed to be received from AP 1075 int type = ie.bytes[IE_TYPE_OFFSET] & IE_TYPE_MASK; 1076 if (type != TYPE_BASIC) { 1077 if (DBG) { 1078 Log.w(TAG, "Invalid/Unsupported Multi-Link IE type: " + type); 1079 } 1080 // Skipping parsing of the IE 1081 return; 1082 } 1083 1084 // Check length 1085 if (ie.bytes.length < BASIC_IE_MIN_LEN) { 1086 if (DBG) { 1087 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1088 } 1089 // Skipping parsing of the IE 1090 return; 1091 } 1092 1093 int commonInfoLength = parseCommonInfoField(ie); 1094 if (commonInfoLength == 0) { 1095 return; 1096 } 1097 1098 if (!parseLinkInfoField(ie, CONTROL_FIELD_LEN + commonInfoLength)) { 1099 return; 1100 } 1101 1102 mPresent = true; 1103 } 1104 } 1105 parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap)1106 private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) { 1107 int maxNumberSpatialStreams = 1; 1108 for (int i = 8; i >= 1; --i) { 1109 int streamMap = mcsMapToStreamMap(mcsMap, i); 1110 // 3 means unsupported 1111 if (streamMap != 3) { 1112 maxNumberSpatialStreams = i; 1113 break; 1114 } 1115 } 1116 if (DBG) { 1117 for (int i = 8; i >= 1; --i) { 1118 int streamMap = mcsMapToStreamMap(mcsMap, i); 1119 Log.d(TAG, "Rx MCS set " + i + " : " + streamMap); 1120 } 1121 } 1122 return maxNumberSpatialStreams; 1123 } 1124 mcsMapToStreamMap(int mcsMap, int i)1125 private static int mcsMapToStreamMap(int mcsMap, int i) { 1126 return (mcsMap >> ((i - 1) * 2)) & 0x3; 1127 } 1128 1129 public static class Interworking { 1130 public NetworkDetail.Ant ant = null; 1131 public boolean internet = false; 1132 public long hessid = 0L; 1133 from(InformationElement ie)1134 public void from(InformationElement ie) { 1135 if (ie.id != InformationElement.EID_INTERWORKING) { 1136 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id); 1137 } 1138 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1139 int anOptions = data.get() & Constants.BYTE_MASK; 1140 ant = NetworkDetail.Ant.values()[anOptions & 0x0f]; 1141 internet = (anOptions & 0x10) != 0; 1142 // There are only three possible lengths for the Interworking IE: 1143 // Len 1: Access Network Options only 1144 // Len 3: Access Network Options & Venue Info 1145 // Len 7: Access Network Options & HESSID 1146 // Len 9: Access Network Options, Venue Info, & HESSID 1147 if (ie.bytes.length != 1 1148 && ie.bytes.length != 3 1149 && ie.bytes.length != 7 1150 && ie.bytes.length != 9) { 1151 throw new IllegalArgumentException( 1152 "Bad Interworking element length: " + ie.bytes.length); 1153 } 1154 1155 if (ie.bytes.length == 3 || ie.bytes.length == 9) { 1156 int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2); 1157 } 1158 1159 if (ie.bytes.length == 7 || ie.bytes.length == 9) { 1160 hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6); 1161 } 1162 } 1163 } 1164 1165 public static class RoamingConsortium { 1166 public int anqpOICount = 0; 1167 1168 private long[] roamingConsortiums = null; 1169 getRoamingConsortiums()1170 public long[] getRoamingConsortiums() { 1171 return roamingConsortiums; 1172 } 1173 from(InformationElement ie)1174 public void from(InformationElement ie) { 1175 if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) { 1176 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " 1177 + ie.id); 1178 } 1179 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1180 anqpOICount = data.get() & Constants.BYTE_MASK; 1181 1182 int oi12Length = data.get() & Constants.BYTE_MASK; 1183 int oi1Length = oi12Length & Constants.NIBBLE_MASK; 1184 int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK; 1185 int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length; 1186 int oiCount = 0; 1187 if (oi1Length > 0) { 1188 oiCount++; 1189 if (oi2Length > 0) { 1190 oiCount++; 1191 if (oi3Length > 0) { 1192 oiCount++; 1193 } 1194 } 1195 } 1196 roamingConsortiums = new long[oiCount]; 1197 if (oi1Length > 0 && roamingConsortiums.length > 0) { 1198 roamingConsortiums[0] = 1199 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); 1200 } 1201 if (oi2Length > 0 && roamingConsortiums.length > 1) { 1202 roamingConsortiums[1] = 1203 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); 1204 } 1205 if (oi3Length > 0 && roamingConsortiums.length > 2) { 1206 roamingConsortiums[2] = 1207 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); 1208 } 1209 } 1210 } 1211 1212 public static class Vsa { 1213 private static final int ANQP_DOMAIN_ID_PRESENT_BIT = 0x04; 1214 private static final int ANQP_PPS_MO_ID_BIT = 0x02; 1215 private static final int OUI_WFA_ALLIANCE = 0x506F9a; 1216 private static final int OUI_TYPE_HS20 = 0x10; 1217 private static final int OUI_TYPE_MBO_OCE = 0x16; 1218 1219 public NetworkDetail.HSRelease hsRelease = null; 1220 public int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP. 1221 1222 public boolean IsMboCapable = false; 1223 public boolean IsMboApCellularDataAware = false; 1224 public boolean IsOceCapable = false; 1225 public int mboAssociationDisallowedReasonCode = 1226 MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT; 1227 public byte[] oui; 1228 parseVsaMboOce(InformationElement ie)1229 private void parseVsaMboOce(InformationElement ie) { 1230 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1231 1232 // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying 1233 // MBO-OCE OUI type. 1234 data.getInt(); 1235 1236 while (data.remaining() > 1) { 1237 int attrId = data.get() & Constants.BYTE_MASK; 1238 int attrLen = data.get() & Constants.BYTE_MASK; 1239 1240 if ((attrLen == 0) || (attrLen > data.remaining())) { 1241 return; 1242 } 1243 byte[] attrBytes = new byte[attrLen]; 1244 data.get(attrBytes); 1245 switch (attrId) { 1246 case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION: 1247 IsMboCapable = true; 1248 IsMboApCellularDataAware = (attrBytes[0] 1249 & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0; 1250 break; 1251 case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED: 1252 mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK; 1253 break; 1254 case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION: 1255 IsOceCapable = true; 1256 break; 1257 default: 1258 break; 1259 } 1260 } 1261 if (DBG) { 1262 Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: " 1263 + IsMboApCellularDataAware + " AssocDisAllowRC: " 1264 + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable); 1265 } 1266 } 1267 parseVsaHs20(InformationElement ie)1268 private void parseVsaHs20(InformationElement ie) { 1269 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1270 if (ie.bytes.length >= 5) { 1271 // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying 1272 // HS20 OUI type. 1273 data.getInt(); 1274 1275 int hsConf = data.get() & Constants.BYTE_MASK; 1276 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) { 1277 case 0: 1278 hsRelease = NetworkDetail.HSRelease.R1; 1279 break; 1280 case 1: 1281 hsRelease = NetworkDetail.HSRelease.R2; 1282 break; 1283 case 2: 1284 hsRelease = NetworkDetail.HSRelease.R3; 1285 break; 1286 default: 1287 hsRelease = NetworkDetail.HSRelease.Unknown; 1288 break; 1289 } 1290 if ((hsConf & ANQP_DOMAIN_ID_PRESENT_BIT) != 0) { 1291 // According to Hotspot 2.0 Specification v3.0 section 3.1.1 HS2.0 Indication 1292 // element, the size of the element is 5 bytes, and 2 bytes are optionally added 1293 // for each optional field; ANQP PPS MO ID and ANQP Domain ID present. 1294 int expectedSize = 7; 1295 if ((hsConf & ANQP_PPS_MO_ID_BIT) != 0) { 1296 expectedSize += 2; 1297 if (ie.bytes.length < expectedSize) { 1298 throw new IllegalArgumentException( 1299 "HS20 indication element too short: " + ie.bytes.length); 1300 } 1301 data.getShort(); // Skip 2 bytes 1302 } 1303 if (ie.bytes.length < expectedSize) { 1304 throw new IllegalArgumentException( 1305 "HS20 indication element too short: " + ie.bytes.length); 1306 } 1307 anqpDomainID = data.getShort() & Constants.SHORT_MASK; 1308 } 1309 } 1310 } 1311 1312 /** 1313 * Parse the vendor specific information element to build 1314 * InformationElemmentUtil.vsa object. 1315 * 1316 * @param ie -- Information Element 1317 */ from(InformationElement ie)1318 public void from(InformationElement ie) { 1319 if (ie.bytes.length < 3) { 1320 if (DBG) { 1321 Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length); 1322 } 1323 return; 1324 } 1325 1326 oui = Arrays.copyOfRange(ie.bytes, 0, 3); 1327 int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16) 1328 | ((ie.bytes[1] & Constants.BYTE_MASK) << 8) 1329 | ((ie.bytes[2] & Constants.BYTE_MASK))); 1330 1331 if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) { 1332 int ouiType = ie.bytes[3]; 1333 switch (ouiType) { 1334 case OUI_TYPE_HS20: 1335 parseVsaHs20(ie); 1336 break; 1337 case OUI_TYPE_MBO_OCE: 1338 parseVsaMboOce(ie); 1339 break; 1340 default: 1341 break; 1342 } 1343 } 1344 } 1345 } 1346 1347 /** 1348 * This IE contained a bit field indicating the capabilities being advertised by the STA. 1349 * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE. 1350 * 1351 * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each 1352 * bit. 1353 * 1354 * Here is the wire format of this IE: 1355 * | Element ID | Length | Capabilities | 1356 * 1 1 n 1357 */ 1358 public static class ExtendedCapabilities { 1359 private static final int RTT_RESP_ENABLE_BIT = 70; 1360 private static final int SSID_UTF8_BIT = 48; 1361 1362 public BitSet capabilitiesBitSet; 1363 1364 /** 1365 * @return true if SSID should be interpreted using UTF-8 encoding 1366 */ isStrictUtf8()1367 public boolean isStrictUtf8() { 1368 return capabilitiesBitSet.get(SSID_UTF8_BIT); 1369 } 1370 1371 /** 1372 * @return true if 802.11 MC RTT Response is enabled 1373 */ is80211McRTTResponder()1374 public boolean is80211McRTTResponder() { 1375 return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT); 1376 } 1377 ExtendedCapabilities()1378 public ExtendedCapabilities() { 1379 capabilitiesBitSet = new BitSet(); 1380 } 1381 ExtendedCapabilities(ExtendedCapabilities other)1382 public ExtendedCapabilities(ExtendedCapabilities other) { 1383 capabilitiesBitSet = other.capabilitiesBitSet; 1384 } 1385 1386 /** 1387 * Parse an ExtendedCapabilities from the IE containing raw bytes. 1388 * 1389 * @param ie The Information element data 1390 */ from(InformationElement ie)1391 public void from(InformationElement ie) { 1392 capabilitiesBitSet = BitSet.valueOf(ie.bytes); 1393 } 1394 } 1395 1396 /** 1397 * parse beacon to build the capabilities 1398 * 1399 * This class is used to build the capabilities string of the scan results coming 1400 * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, 1401 * and builds the ScanResult.capabilities String in a way that mirrors the values returned 1402 * by wpa_supplicant. 1403 */ 1404 public static class Capabilities { 1405 private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000; 1406 private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000; 1407 private static final short WPA_VENDOR_OUI_VERSION = 0x0001; 1408 private static final int OWE_VENDOR_OUI_TYPE = 0x1c9a6f50; 1409 private static final short RSNE_VERSION = 0x0001; 1410 1411 private static final int WPA_AKM_EAP = 0x01f25000; 1412 private static final int WPA_AKM_PSK = 0x02f25000; 1413 1414 private static final int RSN_AKM_EAP = 0x01ac0f00; 1415 private static final int RSN_AKM_PSK = 0x02ac0f00; 1416 private static final int RSN_AKM_FT_EAP = 0x03ac0f00; 1417 private static final int RSN_AKM_FT_PSK = 0x04ac0f00; 1418 private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00; 1419 private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00; 1420 private static final int RSN_AKM_SAE = 0x08ac0f00; 1421 private static final int RSN_AKM_FT_SAE = 0x09ac0f00; 1422 private static final int RSN_AKM_OWE = 0x12ac0f00; 1423 private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; 1424 private static final int RSN_OSEN = 0x019a6f50; 1425 private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00; 1426 private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00; 1427 private static final int RSN_AKM_SAE_EXT_KEY = 0x18ac0f00; 1428 private static final int RSN_AKM_FT_SAE_EXT_KEY = 0x19ac0f00; 1429 private static final int RSN_AKM_DPP = 0x029a6f50; 1430 1431 private static final int WPA_CIPHER_NONE = 0x00f25000; 1432 private static final int WPA_CIPHER_TKIP = 0x02f25000; 1433 private static final int WPA_CIPHER_CCMP = 0x04f25000; 1434 1435 private static final int RSN_CIPHER_NONE = 0x00ac0f00; 1436 private static final int RSN_CIPHER_TKIP = 0x02ac0f00; 1437 private static final int RSN_CIPHER_CCMP = 0x04ac0f00; 1438 private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00; 1439 private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00; 1440 private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00; 1441 private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00; 1442 private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00; 1443 private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00; 1444 1445 // RSN capability bit definition 1446 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6; 1447 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7; 1448 1449 public List<Integer> protocol; 1450 public List<List<Integer>> keyManagement; 1451 public List<List<Integer>> pairwiseCipher; 1452 public List<Integer> groupCipher; 1453 public List<Integer> groupManagementCipher; 1454 public boolean isESS; 1455 public boolean isIBSS; 1456 public boolean isPrivacy; 1457 public boolean isWPS; 1458 public boolean isManagementFrameProtectionRequired; 1459 public boolean isManagementFrameProtectionCapable; 1460 Capabilities()1461 public Capabilities() { 1462 } 1463 1464 // RSNE format (size unit: byte) 1465 // 1466 // | Element ID | Length | Version | Group Data Cipher Suite | 1467 // 1 1 2 4 1468 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1469 // 2 4 * m 1470 // | AKM Suite Count | AKM Suite List | RSN Capabilities | 1471 // 2 4 * n 2 1472 // | PMKID Count | PMKID List | Group Management Cipher Suite | 1473 // 2 16 * s 4 1474 // 1475 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1476 // stripped off already parseRsnElement(InformationElement ie)1477 private void parseRsnElement(InformationElement ie) { 1478 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1479 1480 try { 1481 // version 1482 if (buf.getShort() != RSNE_VERSION) { 1483 // incorrect version 1484 return; 1485 } 1486 1487 // found the RSNE IE, hence start building the capability string 1488 protocol.add(ScanResult.PROTOCOL_RSN); 1489 1490 // group data cipher suite 1491 groupCipher.add(parseRsnCipher(buf.getInt())); 1492 1493 // pairwise cipher suite count 1494 short cipherCount = buf.getShort(); 1495 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>(); 1496 // pairwise cipher suite list 1497 for (int i = 0; i < cipherCount; i++) { 1498 rsnPairwiseCipher.add(parseRsnCipher(buf.getInt())); 1499 } 1500 pairwiseCipher.add(rsnPairwiseCipher); 1501 1502 // AKM 1503 // AKM suite count 1504 short akmCount = buf.getShort(); 1505 ArrayList<Integer> rsnKeyManagement = new ArrayList<>(); 1506 1507 for (int i = 0; i < akmCount; i++) { 1508 int akm = buf.getInt(); 1509 switch (akm) { 1510 case RSN_AKM_EAP: 1511 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1512 break; 1513 case RSN_AKM_PSK: 1514 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1515 break; 1516 case RSN_AKM_FT_EAP: 1517 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP); 1518 break; 1519 case RSN_AKM_FT_PSK: 1520 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK); 1521 break; 1522 case RSN_AKM_EAP_SHA256: 1523 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256); 1524 break; 1525 case RSN_AKM_PSK_SHA256: 1526 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256); 1527 break; 1528 case RSN_AKM_SAE: 1529 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE); 1530 break; 1531 case RSN_AKM_FT_SAE: 1532 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE); 1533 break; 1534 case RSN_AKM_SAE_EXT_KEY: 1535 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE_EXT_KEY); 1536 break; 1537 case RSN_AKM_FT_SAE_EXT_KEY: 1538 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE_EXT_KEY); 1539 break; 1540 case RSN_AKM_OWE: 1541 rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE); 1542 break; 1543 case RSN_AKM_EAP_SUITE_B_192: 1544 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); 1545 break; 1546 case RSN_OSEN: 1547 rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); 1548 break; 1549 case RSN_AKM_EAP_FILS_SHA256: 1550 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); 1551 break; 1552 case RSN_AKM_EAP_FILS_SHA384: 1553 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); 1554 break; 1555 case RSN_AKM_DPP: 1556 rsnKeyManagement.add(ScanResult.KEY_MGMT_DPP); 1557 break; 1558 default: 1559 rsnKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN); 1560 break; 1561 } 1562 } 1563 // Default AKM 1564 if (rsnKeyManagement.isEmpty()) { 1565 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1566 } 1567 keyManagement.add(rsnKeyManagement); 1568 1569 // RSN capabilities (optional), 1570 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016 1571 if (buf.remaining() < 2) return; 1572 int rsnCaps = buf.getShort(); 1573 isManagementFrameProtectionRequired = 1574 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps); 1575 isManagementFrameProtectionCapable = 1576 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps); 1577 1578 if (buf.remaining() < 2) return; 1579 // PMKID, it's not used, drop it if exists (optional). 1580 int rsnPmkIdCount = buf.getShort(); 1581 for (int i = 0; i < rsnPmkIdCount; i++) { 1582 // Each PMKID element length in the PMKID List is 16 bytes 1583 byte[] tmp = new byte[16]; 1584 buf.get(tmp); 1585 } 1586 1587 // Group management cipher suite (optional). 1588 if (buf.remaining() < 4) return; 1589 groupManagementCipher.add(parseRsnCipher(buf.getInt())); 1590 } catch (BufferUnderflowException e) { 1591 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow"); 1592 } 1593 } 1594 parseWpaCipher(int cipher)1595 private static @Cipher int parseWpaCipher(int cipher) { 1596 switch (cipher) { 1597 case WPA_CIPHER_NONE: 1598 return ScanResult.CIPHER_NONE; 1599 case WPA_CIPHER_TKIP: 1600 return ScanResult.CIPHER_TKIP; 1601 case WPA_CIPHER_CCMP: 1602 return ScanResult.CIPHER_CCMP; 1603 default: 1604 Log.w("IE_Capabilities", "Unknown WPA cipher suite: " 1605 + Integer.toHexString(cipher)); 1606 return ScanResult.CIPHER_NONE; 1607 } 1608 } 1609 parseRsnCipher(int cipher)1610 private static @Cipher int parseRsnCipher(int cipher) { 1611 switch (cipher) { 1612 case RSN_CIPHER_NONE: 1613 return ScanResult.CIPHER_NONE; 1614 case RSN_CIPHER_TKIP: 1615 return ScanResult.CIPHER_TKIP; 1616 case RSN_CIPHER_CCMP: 1617 return ScanResult.CIPHER_CCMP; 1618 case RSN_CIPHER_GCMP_256: 1619 return ScanResult.CIPHER_GCMP_256; 1620 case RSN_CIPHER_NO_GROUP_ADDRESSED: 1621 return ScanResult.CIPHER_NO_GROUP_ADDRESSED; 1622 case RSN_CIPHER_GCMP_128: 1623 return ScanResult.CIPHER_GCMP_128; 1624 case RSN_CIPHER_BIP_GMAC_128: 1625 return ScanResult.CIPHER_BIP_GMAC_128; 1626 case RSN_CIPHER_BIP_GMAC_256: 1627 return ScanResult.CIPHER_BIP_GMAC_256; 1628 case RSN_CIPHER_BIP_CMAC_256: 1629 return ScanResult.CIPHER_BIP_CMAC_256; 1630 default: 1631 Log.w("IE_Capabilities", "Unknown RSN cipher suite: " 1632 + Integer.toHexString(cipher)); 1633 return ScanResult.CIPHER_NONE; 1634 } 1635 } 1636 isWpsElement(InformationElement ie)1637 private static boolean isWpsElement(InformationElement ie) { 1638 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1639 try { 1640 // WPS OUI and type 1641 return (buf.getInt() == WPS_VENDOR_OUI_TYPE); 1642 } catch (BufferUnderflowException e) { 1643 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1644 return false; 1645 } 1646 } 1647 isWpaOneElement(InformationElement ie)1648 private static boolean isWpaOneElement(InformationElement ie) { 1649 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1650 1651 try { 1652 // WPA OUI and type 1653 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE); 1654 } catch (BufferUnderflowException e) { 1655 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1656 return false; 1657 } 1658 } 1659 1660 // WPA type 1 format (size unit: byte) 1661 // 1662 // | Element ID | Length | OUI | Type | Version | 1663 // 1 1 3 1 2 1664 // | Group Data Cipher Suite | 1665 // 4 1666 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1667 // 2 4 * m 1668 // | AKM Suite Count | AKM Suite List | 1669 // 2 4 * n 1670 // 1671 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1672 // stripped off already 1673 // parseWpaOneElement(InformationElement ie)1674 private void parseWpaOneElement(InformationElement ie) { 1675 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1676 1677 try { 1678 // skip WPA OUI and type parsing. isWpaOneElement() should have 1679 // been called for verification before we reach here. 1680 buf.getInt(); 1681 1682 // version 1683 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) { 1684 // incorrect version 1685 return; 1686 } 1687 1688 // start building the string 1689 protocol.add(ScanResult.PROTOCOL_WPA); 1690 1691 // group data cipher suite 1692 groupCipher.add(parseWpaCipher(buf.getInt())); 1693 1694 // pairwise cipher suite count 1695 short cipherCount = buf.getShort(); 1696 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>(); 1697 // pairwise chipher suite list 1698 for (int i = 0; i < cipherCount; i++) { 1699 wpaPairwiseCipher.add(parseWpaCipher(buf.getInt())); 1700 } 1701 pairwiseCipher.add(wpaPairwiseCipher); 1702 1703 // AKM 1704 // AKM suite count 1705 short akmCount = buf.getShort(); 1706 ArrayList<Integer> wpaKeyManagement = new ArrayList<>(); 1707 1708 // AKM suite list 1709 for (int i = 0; i < akmCount; i++) { 1710 int akm = buf.getInt(); 1711 switch (akm) { 1712 case WPA_AKM_EAP: 1713 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1714 break; 1715 case WPA_AKM_PSK: 1716 wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1717 break; 1718 default: 1719 wpaKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN); 1720 break; 1721 } 1722 } 1723 // Default AKM 1724 if (wpaKeyManagement.isEmpty()) { 1725 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1726 } 1727 keyManagement.add(wpaKeyManagement); 1728 } catch (BufferUnderflowException e) { 1729 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow"); 1730 } 1731 } 1732 1733 /** 1734 * Parse the Information Element and the 16-bit Capability Information field 1735 * to build the InformationElemmentUtil.capabilities object. 1736 * 1737 * @param ies -- Information Element array 1738 * @param beaconCap -- 16-bit Beacon Capability Information field 1739 * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device 1740 * @param freq -- Frequency on which frame/beacon was transmitted. 1741 * Some parsing may be affected such as DMG parameters in 1742 * DMG (60GHz) beacon. 1743 */ 1744 from(InformationElement[] ies, int beaconCap, boolean isOweSupported, int freq)1745 public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported, 1746 int freq) { 1747 protocol = new ArrayList<>(); 1748 keyManagement = new ArrayList<>(); 1749 groupCipher = new ArrayList<>(); 1750 pairwiseCipher = new ArrayList<>(); 1751 groupManagementCipher = new ArrayList<>(); 1752 1753 if (ies == null) { 1754 return; 1755 } 1756 isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0; 1757 if (ScanResult.is60GHz(freq)) { 1758 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */ 1759 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS) 1760 == NativeScanResult.BSS_CAPABILITY_DMG_ESS) { 1761 isESS = true; 1762 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) { 1763 isIBSS = true; 1764 } 1765 } else { 1766 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0; 1767 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0; 1768 } 1769 for (InformationElement ie : ies) { 1770 WifiNl80211Manager.OemSecurityType oemSecurityType = 1771 WifiNl80211Manager.parseOemSecurityTypeElement( 1772 ie.id, ie.idExt, ie.bytes); 1773 if (oemSecurityType != null 1774 && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) { 1775 protocol.add(oemSecurityType.protocol); 1776 keyManagement.add(oemSecurityType.keyManagement); 1777 pairwiseCipher.add(oemSecurityType.pairwiseCipher); 1778 groupCipher.add(oemSecurityType.groupCipher); 1779 } 1780 1781 if (ie.id == InformationElement.EID_RSN) { 1782 parseRsnElement(ie); 1783 } 1784 1785 if (ie.id == InformationElement.EID_VSA) { 1786 if (isWpaOneElement(ie)) { 1787 parseWpaOneElement(ie); 1788 } 1789 if (isWpsElement(ie)) { 1790 // TODO(b/62134557): parse WPS IE to provide finer granularity information. 1791 isWPS = true; 1792 } 1793 if (isOweSupported && isOweElement(ie)) { 1794 /* From RFC 8110: Once the client and AP have finished 802.11 association, 1795 they then complete the Diffie-Hellman key exchange and create a Pairwise 1796 Master Key (PMK) and its associated identifier, PMKID [IEEE802.11]. 1797 Upon completion of 802.11 association, the AP initiates the 4-way 1798 handshake to the client using the PMK generated above. The 4-way 1799 handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation 1800 Key (KCK), and a Message Integrity Code (MIC) to use for protection 1801 of the frames that define the 4-way handshake. 1802 1803 We check if OWE is supported here because we are adding the OWE 1804 capabilities to the Open BSS. Non-supporting devices need to see this 1805 open network and ignore this element. Supporting devices need to hide 1806 the Open BSS of OWE in transition mode and connect to the Hidden one. 1807 */ 1808 protocol.add(ScanResult.PROTOCOL_RSN); 1809 groupCipher.add(ScanResult.CIPHER_CCMP); 1810 ArrayList<Integer> owePairwiseCipher = new ArrayList<>(); 1811 owePairwiseCipher.add(ScanResult.CIPHER_CCMP); 1812 pairwiseCipher.add(owePairwiseCipher); 1813 ArrayList<Integer> oweKeyManagement = new ArrayList<>(); 1814 oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION); 1815 keyManagement.add(oweKeyManagement); 1816 } 1817 } 1818 } 1819 } 1820 isOweElement(InformationElement ie)1821 private static boolean isOweElement(InformationElement ie) { 1822 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1823 try { 1824 // OWE OUI and type 1825 return (buf.getInt() == OWE_VENDOR_OUI_TYPE); 1826 } catch (BufferUnderflowException e) { 1827 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1828 return false; 1829 } 1830 } 1831 protocolToString(@rotocol int protocol)1832 private String protocolToString(@Protocol int protocol) { 1833 switch (protocol) { 1834 case ScanResult.PROTOCOL_NONE: 1835 return "None"; 1836 case ScanResult.PROTOCOL_WPA: 1837 return "WPA"; 1838 case ScanResult.PROTOCOL_RSN: 1839 return "RSN"; 1840 case ScanResult.PROTOCOL_OSEN: 1841 return "OSEN"; 1842 case ScanResult.PROTOCOL_WAPI: 1843 return "WAPI"; 1844 default: 1845 return "?"; 1846 } 1847 } 1848 keyManagementToString(@eyMgmt int akm)1849 private String keyManagementToString(@KeyMgmt int akm) { 1850 switch (akm) { 1851 case ScanResult.KEY_MGMT_NONE: 1852 return "None"; 1853 case ScanResult.KEY_MGMT_PSK: 1854 return "PSK"; 1855 case ScanResult.KEY_MGMT_EAP: 1856 return "EAP/SHA1"; 1857 case ScanResult.KEY_MGMT_FT_EAP: 1858 return "FT/EAP"; 1859 case ScanResult.KEY_MGMT_FT_PSK: 1860 return "FT/PSK"; 1861 case ScanResult.KEY_MGMT_EAP_SHA256: 1862 return "EAP/SHA256"; 1863 case ScanResult.KEY_MGMT_PSK_SHA256: 1864 return "PSK-SHA256"; 1865 case ScanResult.KEY_MGMT_OWE: 1866 return "OWE"; 1867 case ScanResult.KEY_MGMT_OWE_TRANSITION: 1868 return "OWE_TRANSITION"; 1869 case ScanResult.KEY_MGMT_SAE: 1870 return "SAE"; 1871 case ScanResult.KEY_MGMT_FT_SAE: 1872 return "FT/SAE"; 1873 case ScanResult.KEY_MGMT_SAE_EXT_KEY: 1874 return "SAE_EXT_KEY"; 1875 case ScanResult.KEY_MGMT_FT_SAE_EXT_KEY: 1876 return "FT/SAE_EXT_KEY"; 1877 case ScanResult.KEY_MGMT_EAP_SUITE_B_192: 1878 return "EAP_SUITE_B_192"; 1879 case ScanResult.KEY_MGMT_OSEN: 1880 return "OSEN"; 1881 case ScanResult.KEY_MGMT_WAPI_PSK: 1882 return "WAPI-PSK"; 1883 case ScanResult.KEY_MGMT_WAPI_CERT: 1884 return "WAPI-CERT"; 1885 case ScanResult.KEY_MGMT_FILS_SHA256: 1886 return "EAP-FILS-SHA256"; 1887 case ScanResult.KEY_MGMT_FILS_SHA384: 1888 return "EAP-FILS-SHA384"; 1889 case ScanResult.KEY_MGMT_DPP: 1890 return "DPP"; 1891 default: 1892 return "?"; 1893 } 1894 } 1895 cipherToString(@ipher int cipher)1896 private String cipherToString(@Cipher int cipher) { 1897 switch (cipher) { 1898 case ScanResult.CIPHER_NONE: 1899 return "None"; 1900 case ScanResult.CIPHER_CCMP: 1901 return "CCMP"; 1902 case ScanResult.CIPHER_GCMP_256: 1903 return "GCMP-256"; 1904 case ScanResult.CIPHER_TKIP: 1905 return "TKIP"; 1906 case ScanResult.CIPHER_SMS4: 1907 return "SMS4"; 1908 default: 1909 return "?"; 1910 } 1911 } 1912 1913 /** 1914 * Build the ScanResult.capabilities String. 1915 * 1916 * @return security string that mirrors what wpa_supplicant generates 1917 */ generateCapabilitiesString()1918 public String generateCapabilitiesString() { 1919 StringBuilder capabilities = new StringBuilder(); 1920 // private Beacon without an RSNE or WPA IE, hence WEP0 1921 boolean isWEP = (protocol.isEmpty()) && isPrivacy; 1922 1923 if (isWEP) { 1924 capabilities.append("[WEP]"); 1925 } 1926 for (int i = 0; i < protocol.size(); i++) { 1927 String capability = generateCapabilitiesStringPerProtocol(i); 1928 // add duplicate capabilities for WPA2 for backward compatibility: 1929 // duplicate "RSN" entries as "WPA2" 1930 String capWpa2 = generateWPA2CapabilitiesString(capability, i); 1931 capabilities.append(capWpa2); 1932 capabilities.append(capability); 1933 } 1934 if (isESS) { 1935 capabilities.append("[ESS]"); 1936 } 1937 if (isIBSS) { 1938 capabilities.append("[IBSS]"); 1939 } 1940 if (isWPS) { 1941 capabilities.append("[WPS]"); 1942 } 1943 if (isManagementFrameProtectionRequired) { 1944 capabilities.append("[MFPR]"); 1945 } 1946 if (isManagementFrameProtectionCapable) { 1947 capabilities.append("[MFPC]"); 1948 } 1949 1950 return capabilities.toString(); 1951 } 1952 1953 /** 1954 * Build the Capability String for one protocol 1955 * @param index: index number of the protocol 1956 * @return security string for one protocol 1957 */ generateCapabilitiesStringPerProtocol(int index)1958 private String generateCapabilitiesStringPerProtocol(int index) { 1959 StringBuilder capability = new StringBuilder(); 1960 capability.append("[").append(protocolToString(protocol.get(index))); 1961 1962 if (index < keyManagement.size()) { 1963 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1964 capability.append((j == 0) ? "-" : "+").append( 1965 keyManagementToString(keyManagement.get(index).get(j))); 1966 } 1967 } 1968 if (index < pairwiseCipher.size()) { 1969 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1970 capability.append((j == 0) ? "-" : "+").append( 1971 cipherToString(pairwiseCipher.get(index).get(j))); 1972 } 1973 } 1974 capability.append("]"); 1975 return capability.toString(); 1976 } 1977 1978 /** 1979 * Build the duplicate Capability String for WPA2 1980 * @param cap: original capability String 1981 * @param index: index number of the protocol 1982 * @return security string for WPA2, empty String if protocol is not WPA2 1983 */ generateWPA2CapabilitiesString(String cap, int index)1984 private String generateWPA2CapabilitiesString(String cap, int index) { 1985 StringBuilder capWpa2 = new StringBuilder(); 1986 // if not WPA2, return empty String 1987 if (cap.contains("EAP_SUITE_B_192") 1988 || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP") 1989 && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) { 1990 return ""; 1991 } 1992 capWpa2.append("[").append("WPA2"); 1993 if (index < keyManagement.size()) { 1994 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1995 capWpa2.append((j == 0) ? "-" : "+").append( 1996 keyManagementToString(keyManagement.get(index).get(j))); 1997 // WPA3/WPA2 transition mode 1998 if (cap.contains("SAE")) { 1999 break; 2000 } 2001 } 2002 } 2003 if (index < pairwiseCipher.size()) { 2004 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 2005 capWpa2.append((j == 0) ? "-" : "+").append( 2006 cipherToString(pairwiseCipher.get(index).get(j))); 2007 } 2008 } 2009 capWpa2.append("]"); 2010 return capWpa2.toString(); 2011 } 2012 } 2013 2014 2015 /** 2016 * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will 2017 * only be present in scan results that are derived from a Beacon Frame, not from the more 2018 * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct. 2019 */ 2020 public static class TrafficIndicationMap { 2021 private static final int MAX_TIM_LENGTH = 254; 2022 private boolean mValid = false; 2023 public int mLength = 0; 2024 public int mDtimCount = -1; 2025 //Negative DTIM Period means no TIM element was given this frame. 2026 public int mDtimPeriod = -1; 2027 public int mBitmapControl = 0; 2028 2029 /** 2030 * Is this a valid TIM information element. 2031 */ isValid()2032 public boolean isValid() { 2033 return mValid; 2034 } 2035 2036 // Traffic Indication Map format (size unit: byte) 2037 // 2038 //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap | 2039 // 1 1 1 1 1 1 - 251 2040 // 2041 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2042 // stripped off already 2043 // from(InformationElement ie)2044 public void from(InformationElement ie) { 2045 mValid = false; 2046 if (ie == null || ie.bytes == null) return; 2047 mLength = ie.bytes.length; 2048 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2049 try { 2050 mDtimCount = data.get() & Constants.BYTE_MASK; 2051 mDtimPeriod = data.get() & Constants.BYTE_MASK; 2052 mBitmapControl = data.get() & Constants.BYTE_MASK; 2053 //A valid TIM element must have atleast one more byte 2054 data.get(); 2055 } catch (BufferUnderflowException e) { 2056 return; 2057 } 2058 if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { 2059 mValid = true; 2060 } 2061 } 2062 } 2063 2064 /** 2065 * This util class determines the 802.11 standard (a/b/g/n/ac/ax/be) being used 2066 */ 2067 public static class WifiMode { 2068 public static final int MODE_UNDEFINED = 0; // Unknown/undefined 2069 public static final int MODE_11A = 1; // 802.11a 2070 public static final int MODE_11B = 2; // 802.11b 2071 public static final int MODE_11G = 3; // 802.11g 2072 public static final int MODE_11N = 4; // 802.11n 2073 public static final int MODE_11AC = 5; // 802.11ac 2074 public static final int MODE_11AX = 6; // 802.11ax 2075 public static final int MODE_11BE = 7; // 802.11be 2076 //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A 2077 2078 /** 2079 * Use frequency, max supported rate, and the existence of EHT, HE, VHT, HT & ERP fields in 2080 * scan result to determine the 802.11 Wifi standard being used. 2081 */ determineMode(int frequency, int maxRate, boolean foundEht, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)2082 public static int determineMode(int frequency, int maxRate, boolean foundEht, 2083 boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp) { 2084 if (foundEht) { 2085 return MODE_11BE; 2086 } else if (foundHe) { 2087 return MODE_11AX; 2088 } else if (!ScanResult.is24GHz(frequency) && foundVht) { 2089 // Do not include subset of VHT on 2.4 GHz vendor extension 2090 // in consideration for reporting VHT. 2091 return MODE_11AC; 2092 } else if (foundHt) { 2093 return MODE_11N; 2094 } else if (foundErp) { 2095 return MODE_11G; 2096 } else if (ScanResult.is24GHz(frequency)) { 2097 if (maxRate < 24000000) { 2098 return MODE_11B; 2099 } else { 2100 return MODE_11G; 2101 } 2102 } else { 2103 return MODE_11A; 2104 } 2105 } 2106 2107 /** 2108 * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC/AX/BE> 2109 */ toString(int mode)2110 public static String toString(int mode) { 2111 switch(mode) { 2112 case MODE_11A: 2113 return "MODE_11A"; 2114 case MODE_11B: 2115 return "MODE_11B"; 2116 case MODE_11G: 2117 return "MODE_11G"; 2118 case MODE_11N: 2119 return "MODE_11N"; 2120 case MODE_11AC: 2121 return "MODE_11AC"; 2122 case MODE_11AX: 2123 return "MODE_11AX"; 2124 case MODE_11BE: 2125 return "MODE_11BE"; 2126 default: 2127 return "MODE_UNDEFINED"; 2128 } 2129 } 2130 } 2131 2132 /** 2133 * Parser for both the Supported Rates & Extended Supported Rates Information Elements 2134 */ 2135 public static class SupportedRates { 2136 public static final int MASK = 0x7F; // 0111 1111 2137 public boolean mValid = false; 2138 public ArrayList<Integer> mRates; 2139 SupportedRates()2140 public SupportedRates() { 2141 mRates = new ArrayList<Integer>(); 2142 } 2143 2144 /** 2145 * Is this a valid Supported Rates information element. 2146 */ isValid()2147 public boolean isValid() { 2148 return mValid; 2149 } 2150 2151 /** 2152 * get the Rate in bits/s from associated byteval 2153 */ getRateFromByte(int byteVal)2154 public static int getRateFromByte(int byteVal) { 2155 byteVal &= MASK; 2156 switch(byteVal) { 2157 case 2: 2158 return 1000000; 2159 case 4: 2160 return 2000000; 2161 case 11: 2162 return 5500000; 2163 case 12: 2164 return 6000000; 2165 case 18: 2166 return 9000000; 2167 case 22: 2168 return 11000000; 2169 case 24: 2170 return 12000000; 2171 case 36: 2172 return 18000000; 2173 case 44: 2174 return 22000000; 2175 case 48: 2176 return 24000000; 2177 case 66: 2178 return 33000000; 2179 case 72: 2180 return 36000000; 2181 case 96: 2182 return 48000000; 2183 case 108: 2184 return 54000000; 2185 default: 2186 //ERROR UNKNOWN RATE 2187 return -1; 2188 } 2189 } 2190 2191 // Supported Rates format (size unit: byte) 2192 // 2193 //| ElementID | Length | Supported Rates [7 Little Endian Info bits - 1 Flag bit] 2194 // 1 1 1 - 8 2195 // 2196 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2197 // stripped off already 2198 // from(InformationElement ie)2199 public void from(InformationElement ie) { 2200 mValid = false; 2201 if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1) { 2202 return; 2203 } 2204 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2205 try { 2206 for (int i = 0; i < ie.bytes.length; i++) { 2207 int rate = getRateFromByte(data.get()); 2208 if (rate > 0) { 2209 mRates.add(rate); 2210 } else { 2211 return; 2212 } 2213 } 2214 } catch (BufferUnderflowException e) { 2215 return; 2216 } 2217 mValid = true; 2218 return; 2219 } 2220 2221 /** 2222 * Lists the rates in a human readable string 2223 */ toString()2224 public String toString() { 2225 StringBuilder sbuf = new StringBuilder(); 2226 for (Integer rate : mRates) { 2227 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", "); 2228 } 2229 return sbuf.toString(); 2230 } 2231 } 2232 2233 /** 2234 * This util class determines country related information in beacon/probe response 2235 */ 2236 public static class Country { 2237 private boolean mValid = false; 2238 public String mCountryCode = "00"; 2239 2240 /** 2241 * Parse the Information Element Country Information field. Note that element ID and length 2242 * fields are already removed. 2243 * 2244 * Country IE format (size unit: byte) 2245 * 2246 * ElementID | Length | country string | triplet | padding 2247 * 1 1 3 Q*x 0 or 1 2248 * First two bytes of country string are country code 2249 * See 802.11 spec dot11CountryString definition. 2250 */ from(InformationElement ie)2251 public void from(InformationElement ie) { 2252 mValid = false; 2253 if (ie == null || ie.bytes == null || ie.bytes.length < 3) return; 2254 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2255 try { 2256 char letter1 = (char) (data.get() & Constants.BYTE_MASK); 2257 char letter2 = (char) (data.get() & Constants.BYTE_MASK); 2258 char letter3 = (char) (data.get() & Constants.BYTE_MASK); 2259 // See 802.11 spec dot11CountryString definition. 2260 // ' ', 'O', 'I' are for all operation, outdoor, indoor environments, respectively. 2261 mValid = (letter3 == ' ' || letter3 == 'O' || letter3 == 'I') 2262 && Character.isLetterOrDigit((int) letter1) 2263 && Character.isLetterOrDigit((int) letter2); 2264 if (mValid) { 2265 mCountryCode = (String.valueOf(letter1) + letter2).toUpperCase(Locale.US); 2266 } 2267 } catch (BufferUnderflowException e) { 2268 return; 2269 } 2270 } 2271 2272 /** 2273 * Is this a valid country information element. 2274 */ isValid()2275 public boolean isValid() { 2276 return mValid; 2277 } 2278 2279 /** 2280 * @return country code indicated in beacon/probe response frames 2281 */ getCountryCode()2282 public String getCountryCode() { 2283 return mCountryCode; 2284 } 2285 } 2286 } 2287