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 if (ie.bytes.length < startOffset + subElementLen) { 979 if (DBG) { 980 Log.w(TAG, "Invalid sub-element length: " + subElementLen); 981 } 982 // Skipping parsing of the IE 983 return false; 984 } 985 if (subElementId != PER_STA_SUB_ELEMENT_ID) { 986 // Skip this subelement, could be an unsupported one 987 startOffset += subElementLen; 988 continue; 989 } 990 991 MloLink link = new MloLink(); 992 link.setLinkId(ie.bytes[startOffset + PER_STA_SUB_ELEMENT_LINK_ID_OFFSET] 993 & PER_STA_SUB_ELEMENT_LINK_ID_MASK); 994 995 int staInfoLength = ie.bytes[startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET] 996 & Constants.BYTE_MASK; 997 if (subElementLen < PER_STA_SUB_ELEMENT_STA_INFO_OFFSET + staInfoLength) { 998 if (DBG) { 999 Log.w(TAG, "Invalid sta info length: " + staInfoLength); 1000 } 1001 // Skipping parsing of the IE 1002 return false; 1003 } 1004 1005 // Check if MAC Address is present 1006 if ((ie.bytes[startOffset + PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET] 1007 & PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK) != 0) { 1008 if (staInfoLength < 1 /*length*/ + 6 /*mac address*/) { 1009 if (DBG) { 1010 Log.w(TAG, "Invalid sta info length: " + staInfoLength); 1011 } 1012 // Skipping parsing of the IE 1013 return false; 1014 } 1015 1016 int macAddressOffset = startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET 1017 + PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET; 1018 link.setApMacAddress(MacAddress.fromBytes(Arrays.copyOfRange(ie.bytes, 1019 macAddressOffset, macAddressOffset + 6))); 1020 } 1021 1022 mAffiliatedLinks.add(link); 1023 1024 // Done with this sub-element 1025 startOffset += subElementLen; 1026 } 1027 1028 return true; 1029 } 1030 1031 /** 1032 * Parse Multi-Link Operation IE 1033 * 1034 * Multi-Link IE format as described in IEEE 802.11 specs, Section 9.4.2.312 1035 * 1036 * | ElementID | Length | ExtendedID | Control | Common Info | Link Info | 1037 * Octets: 1 1 1 2 Variable variable 1038 * 1039 * 1040 * Where Control field is described as, 1041 * 1042 * | Type | Reserved | Presence Bitmap | 1043 * Bits: 3 1 12 1044 * 1045 * Where the Presence Bitmap subfield is described as, 1046 * 1047 * | LinkId | BSS change count | MedSync | EML cap | MLD cap | Reserved | 1048 * Bits: 1 1 1 1 1 7 1049 * 1050 * 1051 * 1052 * Note: InformationElement.bytes has 'Element ID', 'Length', and 'Extended ID' 1053 * stripped off already 1054 * 1055 */ from(InformationElement ie)1056 public void from(InformationElement ie) { 1057 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 1058 || ie.idExt != InformationElement.EID_EXT_MULTI_LINK) { 1059 throw new IllegalArgumentException("Element id is not Multi-Link: " + ie.id); 1060 } 1061 1062 // Make sure the byte array length is at least the Control field size 1063 if (ie.bytes.length < CONTROL_FIELD_LEN) { 1064 if (DBG) { 1065 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1066 } 1067 // Skipping parsing of the IE 1068 return; 1069 } 1070 1071 // Check on IE type 1072 // Note only the BASIC type is allowed to be received from AP 1073 int type = ie.bytes[IE_TYPE_OFFSET] & IE_TYPE_MASK; 1074 if (type != TYPE_BASIC) { 1075 if (DBG) { 1076 Log.w(TAG, "Invalid/Unsupported Multi-Link IE type: " + type); 1077 } 1078 // Skipping parsing of the IE 1079 return; 1080 } 1081 1082 // Check length 1083 if (ie.bytes.length < BASIC_IE_MIN_LEN) { 1084 if (DBG) { 1085 Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length); 1086 } 1087 // Skipping parsing of the IE 1088 return; 1089 } 1090 1091 int commonInfoLength = parseCommonInfoField(ie); 1092 if (commonInfoLength == 0) { 1093 return; 1094 } 1095 1096 if (!parseLinkInfoField(ie, CONTROL_FIELD_LEN + commonInfoLength)) { 1097 return; 1098 } 1099 1100 mPresent = true; 1101 } 1102 } 1103 parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap)1104 private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) { 1105 int maxNumberSpatialStreams = 1; 1106 for (int i = 8; i >= 1; --i) { 1107 int streamMap = mcsMapToStreamMap(mcsMap, i); 1108 // 3 means unsupported 1109 if (streamMap != 3) { 1110 maxNumberSpatialStreams = i; 1111 break; 1112 } 1113 } 1114 if (DBG) { 1115 for (int i = 8; i >= 1; --i) { 1116 int streamMap = mcsMapToStreamMap(mcsMap, i); 1117 Log.d(TAG, "Rx MCS set " + i + " : " + streamMap); 1118 } 1119 } 1120 return maxNumberSpatialStreams; 1121 } 1122 mcsMapToStreamMap(int mcsMap, int i)1123 private static int mcsMapToStreamMap(int mcsMap, int i) { 1124 return (mcsMap >> ((i - 1) * 2)) & 0x3; 1125 } 1126 1127 public static class Interworking { 1128 public NetworkDetail.Ant ant = null; 1129 public boolean internet = false; 1130 public long hessid = 0L; 1131 from(InformationElement ie)1132 public void from(InformationElement ie) { 1133 if (ie.id != InformationElement.EID_INTERWORKING) { 1134 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id); 1135 } 1136 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1137 int anOptions = data.get() & Constants.BYTE_MASK; 1138 ant = NetworkDetail.Ant.values()[anOptions & 0x0f]; 1139 internet = (anOptions & 0x10) != 0; 1140 // There are only three possible lengths for the Interworking IE: 1141 // Len 1: Access Network Options only 1142 // Len 3: Access Network Options & Venue Info 1143 // Len 7: Access Network Options & HESSID 1144 // Len 9: Access Network Options, Venue Info, & HESSID 1145 if (ie.bytes.length != 1 1146 && ie.bytes.length != 3 1147 && ie.bytes.length != 7 1148 && ie.bytes.length != 9) { 1149 throw new IllegalArgumentException( 1150 "Bad Interworking element length: " + ie.bytes.length); 1151 } 1152 1153 if (ie.bytes.length == 3 || ie.bytes.length == 9) { 1154 int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2); 1155 } 1156 1157 if (ie.bytes.length == 7 || ie.bytes.length == 9) { 1158 hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6); 1159 } 1160 } 1161 } 1162 1163 public static class RoamingConsortium { 1164 public int anqpOICount = 0; 1165 1166 private long[] roamingConsortiums = null; 1167 getRoamingConsortiums()1168 public long[] getRoamingConsortiums() { 1169 return roamingConsortiums; 1170 } 1171 from(InformationElement ie)1172 public void from(InformationElement ie) { 1173 if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) { 1174 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " 1175 + ie.id); 1176 } 1177 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1178 anqpOICount = data.get() & Constants.BYTE_MASK; 1179 1180 int oi12Length = data.get() & Constants.BYTE_MASK; 1181 int oi1Length = oi12Length & Constants.NIBBLE_MASK; 1182 int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK; 1183 int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length; 1184 int oiCount = 0; 1185 if (oi1Length > 0) { 1186 oiCount++; 1187 if (oi2Length > 0) { 1188 oiCount++; 1189 if (oi3Length > 0) { 1190 oiCount++; 1191 } 1192 } 1193 } 1194 roamingConsortiums = new long[oiCount]; 1195 if (oi1Length > 0 && roamingConsortiums.length > 0) { 1196 roamingConsortiums[0] = 1197 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); 1198 } 1199 if (oi2Length > 0 && roamingConsortiums.length > 1) { 1200 roamingConsortiums[1] = 1201 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); 1202 } 1203 if (oi3Length > 0 && roamingConsortiums.length > 2) { 1204 roamingConsortiums[2] = 1205 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); 1206 } 1207 } 1208 } 1209 1210 public static class Vsa { 1211 private static final int ANQP_DOMAIN_ID_PRESENT_BIT = 0x04; 1212 private static final int ANQP_PPS_MO_ID_BIT = 0x02; 1213 private static final int OUI_WFA_ALLIANCE = 0x506F9a; 1214 private static final int OUI_TYPE_HS20 = 0x10; 1215 private static final int OUI_TYPE_MBO_OCE = 0x16; 1216 1217 public NetworkDetail.HSRelease hsRelease = null; 1218 public int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP. 1219 1220 public boolean IsMboCapable = false; 1221 public boolean IsMboApCellularDataAware = false; 1222 public boolean IsOceCapable = false; 1223 public int mboAssociationDisallowedReasonCode = 1224 MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT; 1225 public byte[] oui; 1226 parseVsaMboOce(InformationElement ie)1227 private void parseVsaMboOce(InformationElement ie) { 1228 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1229 1230 // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying 1231 // MBO-OCE OUI type. 1232 data.getInt(); 1233 1234 while (data.remaining() > 1) { 1235 int attrId = data.get() & Constants.BYTE_MASK; 1236 int attrLen = data.get() & Constants.BYTE_MASK; 1237 1238 if ((attrLen == 0) || (attrLen > data.remaining())) { 1239 return; 1240 } 1241 byte[] attrBytes = new byte[attrLen]; 1242 data.get(attrBytes); 1243 switch (attrId) { 1244 case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION: 1245 IsMboCapable = true; 1246 IsMboApCellularDataAware = (attrBytes[0] 1247 & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0; 1248 break; 1249 case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED: 1250 mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK; 1251 break; 1252 case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION: 1253 IsOceCapable = true; 1254 break; 1255 default: 1256 break; 1257 } 1258 } 1259 if (DBG) { 1260 Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: " 1261 + IsMboApCellularDataAware + " AssocDisAllowRC: " 1262 + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable); 1263 } 1264 } 1265 parseVsaHs20(InformationElement ie)1266 private void parseVsaHs20(InformationElement ie) { 1267 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1268 if (ie.bytes.length >= 5) { 1269 // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying 1270 // HS20 OUI type. 1271 data.getInt(); 1272 1273 int hsConf = data.get() & Constants.BYTE_MASK; 1274 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) { 1275 case 0: 1276 hsRelease = NetworkDetail.HSRelease.R1; 1277 break; 1278 case 1: 1279 hsRelease = NetworkDetail.HSRelease.R2; 1280 break; 1281 case 2: 1282 hsRelease = NetworkDetail.HSRelease.R3; 1283 break; 1284 default: 1285 hsRelease = NetworkDetail.HSRelease.Unknown; 1286 break; 1287 } 1288 if ((hsConf & ANQP_DOMAIN_ID_PRESENT_BIT) != 0) { 1289 // According to Hotspot 2.0 Specification v3.0 section 3.1.1 HS2.0 Indication 1290 // element, the size of the element is 5 bytes, and 2 bytes are optionally added 1291 // for each optional field; ANQP PPS MO ID and ANQP Domain ID present. 1292 int expectedSize = 7; 1293 if ((hsConf & ANQP_PPS_MO_ID_BIT) != 0) { 1294 expectedSize += 2; 1295 if (ie.bytes.length < expectedSize) { 1296 throw new IllegalArgumentException( 1297 "HS20 indication element too short: " + ie.bytes.length); 1298 } 1299 data.getShort(); // Skip 2 bytes 1300 } 1301 if (ie.bytes.length < expectedSize) { 1302 throw new IllegalArgumentException( 1303 "HS20 indication element too short: " + ie.bytes.length); 1304 } 1305 anqpDomainID = data.getShort() & Constants.SHORT_MASK; 1306 } 1307 } 1308 } 1309 1310 /** 1311 * Parse the vendor specific information element to build 1312 * InformationElemmentUtil.vsa object. 1313 * 1314 * @param ie -- Information Element 1315 */ from(InformationElement ie)1316 public void from(InformationElement ie) { 1317 if (ie.bytes.length < 3) { 1318 if (DBG) { 1319 Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length); 1320 } 1321 return; 1322 } 1323 1324 oui = Arrays.copyOfRange(ie.bytes, 0, 3); 1325 int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16) 1326 | ((ie.bytes[1] & Constants.BYTE_MASK) << 8) 1327 | ((ie.bytes[2] & Constants.BYTE_MASK))); 1328 1329 if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) { 1330 int ouiType = ie.bytes[3]; 1331 switch (ouiType) { 1332 case OUI_TYPE_HS20: 1333 parseVsaHs20(ie); 1334 break; 1335 case OUI_TYPE_MBO_OCE: 1336 parseVsaMboOce(ie); 1337 break; 1338 default: 1339 break; 1340 } 1341 } 1342 } 1343 } 1344 1345 /** 1346 * This IE contained a bit field indicating the capabilities being advertised by the STA. 1347 * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE. 1348 * 1349 * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each 1350 * bit. 1351 * 1352 * Here is the wire format of this IE: 1353 * | Element ID | Length | Capabilities | 1354 * 1 1 n 1355 */ 1356 public static class ExtendedCapabilities { 1357 private static final int RTT_RESP_ENABLE_BIT = 70; 1358 private static final int SSID_UTF8_BIT = 48; 1359 1360 public BitSet capabilitiesBitSet; 1361 1362 /** 1363 * @return true if SSID should be interpreted using UTF-8 encoding 1364 */ isStrictUtf8()1365 public boolean isStrictUtf8() { 1366 return capabilitiesBitSet.get(SSID_UTF8_BIT); 1367 } 1368 1369 /** 1370 * @return true if 802.11 MC RTT Response is enabled 1371 */ is80211McRTTResponder()1372 public boolean is80211McRTTResponder() { 1373 return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT); 1374 } 1375 ExtendedCapabilities()1376 public ExtendedCapabilities() { 1377 capabilitiesBitSet = new BitSet(); 1378 } 1379 ExtendedCapabilities(ExtendedCapabilities other)1380 public ExtendedCapabilities(ExtendedCapabilities other) { 1381 capabilitiesBitSet = other.capabilitiesBitSet; 1382 } 1383 1384 /** 1385 * Parse an ExtendedCapabilities from the IE containing raw bytes. 1386 * 1387 * @param ie The Information element data 1388 */ from(InformationElement ie)1389 public void from(InformationElement ie) { 1390 capabilitiesBitSet = BitSet.valueOf(ie.bytes); 1391 } 1392 } 1393 1394 /** 1395 * parse beacon to build the capabilities 1396 * 1397 * This class is used to build the capabilities string of the scan results coming 1398 * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, 1399 * and builds the ScanResult.capabilities String in a way that mirrors the values returned 1400 * by wpa_supplicant. 1401 */ 1402 public static class Capabilities { 1403 private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000; 1404 private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000; 1405 private static final short WPA_VENDOR_OUI_VERSION = 0x0001; 1406 private static final int OWE_VENDOR_OUI_TYPE = 0x1c9a6f50; 1407 private static final short RSNE_VERSION = 0x0001; 1408 1409 private static final int WPA_AKM_EAP = 0x01f25000; 1410 private static final int WPA_AKM_PSK = 0x02f25000; 1411 1412 private static final int RSN_AKM_EAP = 0x01ac0f00; 1413 private static final int RSN_AKM_PSK = 0x02ac0f00; 1414 private static final int RSN_AKM_FT_EAP = 0x03ac0f00; 1415 private static final int RSN_AKM_FT_PSK = 0x04ac0f00; 1416 private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00; 1417 private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00; 1418 private static final int RSN_AKM_SAE = 0x08ac0f00; 1419 private static final int RSN_AKM_FT_SAE = 0x09ac0f00; 1420 private static final int RSN_AKM_OWE = 0x12ac0f00; 1421 private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; 1422 private static final int RSN_OSEN = 0x019a6f50; 1423 private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00; 1424 private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00; 1425 private static final int RSN_AKM_DPP = 0x029a6f50; 1426 1427 private static final int WPA_CIPHER_NONE = 0x00f25000; 1428 private static final int WPA_CIPHER_TKIP = 0x02f25000; 1429 private static final int WPA_CIPHER_CCMP = 0x04f25000; 1430 1431 private static final int RSN_CIPHER_NONE = 0x00ac0f00; 1432 private static final int RSN_CIPHER_TKIP = 0x02ac0f00; 1433 private static final int RSN_CIPHER_CCMP = 0x04ac0f00; 1434 private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00; 1435 private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00; 1436 private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00; 1437 private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00; 1438 private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00; 1439 private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00; 1440 1441 // RSN capability bit definition 1442 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6; 1443 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7; 1444 1445 public List<Integer> protocol; 1446 public List<List<Integer>> keyManagement; 1447 public List<List<Integer>> pairwiseCipher; 1448 public List<Integer> groupCipher; 1449 public List<Integer> groupManagementCipher; 1450 public boolean isESS; 1451 public boolean isIBSS; 1452 public boolean isPrivacy; 1453 public boolean isWPS; 1454 public boolean isManagementFrameProtectionRequired; 1455 public boolean isManagementFrameProtectionCapable; 1456 Capabilities()1457 public Capabilities() { 1458 } 1459 1460 // RSNE format (size unit: byte) 1461 // 1462 // | Element ID | Length | Version | Group Data Cipher Suite | 1463 // 1 1 2 4 1464 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1465 // 2 4 * m 1466 // | AKM Suite Count | AKM Suite List | RSN Capabilities | 1467 // 2 4 * n 2 1468 // | PMKID Count | PMKID List | Group Management Cipher Suite | 1469 // 2 16 * s 4 1470 // 1471 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1472 // stripped off already parseRsnElement(InformationElement ie)1473 private void parseRsnElement(InformationElement ie) { 1474 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1475 1476 try { 1477 // version 1478 if (buf.getShort() != RSNE_VERSION) { 1479 // incorrect version 1480 return; 1481 } 1482 1483 // found the RSNE IE, hence start building the capability string 1484 protocol.add(ScanResult.PROTOCOL_RSN); 1485 1486 // group data cipher suite 1487 groupCipher.add(parseRsnCipher(buf.getInt())); 1488 1489 // pairwise cipher suite count 1490 short cipherCount = buf.getShort(); 1491 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>(); 1492 // pairwise cipher suite list 1493 for (int i = 0; i < cipherCount; i++) { 1494 rsnPairwiseCipher.add(parseRsnCipher(buf.getInt())); 1495 } 1496 pairwiseCipher.add(rsnPairwiseCipher); 1497 1498 // AKM 1499 // AKM suite count 1500 short akmCount = buf.getShort(); 1501 ArrayList<Integer> rsnKeyManagement = new ArrayList<>(); 1502 1503 for (int i = 0; i < akmCount; i++) { 1504 int akm = buf.getInt(); 1505 switch (akm) { 1506 case RSN_AKM_EAP: 1507 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1508 break; 1509 case RSN_AKM_PSK: 1510 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1511 break; 1512 case RSN_AKM_FT_EAP: 1513 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP); 1514 break; 1515 case RSN_AKM_FT_PSK: 1516 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK); 1517 break; 1518 case RSN_AKM_EAP_SHA256: 1519 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256); 1520 break; 1521 case RSN_AKM_PSK_SHA256: 1522 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256); 1523 break; 1524 case RSN_AKM_SAE: 1525 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE); 1526 break; 1527 case RSN_AKM_FT_SAE: 1528 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE); 1529 break; 1530 case RSN_AKM_OWE: 1531 rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE); 1532 break; 1533 case RSN_AKM_EAP_SUITE_B_192: 1534 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); 1535 break; 1536 case RSN_OSEN: 1537 rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); 1538 break; 1539 case RSN_AKM_EAP_FILS_SHA256: 1540 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); 1541 break; 1542 case RSN_AKM_EAP_FILS_SHA384: 1543 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); 1544 break; 1545 case RSN_AKM_DPP: 1546 rsnKeyManagement.add(ScanResult.KEY_MGMT_DPP); 1547 break; 1548 default: 1549 rsnKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN); 1550 break; 1551 } 1552 } 1553 // Default AKM 1554 if (rsnKeyManagement.isEmpty()) { 1555 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1556 } 1557 keyManagement.add(rsnKeyManagement); 1558 1559 // RSN capabilities (optional), 1560 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016 1561 if (buf.remaining() < 2) return; 1562 int rsnCaps = buf.getShort(); 1563 1564 if (buf.remaining() < 2) return; 1565 // PMKID, it's not used, drop it if exists (optional). 1566 int rsnPmkIdCount = buf.getShort(); 1567 for (int i = 0; i < rsnPmkIdCount; i++) { 1568 // Each PMKID element length in the PMKID List is 16 bytes 1569 byte[] tmp = new byte[16]; 1570 buf.get(tmp); 1571 } 1572 1573 // Group management cipher suite (optional). 1574 if (buf.remaining() < 4) return; 1575 groupManagementCipher.add(parseRsnCipher(buf.getInt())); 1576 isManagementFrameProtectionRequired = !groupManagementCipher.isEmpty() 1577 && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps); 1578 isManagementFrameProtectionCapable = !groupManagementCipher.isEmpty() 1579 && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps); 1580 } catch (BufferUnderflowException e) { 1581 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow"); 1582 } 1583 } 1584 parseWpaCipher(int cipher)1585 private static @Cipher int parseWpaCipher(int cipher) { 1586 switch (cipher) { 1587 case WPA_CIPHER_NONE: 1588 return ScanResult.CIPHER_NONE; 1589 case WPA_CIPHER_TKIP: 1590 return ScanResult.CIPHER_TKIP; 1591 case WPA_CIPHER_CCMP: 1592 return ScanResult.CIPHER_CCMP; 1593 default: 1594 Log.w("IE_Capabilities", "Unknown WPA cipher suite: " 1595 + Integer.toHexString(cipher)); 1596 return ScanResult.CIPHER_NONE; 1597 } 1598 } 1599 parseRsnCipher(int cipher)1600 private static @Cipher int parseRsnCipher(int cipher) { 1601 switch (cipher) { 1602 case RSN_CIPHER_NONE: 1603 return ScanResult.CIPHER_NONE; 1604 case RSN_CIPHER_TKIP: 1605 return ScanResult.CIPHER_TKIP; 1606 case RSN_CIPHER_CCMP: 1607 return ScanResult.CIPHER_CCMP; 1608 case RSN_CIPHER_GCMP_256: 1609 return ScanResult.CIPHER_GCMP_256; 1610 case RSN_CIPHER_NO_GROUP_ADDRESSED: 1611 return ScanResult.CIPHER_NO_GROUP_ADDRESSED; 1612 case RSN_CIPHER_GCMP_128: 1613 return ScanResult.CIPHER_GCMP_128; 1614 case RSN_CIPHER_BIP_GMAC_128: 1615 return ScanResult.CIPHER_BIP_GMAC_128; 1616 case RSN_CIPHER_BIP_GMAC_256: 1617 return ScanResult.CIPHER_BIP_GMAC_256; 1618 case RSN_CIPHER_BIP_CMAC_256: 1619 return ScanResult.CIPHER_BIP_CMAC_256; 1620 default: 1621 Log.w("IE_Capabilities", "Unknown RSN cipher suite: " 1622 + Integer.toHexString(cipher)); 1623 return ScanResult.CIPHER_NONE; 1624 } 1625 } 1626 isWpsElement(InformationElement ie)1627 private static boolean isWpsElement(InformationElement ie) { 1628 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1629 try { 1630 // WPS OUI and type 1631 return (buf.getInt() == WPS_VENDOR_OUI_TYPE); 1632 } catch (BufferUnderflowException e) { 1633 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1634 return false; 1635 } 1636 } 1637 isWpaOneElement(InformationElement ie)1638 private static boolean isWpaOneElement(InformationElement ie) { 1639 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1640 1641 try { 1642 // WPA OUI and type 1643 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE); 1644 } catch (BufferUnderflowException e) { 1645 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1646 return false; 1647 } 1648 } 1649 1650 // WPA type 1 format (size unit: byte) 1651 // 1652 // | Element ID | Length | OUI | Type | Version | 1653 // 1 1 3 1 2 1654 // | Group Data Cipher Suite | 1655 // 4 1656 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1657 // 2 4 * m 1658 // | AKM Suite Count | AKM Suite List | 1659 // 2 4 * n 1660 // 1661 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1662 // stripped off already 1663 // parseWpaOneElement(InformationElement ie)1664 private void parseWpaOneElement(InformationElement ie) { 1665 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1666 1667 try { 1668 // skip WPA OUI and type parsing. isWpaOneElement() should have 1669 // been called for verification before we reach here. 1670 buf.getInt(); 1671 1672 // version 1673 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) { 1674 // incorrect version 1675 return; 1676 } 1677 1678 // start building the string 1679 protocol.add(ScanResult.PROTOCOL_WPA); 1680 1681 // group data cipher suite 1682 groupCipher.add(parseWpaCipher(buf.getInt())); 1683 1684 // pairwise cipher suite count 1685 short cipherCount = buf.getShort(); 1686 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>(); 1687 // pairwise chipher suite list 1688 for (int i = 0; i < cipherCount; i++) { 1689 wpaPairwiseCipher.add(parseWpaCipher(buf.getInt())); 1690 } 1691 pairwiseCipher.add(wpaPairwiseCipher); 1692 1693 // AKM 1694 // AKM suite count 1695 short akmCount = buf.getShort(); 1696 ArrayList<Integer> wpaKeyManagement = new ArrayList<>(); 1697 1698 // AKM suite list 1699 for (int i = 0; i < akmCount; i++) { 1700 int akm = buf.getInt(); 1701 switch (akm) { 1702 case WPA_AKM_EAP: 1703 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1704 break; 1705 case WPA_AKM_PSK: 1706 wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1707 break; 1708 default: 1709 wpaKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN); 1710 break; 1711 } 1712 } 1713 // Default AKM 1714 if (wpaKeyManagement.isEmpty()) { 1715 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1716 } 1717 keyManagement.add(wpaKeyManagement); 1718 } catch (BufferUnderflowException e) { 1719 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow"); 1720 } 1721 } 1722 1723 /** 1724 * Parse the Information Element and the 16-bit Capability Information field 1725 * to build the InformationElemmentUtil.capabilities object. 1726 * 1727 * @param ies -- Information Element array 1728 * @param beaconCap -- 16-bit Beacon Capability Information field 1729 * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device 1730 * @param freq -- Frequency on which frame/beacon was transmitted. 1731 * Some parsing may be affected such as DMG parameters in 1732 * DMG (60GHz) beacon. 1733 */ 1734 from(InformationElement[] ies, int beaconCap, boolean isOweSupported, int freq)1735 public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported, 1736 int freq) { 1737 protocol = new ArrayList<>(); 1738 keyManagement = new ArrayList<>(); 1739 groupCipher = new ArrayList<>(); 1740 pairwiseCipher = new ArrayList<>(); 1741 groupManagementCipher = new ArrayList<>(); 1742 1743 if (ies == null) { 1744 return; 1745 } 1746 isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0; 1747 if (ScanResult.is60GHz(freq)) { 1748 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */ 1749 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS) 1750 == NativeScanResult.BSS_CAPABILITY_DMG_ESS) { 1751 isESS = true; 1752 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) { 1753 isIBSS = true; 1754 } 1755 } else { 1756 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0; 1757 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0; 1758 } 1759 for (InformationElement ie : ies) { 1760 WifiNl80211Manager.OemSecurityType oemSecurityType = 1761 WifiNl80211Manager.parseOemSecurityTypeElement( 1762 ie.id, ie.idExt, ie.bytes); 1763 if (oemSecurityType != null 1764 && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) { 1765 protocol.add(oemSecurityType.protocol); 1766 keyManagement.add(oemSecurityType.keyManagement); 1767 pairwiseCipher.add(oemSecurityType.pairwiseCipher); 1768 groupCipher.add(oemSecurityType.groupCipher); 1769 } 1770 1771 if (ie.id == InformationElement.EID_RSN) { 1772 parseRsnElement(ie); 1773 } 1774 1775 if (ie.id == InformationElement.EID_VSA) { 1776 if (isWpaOneElement(ie)) { 1777 parseWpaOneElement(ie); 1778 } 1779 if (isWpsElement(ie)) { 1780 // TODO(b/62134557): parse WPS IE to provide finer granularity information. 1781 isWPS = true; 1782 } 1783 if (isOweSupported && isOweElement(ie)) { 1784 /* From RFC 8110: Once the client and AP have finished 802.11 association, 1785 they then complete the Diffie-Hellman key exchange and create a Pairwise 1786 Master Key (PMK) and its associated identifier, PMKID [IEEE802.11]. 1787 Upon completion of 802.11 association, the AP initiates the 4-way 1788 handshake to the client using the PMK generated above. The 4-way 1789 handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation 1790 Key (KCK), and a Message Integrity Code (MIC) to use for protection 1791 of the frames that define the 4-way handshake. 1792 1793 We check if OWE is supported here because we are adding the OWE 1794 capabilities to the Open BSS. Non-supporting devices need to see this 1795 open network and ignore this element. Supporting devices need to hide 1796 the Open BSS of OWE in transition mode and connect to the Hidden one. 1797 */ 1798 protocol.add(ScanResult.PROTOCOL_RSN); 1799 groupCipher.add(ScanResult.CIPHER_CCMP); 1800 ArrayList<Integer> owePairwiseCipher = new ArrayList<>(); 1801 owePairwiseCipher.add(ScanResult.CIPHER_CCMP); 1802 pairwiseCipher.add(owePairwiseCipher); 1803 ArrayList<Integer> oweKeyManagement = new ArrayList<>(); 1804 oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION); 1805 keyManagement.add(oweKeyManagement); 1806 } 1807 } 1808 } 1809 } 1810 isOweElement(InformationElement ie)1811 private static boolean isOweElement(InformationElement ie) { 1812 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1813 try { 1814 // OWE OUI and type 1815 return (buf.getInt() == OWE_VENDOR_OUI_TYPE); 1816 } catch (BufferUnderflowException e) { 1817 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1818 return false; 1819 } 1820 } 1821 protocolToString(@rotocol int protocol)1822 private String protocolToString(@Protocol int protocol) { 1823 switch (protocol) { 1824 case ScanResult.PROTOCOL_NONE: 1825 return "None"; 1826 case ScanResult.PROTOCOL_WPA: 1827 return "WPA"; 1828 case ScanResult.PROTOCOL_RSN: 1829 return "RSN"; 1830 case ScanResult.PROTOCOL_OSEN: 1831 return "OSEN"; 1832 case ScanResult.PROTOCOL_WAPI: 1833 return "WAPI"; 1834 default: 1835 return "?"; 1836 } 1837 } 1838 keyManagementToString(@eyMgmt int akm)1839 private String keyManagementToString(@KeyMgmt int akm) { 1840 switch (akm) { 1841 case ScanResult.KEY_MGMT_NONE: 1842 return "None"; 1843 case ScanResult.KEY_MGMT_PSK: 1844 return "PSK"; 1845 case ScanResult.KEY_MGMT_EAP: 1846 return "EAP/SHA1"; 1847 case ScanResult.KEY_MGMT_FT_EAP: 1848 return "FT/EAP"; 1849 case ScanResult.KEY_MGMT_FT_PSK: 1850 return "FT/PSK"; 1851 case ScanResult.KEY_MGMT_EAP_SHA256: 1852 return "EAP/SHA256"; 1853 case ScanResult.KEY_MGMT_PSK_SHA256: 1854 return "PSK-SHA256"; 1855 case ScanResult.KEY_MGMT_OWE: 1856 return "OWE"; 1857 case ScanResult.KEY_MGMT_OWE_TRANSITION: 1858 return "OWE_TRANSITION"; 1859 case ScanResult.KEY_MGMT_SAE: 1860 return "SAE"; 1861 case ScanResult.KEY_MGMT_FT_SAE: 1862 return "FT/SAE"; 1863 case ScanResult.KEY_MGMT_EAP_SUITE_B_192: 1864 return "EAP_SUITE_B_192"; 1865 case ScanResult.KEY_MGMT_OSEN: 1866 return "OSEN"; 1867 case ScanResult.KEY_MGMT_WAPI_PSK: 1868 return "WAPI-PSK"; 1869 case ScanResult.KEY_MGMT_WAPI_CERT: 1870 return "WAPI-CERT"; 1871 case ScanResult.KEY_MGMT_FILS_SHA256: 1872 return "EAP-FILS-SHA256"; 1873 case ScanResult.KEY_MGMT_FILS_SHA384: 1874 return "EAP-FILS-SHA384"; 1875 case ScanResult.KEY_MGMT_DPP: 1876 return "DPP"; 1877 default: 1878 return "?"; 1879 } 1880 } 1881 cipherToString(@ipher int cipher)1882 private String cipherToString(@Cipher int cipher) { 1883 switch (cipher) { 1884 case ScanResult.CIPHER_NONE: 1885 return "None"; 1886 case ScanResult.CIPHER_CCMP: 1887 return "CCMP"; 1888 case ScanResult.CIPHER_GCMP_256: 1889 return "GCMP-256"; 1890 case ScanResult.CIPHER_TKIP: 1891 return "TKIP"; 1892 case ScanResult.CIPHER_SMS4: 1893 return "SMS4"; 1894 default: 1895 return "?"; 1896 } 1897 } 1898 1899 /** 1900 * Build the ScanResult.capabilities String. 1901 * 1902 * @return security string that mirrors what wpa_supplicant generates 1903 */ generateCapabilitiesString()1904 public String generateCapabilitiesString() { 1905 StringBuilder capabilities = new StringBuilder(); 1906 // private Beacon without an RSNE or WPA IE, hence WEP0 1907 boolean isWEP = (protocol.isEmpty()) && isPrivacy; 1908 1909 if (isWEP) { 1910 capabilities.append("[WEP]"); 1911 } 1912 for (int i = 0; i < protocol.size(); i++) { 1913 String capability = generateCapabilitiesStringPerProtocol(i); 1914 // add duplicate capabilities for WPA2 for backward compatibility: 1915 // duplicate "RSN" entries as "WPA2" 1916 String capWpa2 = generateWPA2CapabilitiesString(capability, i); 1917 capabilities.append(capWpa2); 1918 capabilities.append(capability); 1919 } 1920 if (isESS) { 1921 capabilities.append("[ESS]"); 1922 } 1923 if (isIBSS) { 1924 capabilities.append("[IBSS]"); 1925 } 1926 if (isWPS) { 1927 capabilities.append("[WPS]"); 1928 } 1929 if (!groupManagementCipher.isEmpty()) { 1930 if (isManagementFrameProtectionRequired) { 1931 capabilities.append("[MFPR]"); 1932 } 1933 if (isManagementFrameProtectionCapable) { 1934 capabilities.append("[MFPC]"); 1935 } 1936 } 1937 1938 return capabilities.toString(); 1939 } 1940 1941 /** 1942 * Build the Capability String for one protocol 1943 * @param index: index number of the protocol 1944 * @return security string for one protocol 1945 */ generateCapabilitiesStringPerProtocol(int index)1946 private String generateCapabilitiesStringPerProtocol(int index) { 1947 StringBuilder capability = new StringBuilder(); 1948 capability.append("[").append(protocolToString(protocol.get(index))); 1949 1950 if (index < keyManagement.size()) { 1951 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1952 capability.append((j == 0) ? "-" : "+").append( 1953 keyManagementToString(keyManagement.get(index).get(j))); 1954 } 1955 } 1956 if (index < pairwiseCipher.size()) { 1957 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1958 capability.append((j == 0) ? "-" : "+").append( 1959 cipherToString(pairwiseCipher.get(index).get(j))); 1960 } 1961 } 1962 capability.append("]"); 1963 return capability.toString(); 1964 } 1965 1966 /** 1967 * Build the duplicate Capability String for WPA2 1968 * @param cap: original capability String 1969 * @param index: index number of the protocol 1970 * @return security string for WPA2, empty String if protocol is not WPA2 1971 */ generateWPA2CapabilitiesString(String cap, int index)1972 private String generateWPA2CapabilitiesString(String cap, int index) { 1973 StringBuilder capWpa2 = new StringBuilder(); 1974 // if not WPA2, return empty String 1975 if (cap.contains("EAP_SUITE_B_192") 1976 || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP") 1977 && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) { 1978 return ""; 1979 } 1980 capWpa2.append("[").append("WPA2"); 1981 if (index < keyManagement.size()) { 1982 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1983 capWpa2.append((j == 0) ? "-" : "+").append( 1984 keyManagementToString(keyManagement.get(index).get(j))); 1985 // WPA3/WPA2 transition mode 1986 if (cap.contains("SAE")) { 1987 break; 1988 } 1989 } 1990 } 1991 if (index < pairwiseCipher.size()) { 1992 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1993 capWpa2.append((j == 0) ? "-" : "+").append( 1994 cipherToString(pairwiseCipher.get(index).get(j))); 1995 } 1996 } 1997 capWpa2.append("]"); 1998 return capWpa2.toString(); 1999 } 2000 } 2001 2002 2003 /** 2004 * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will 2005 * only be present in scan results that are derived from a Beacon Frame, not from the more 2006 * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct. 2007 */ 2008 public static class TrafficIndicationMap { 2009 private static final int MAX_TIM_LENGTH = 254; 2010 private boolean mValid = false; 2011 public int mLength = 0; 2012 public int mDtimCount = -1; 2013 //Negative DTIM Period means no TIM element was given this frame. 2014 public int mDtimPeriod = -1; 2015 public int mBitmapControl = 0; 2016 2017 /** 2018 * Is this a valid TIM information element. 2019 */ isValid()2020 public boolean isValid() { 2021 return mValid; 2022 } 2023 2024 // Traffic Indication Map format (size unit: byte) 2025 // 2026 //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap | 2027 // 1 1 1 1 1 1 - 251 2028 // 2029 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2030 // stripped off already 2031 // from(InformationElement ie)2032 public void from(InformationElement ie) { 2033 mValid = false; 2034 if (ie == null || ie.bytes == null) return; 2035 mLength = ie.bytes.length; 2036 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2037 try { 2038 mDtimCount = data.get() & Constants.BYTE_MASK; 2039 mDtimPeriod = data.get() & Constants.BYTE_MASK; 2040 mBitmapControl = data.get() & Constants.BYTE_MASK; 2041 //A valid TIM element must have atleast one more byte 2042 data.get(); 2043 } catch (BufferUnderflowException e) { 2044 return; 2045 } 2046 if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { 2047 mValid = true; 2048 } 2049 } 2050 } 2051 2052 /** 2053 * This util class determines the 802.11 standard (a/b/g/n/ac/ax/be) being used 2054 */ 2055 public static class WifiMode { 2056 public static final int MODE_UNDEFINED = 0; // Unknown/undefined 2057 public static final int MODE_11A = 1; // 802.11a 2058 public static final int MODE_11B = 2; // 802.11b 2059 public static final int MODE_11G = 3; // 802.11g 2060 public static final int MODE_11N = 4; // 802.11n 2061 public static final int MODE_11AC = 5; // 802.11ac 2062 public static final int MODE_11AX = 6; // 802.11ax 2063 public static final int MODE_11BE = 7; // 802.11be 2064 //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A 2065 2066 /** 2067 * Use frequency, max supported rate, and the existence of EHT, HE, VHT, HT & ERP fields in 2068 * scan result to determine the 802.11 Wifi standard being used. 2069 */ determineMode(int frequency, int maxRate, boolean foundEht, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)2070 public static int determineMode(int frequency, int maxRate, boolean foundEht, 2071 boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp) { 2072 if (foundEht) { 2073 return MODE_11BE; 2074 } else if (foundHe) { 2075 return MODE_11AX; 2076 } else if (!ScanResult.is24GHz(frequency) && foundVht) { 2077 // Do not include subset of VHT on 2.4 GHz vendor extension 2078 // in consideration for reporting VHT. 2079 return MODE_11AC; 2080 } else if (foundHt) { 2081 return MODE_11N; 2082 } else if (foundErp) { 2083 return MODE_11G; 2084 } else if (ScanResult.is24GHz(frequency)) { 2085 if (maxRate < 24000000) { 2086 return MODE_11B; 2087 } else { 2088 return MODE_11G; 2089 } 2090 } else { 2091 return MODE_11A; 2092 } 2093 } 2094 2095 /** 2096 * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC/AX/BE> 2097 */ toString(int mode)2098 public static String toString(int mode) { 2099 switch(mode) { 2100 case MODE_11A: 2101 return "MODE_11A"; 2102 case MODE_11B: 2103 return "MODE_11B"; 2104 case MODE_11G: 2105 return "MODE_11G"; 2106 case MODE_11N: 2107 return "MODE_11N"; 2108 case MODE_11AC: 2109 return "MODE_11AC"; 2110 case MODE_11AX: 2111 return "MODE_11AX"; 2112 case MODE_11BE: 2113 return "MODE_11BE"; 2114 default: 2115 return "MODE_UNDEFINED"; 2116 } 2117 } 2118 } 2119 2120 /** 2121 * Parser for both the Supported Rates & Extended Supported Rates Information Elements 2122 */ 2123 public static class SupportedRates { 2124 public static final int MASK = 0x7F; // 0111 1111 2125 public boolean mValid = false; 2126 public ArrayList<Integer> mRates; 2127 SupportedRates()2128 public SupportedRates() { 2129 mRates = new ArrayList<Integer>(); 2130 } 2131 2132 /** 2133 * Is this a valid Supported Rates information element. 2134 */ isValid()2135 public boolean isValid() { 2136 return mValid; 2137 } 2138 2139 /** 2140 * get the Rate in bits/s from associated byteval 2141 */ getRateFromByte(int byteVal)2142 public static int getRateFromByte(int byteVal) { 2143 byteVal &= MASK; 2144 switch(byteVal) { 2145 case 2: 2146 return 1000000; 2147 case 4: 2148 return 2000000; 2149 case 11: 2150 return 5500000; 2151 case 12: 2152 return 6000000; 2153 case 18: 2154 return 9000000; 2155 case 22: 2156 return 11000000; 2157 case 24: 2158 return 12000000; 2159 case 36: 2160 return 18000000; 2161 case 44: 2162 return 22000000; 2163 case 48: 2164 return 24000000; 2165 case 66: 2166 return 33000000; 2167 case 72: 2168 return 36000000; 2169 case 96: 2170 return 48000000; 2171 case 108: 2172 return 54000000; 2173 default: 2174 //ERROR UNKNOWN RATE 2175 return -1; 2176 } 2177 } 2178 2179 // Supported Rates format (size unit: byte) 2180 // 2181 //| ElementID | Length | Supported Rates [7 Little Endian Info bits - 1 Flag bit] 2182 // 1 1 1 - 8 2183 // 2184 // Note: InformationElement.bytes has 'Element ID' and 'Length' 2185 // stripped off already 2186 // from(InformationElement ie)2187 public void from(InformationElement ie) { 2188 mValid = false; 2189 if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1) { 2190 return; 2191 } 2192 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2193 try { 2194 for (int i = 0; i < ie.bytes.length; i++) { 2195 int rate = getRateFromByte(data.get()); 2196 if (rate > 0) { 2197 mRates.add(rate); 2198 } else { 2199 return; 2200 } 2201 } 2202 } catch (BufferUnderflowException e) { 2203 return; 2204 } 2205 mValid = true; 2206 return; 2207 } 2208 2209 /** 2210 * Lists the rates in a human readable string 2211 */ toString()2212 public String toString() { 2213 StringBuilder sbuf = new StringBuilder(); 2214 for (Integer rate : mRates) { 2215 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", "); 2216 } 2217 return sbuf.toString(); 2218 } 2219 } 2220 2221 /** 2222 * This util class determines country related information in beacon/probe response 2223 */ 2224 public static class Country { 2225 private boolean mValid = false; 2226 public String mCountryCode = "00"; 2227 2228 /** 2229 * Parse the Information Element Country Information field. Note that element ID and length 2230 * fields are already removed. 2231 * 2232 * Country IE format (size unit: byte) 2233 * 2234 * ElementID | Length | country string | triplet | padding 2235 * 1 1 3 Q*x 0 or 1 2236 * First two bytes of country string are country code 2237 * See 802.11 spec dot11CountryString definition. 2238 */ from(InformationElement ie)2239 public void from(InformationElement ie) { 2240 mValid = false; 2241 if (ie == null || ie.bytes == null || ie.bytes.length < 3) return; 2242 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 2243 try { 2244 char letter1 = (char) (data.get() & Constants.BYTE_MASK); 2245 char letter2 = (char) (data.get() & Constants.BYTE_MASK); 2246 char letter3 = (char) (data.get() & Constants.BYTE_MASK); 2247 // See 802.11 spec dot11CountryString definition. 2248 // ' ', 'O', 'I' are for all operation, outdoor, indoor environments, respectively. 2249 mValid = (letter3 == ' ' || letter3 == 'O' || letter3 == 'I') 2250 && Character.isLetterOrDigit((int) letter1) 2251 && Character.isLetterOrDigit((int) letter2); 2252 if (mValid) { 2253 mCountryCode = (String.valueOf(letter1) + letter2).toUpperCase(Locale.US); 2254 } 2255 } catch (BufferUnderflowException e) { 2256 return; 2257 } 2258 } 2259 2260 /** 2261 * Is this a valid country information element. 2262 */ isValid()2263 public boolean isValid() { 2264 return mValid; 2265 } 2266 2267 /** 2268 * @return country code indicated in beacon/probe response frames 2269 */ getCountryCode()2270 public String getCountryCode() { 2271 return mCountryCode; 2272 } 2273 } 2274 } 2275