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.wifi.ScanResult; 19 import android.net.wifi.ScanResult.InformationElement; 20 import android.net.wifi.WifiAnnotations.Cipher; 21 import android.net.wifi.WifiAnnotations.KeyMgmt; 22 import android.net.wifi.WifiAnnotations.Protocol; 23 import android.net.wifi.WifiScanner; 24 import android.net.wifi.nl80211.NativeScanResult; 25 import android.net.wifi.nl80211.WifiNl80211Manager; 26 import android.util.Log; 27 28 import com.android.server.wifi.ByteBufferReader; 29 import com.android.server.wifi.MboOceConstants; 30 import com.android.server.wifi.hotspot2.NetworkDetail; 31 import com.android.server.wifi.hotspot2.anqp.Constants; 32 33 import java.nio.BufferUnderflowException; 34 import java.nio.ByteBuffer; 35 import java.nio.ByteOrder; 36 import java.util.ArrayList; 37 import java.util.BitSet; 38 import java.util.List; 39 40 public class InformationElementUtil { 41 private static final String TAG = "InformationElementUtil"; 42 private static final boolean DBG = false; parseInformationElements(byte[] bytes)43 public static InformationElement[] parseInformationElements(byte[] bytes) { 44 if (bytes == null) { 45 return new InformationElement[0]; 46 } 47 ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 48 49 ArrayList<InformationElement> infoElements = new ArrayList<>(); 50 boolean found_ssid = false; 51 while (data.remaining() > 1) { 52 int eid = data.get() & Constants.BYTE_MASK; 53 int eidExt = 0; 54 int elementLength = data.get() & Constants.BYTE_MASK; 55 56 if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID 57 && found_ssid)) { 58 // APs often pad the data with bytes that happen to match that of the EID_SSID 59 // marker. This is not due to a known issue for APs to incorrectly send the SSID 60 // name multiple times. 61 break; 62 } 63 if (eid == InformationElement.EID_SSID) { 64 found_ssid = true; 65 } else if (eid == InformationElement.EID_EXTENSION_PRESENT) { 66 if (elementLength == 0) { 67 // Malformed IE, skipping 68 break; 69 } 70 eidExt = data.get() & Constants.BYTE_MASK; 71 elementLength--; 72 } 73 74 InformationElement ie = new InformationElement(); 75 ie.id = eid; 76 ie.idExt = eidExt; 77 ie.bytes = new byte[elementLength]; 78 data.get(ie.bytes); 79 infoElements.add(ie); 80 } 81 return infoElements.toArray(new InformationElement[infoElements.size()]); 82 } 83 84 /** 85 * Parse and retrieve the Roaming Consortium Information Element from the list of IEs. 86 * 87 * @param ies List of IEs to retrieve from 88 * @return {@link RoamingConsortium} 89 */ getRoamingConsortiumIE(InformationElement[] ies)90 public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) { 91 RoamingConsortium roamingConsortium = new RoamingConsortium(); 92 if (ies != null) { 93 for (InformationElement ie : ies) { 94 if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) { 95 try { 96 roamingConsortium.from(ie); 97 } catch (RuntimeException e) { 98 Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage()); 99 } 100 } 101 } 102 } 103 return roamingConsortium; 104 } 105 106 /** 107 * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs. 108 * 109 * @param ies List of IEs to retrieve from 110 * @return {@link Vsa} 111 */ getHS2VendorSpecificIE(InformationElement[] ies)112 public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) { 113 Vsa vsa = new Vsa(); 114 if (ies != null) { 115 for (InformationElement ie : ies) { 116 if (ie.id == InformationElement.EID_VSA) { 117 try { 118 vsa.from(ie); 119 } catch (RuntimeException e) { 120 Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage()); 121 } 122 } 123 } 124 } 125 return vsa; 126 } 127 128 /** 129 * Parse and retrieve the Interworking information element from the list of IEs. 130 * 131 * @param ies List of IEs to retrieve from 132 * @return {@link Interworking} 133 */ getInterworkingIE(InformationElement[] ies)134 public static Interworking getInterworkingIE(InformationElement[] ies) { 135 Interworking interworking = new Interworking(); 136 if (ies != null) { 137 for (InformationElement ie : ies) { 138 if (ie.id == InformationElement.EID_INTERWORKING) { 139 try { 140 interworking.from(ie); 141 } catch (RuntimeException e) { 142 Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage()); 143 } 144 } 145 } 146 } 147 return interworking; 148 } 149 150 public static class BssLoad { 151 public static final int INVALID = -1; 152 public static final int MAX_CHANNEL_UTILIZATION = 255; 153 public static final int MIN_CHANNEL_UTILIZATION = 0; 154 public static final int CHANNEL_UTILIZATION_SCALE = 256; 155 public int stationCount = INVALID; 156 public int channelUtilization = INVALID; 157 public int capacity = INVALID; 158 from(InformationElement ie)159 public void from(InformationElement ie) { 160 if (ie.id != InformationElement.EID_BSS_LOAD) { 161 throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id); 162 } 163 if (ie.bytes.length != 5) { 164 throw new IllegalArgumentException("BSS Load element length is not 5: " 165 + ie.bytes.length); 166 } 167 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 168 stationCount = data.getShort() & Constants.SHORT_MASK; 169 channelUtilization = data.get() & Constants.BYTE_MASK; 170 capacity = data.getShort() & Constants.SHORT_MASK; 171 } 172 } 173 174 public static class HtOperation { 175 private static final int HT_OPERATION_IE_LEN = 22; 176 private boolean mPresent = false; 177 private int mSecondChannelOffset = 0; 178 179 /** 180 * returns if HT Operation IE present in the message. 181 */ isPresent()182 public boolean isPresent() { 183 return mPresent; 184 } 185 186 /** 187 * Returns channel width if it is 20 or 40MHz 188 * Results will be invalid if channel width greater than 40MHz 189 * So caller should only call this method if VHT Operation IE is not present, 190 * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. 191 */ getChannelWidth()192 public int getChannelWidth() { 193 if (mSecondChannelOffset != 0) { 194 return ScanResult.CHANNEL_WIDTH_40MHZ; 195 } else { 196 return ScanResult.CHANNEL_WIDTH_20MHZ; 197 } 198 } 199 200 /** 201 * Returns channel Center frequency (for 20/40 MHz channels only) 202 * Results will be invalid for larger channel width, 203 * So, caller should only call this method if VHT Operation IE is not present, 204 * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. 205 */ getCenterFreq0(int primaryFrequency)206 public int getCenterFreq0(int primaryFrequency) { 207 if (mSecondChannelOffset != 0) { 208 //40 MHz 209 if (mSecondChannelOffset == 1) { 210 return primaryFrequency + 10; 211 } else if (mSecondChannelOffset == 3) { 212 return primaryFrequency - 10; 213 } else { 214 Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset); 215 return 0; 216 } 217 } else { 218 //20 MHz 219 return primaryFrequency; 220 } 221 } 222 223 /** 224 * Parse the HT Operation IE to read the fields of interest. 225 */ from(InformationElement ie)226 public void from(InformationElement ie) { 227 if (ie.id != InformationElement.EID_HT_OPERATION) { 228 throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id); 229 } 230 if (ie.bytes.length < HT_OPERATION_IE_LEN) { 231 throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length); 232 } 233 mPresent = true; 234 mSecondChannelOffset = ie.bytes[1] & 0x3; 235 } 236 } 237 238 public static class VhtOperation { 239 private static final int VHT_OPERATION_IE_LEN = 5; 240 private boolean mPresent = false; 241 private int mChannelMode = 0; 242 private int mCenterFreqIndex1 = 0; 243 private int mCenterFreqIndex2 = 0; 244 245 /** 246 * returns if VHT Operation IE present in the message. 247 */ isPresent()248 public boolean isPresent() { 249 return mPresent; 250 } 251 252 /** 253 * Returns channel width if it is above 40MHz, 254 * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that 255 * channel width should be obtained from the HT Operation IE via 256 * HtOperation.getChannelWidth(). 257 */ getChannelWidth()258 public int getChannelWidth() { 259 if (mChannelMode == 0) { 260 // 20 or 40MHz 261 return ScanResult.UNSPECIFIED; 262 } else if (mCenterFreqIndex2 == 0) { 263 // No secondary channel 264 return ScanResult.CHANNEL_WIDTH_80MHZ; 265 } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) { 266 // Primary and secondary channels adjacent 267 return ScanResult.CHANNEL_WIDTH_160MHZ; 268 } else { 269 // Primary and secondary channels not adjacent 270 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; 271 } 272 } 273 274 /** 275 * Returns center frequency of primary channel (if channel width greater than 40MHz), 276 * otherwise, it returns zero to indicate that center frequency should be obtained from 277 * the HT Operation IE via HtOperation.getCenterFreq0(). 278 */ getCenterFreq0()279 public int getCenterFreq0() { 280 if (mCenterFreqIndex1 == 0 || mChannelMode == 0) { 281 return 0; 282 } else { 283 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex1, 284 WifiScanner.WIFI_BAND_5_GHZ); 285 } 286 } 287 288 /** 289 * Returns center frequency of secondary channel if exists (channel width greater than 290 * 40MHz), otherwise, it returns zero. 291 * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz 292 * channels. 293 */ getCenterFreq1()294 public int getCenterFreq1() { 295 if (mCenterFreqIndex2 == 0 || mChannelMode == 0) { 296 return 0; 297 } else { 298 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex2, 299 WifiScanner.WIFI_BAND_5_GHZ); 300 } 301 } 302 303 /** 304 * Parse the VHT Operation IE to read the fields of interest. 305 */ from(InformationElement ie)306 public void from(InformationElement ie) { 307 if (ie.id != InformationElement.EID_VHT_OPERATION) { 308 throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id); 309 } 310 if (ie.bytes.length < VHT_OPERATION_IE_LEN) { 311 throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length); 312 } 313 mPresent = true; 314 mChannelMode = ie.bytes[0] & Constants.BYTE_MASK; 315 mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; 316 mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; 317 } 318 } 319 320 /** 321 * HeOperation: represents the HE Operation IE 322 */ 323 public static class HeOperation { 324 325 private static final int HE_OPERATION_BASIC_LENGTH = 6; 326 private static final int VHT_OPERATION_INFO_PRESENT_MASK = 0x40; 327 private static final int HE_6GHZ_INFO_PRESENT_MASK = 0x02; 328 private static final int HE_6GHZ_CH_WIDTH_MASK = 0x03; 329 private static final int CO_HOSTED_BSS_PRESENT_MASK = 0x80; 330 private static final int VHT_OPERATION_INFO_START_INDEX = 6; 331 private static final int HE_BW_80_80_160 = 3; 332 333 private boolean mPresent = false; 334 private boolean mVhtInfoPresent = false; 335 private boolean m6GhzInfoPresent = false; 336 private int mChannelWidth; 337 private int mPrimaryChannel; 338 private int mCenterFreqSeg0; 339 private int mCenterFreqSeg1; 340 private InformationElement mVhtInfo = null; 341 342 /** 343 * Returns whether the HE Information Element is present. 344 */ isPresent()345 public boolean isPresent() { 346 return mPresent; 347 } 348 349 /** 350 * Returns whether VHT Information field is present. 351 */ isVhtInfoPresent()352 public boolean isVhtInfoPresent() { 353 return mVhtInfoPresent; 354 } 355 356 /** 357 * Returns the VHT Information Element if it exists 358 * otherwise, return null. 359 */ getVhtInfoElement()360 public InformationElement getVhtInfoElement() { 361 return mVhtInfo; 362 } 363 364 /** 365 * Returns whether the 6GHz information field is present. 366 */ is6GhzInfoPresent()367 public boolean is6GhzInfoPresent() { 368 return m6GhzInfoPresent; 369 } 370 371 /** 372 * Returns the Channel BW 373 * Only applicable to 6GHz band 374 */ getChannelWidth()375 public int getChannelWidth() { 376 if (!m6GhzInfoPresent) { 377 return ScanResult.UNSPECIFIED; 378 } else if (mChannelWidth == 0) { 379 return ScanResult.CHANNEL_WIDTH_20MHZ; 380 } else if (mChannelWidth == 1) { 381 return ScanResult.CHANNEL_WIDTH_40MHZ; 382 } else if (mChannelWidth == 2) { 383 return ScanResult.CHANNEL_WIDTH_80MHZ; 384 } else if (Math.abs(mCenterFreqSeg1 - mCenterFreqSeg0) == 8) { 385 return ScanResult.CHANNEL_WIDTH_160MHZ; 386 } else { 387 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; 388 } 389 } 390 391 /** 392 * Returns the primary channel frequency 393 * Only applicable for 6GHz channels 394 */ getPrimaryFreq()395 public int getPrimaryFreq() { 396 return ScanResult.convertChannelToFrequencyMhzIfSupported(mPrimaryChannel, 397 WifiScanner.WIFI_BAND_6_GHZ); 398 } 399 400 /** 401 * Returns the center frequency for the primary channel 402 * Only applicable to 6GHz channels 403 */ getCenterFreq0()404 public int getCenterFreq0() { 405 if (m6GhzInfoPresent) { 406 if (mCenterFreqSeg0 == 0) { 407 return 0; 408 } else { 409 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg0, 410 WifiScanner.WIFI_BAND_6_GHZ); 411 } 412 } else { 413 return 0; 414 } 415 } 416 417 /** 418 * Returns the center frequency for the secondary channel 419 * Only applicable to 6GHz channels 420 */ getCenterFreq1()421 public int getCenterFreq1() { 422 if (m6GhzInfoPresent) { 423 if (mCenterFreqSeg1 == 0) { 424 return 0; 425 } else { 426 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg1, 427 WifiScanner.WIFI_BAND_6_GHZ); 428 } 429 } else { 430 return 0; 431 } 432 } 433 434 /** Parse HE Operation IE */ from(InformationElement ie)435 public void from(InformationElement ie) { 436 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 437 || ie.idExt != InformationElement.EID_EXT_HE_OPERATION) { 438 throw new IllegalArgumentException("Element id is not HE_OPERATION"); 439 } 440 441 // Make sure the byte array length is at least the fixed size 442 if (ie.bytes.length < HE_OPERATION_BASIC_LENGTH) { 443 if (DBG) { 444 Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length); 445 } 446 // Skipping parsing of the IE 447 return; 448 } 449 450 mVhtInfoPresent = (ie.bytes[1] & VHT_OPERATION_INFO_PRESENT_MASK) != 0; 451 m6GhzInfoPresent = (ie.bytes[2] & HE_6GHZ_INFO_PRESENT_MASK) != 0; 452 boolean coHostedBssPresent = (ie.bytes[1] & CO_HOSTED_BSS_PRESENT_MASK) != 0; 453 int expectedLen = HE_OPERATION_BASIC_LENGTH + (mVhtInfoPresent ? 3 : 0) 454 + (coHostedBssPresent ? 1 : 0) + (m6GhzInfoPresent ? 5 : 0); 455 456 // Make sure the byte array length is at least fitting the known parameters 457 if (ie.bytes.length < expectedLen) { 458 if (DBG) { 459 Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length); 460 } 461 // Skipping parsing of the IE 462 return; 463 } 464 465 // Passed all checks, IE is ready for decoding 466 mPresent = true; 467 468 if (mVhtInfoPresent) { 469 mVhtInfo = new InformationElement(); 470 mVhtInfo.id = InformationElement.EID_VHT_OPERATION; 471 mVhtInfo.bytes = new byte[5]; 472 System.arraycopy(ie.bytes, VHT_OPERATION_INFO_START_INDEX, mVhtInfo.bytes, 0, 3); 473 } 474 475 if (m6GhzInfoPresent) { 476 int startIndx = VHT_OPERATION_INFO_START_INDEX + (mVhtInfoPresent ? 3 : 0) 477 + (coHostedBssPresent ? 1 : 0); 478 479 mChannelWidth = ie.bytes[startIndx + 1] & HE_6GHZ_CH_WIDTH_MASK; 480 mPrimaryChannel = ie.bytes[startIndx] & Constants.BYTE_MASK; 481 mCenterFreqSeg0 = ie.bytes[startIndx + 2] & Constants.BYTE_MASK; 482 mCenterFreqSeg1 = ie.bytes[startIndx + 3] & Constants.BYTE_MASK; 483 } 484 } 485 } 486 487 /** 488 * HtCapabilities: represents the HT Capabilities IE 489 */ 490 public static class HtCapabilities { 491 private int mMaxNumberSpatialStreams = 1; 492 private boolean mPresent = false; 493 /** Returns whether HT Capabilities IE is present */ isPresent()494 public boolean isPresent() { 495 return mPresent; 496 } 497 /** 498 * Returns max number of spatial streams if HT Capabilities IE is found and parsed, 499 * or 1 otherwise 500 */ getMaxNumberSpatialStreams()501 public int getMaxNumberSpatialStreams() { 502 return mMaxNumberSpatialStreams; 503 } 504 505 /** Parse HT Capabilities IE */ from(InformationElement ie)506 public void from(InformationElement ie) { 507 if (ie.id != InformationElement.EID_HT_CAPABILITIES) { 508 throw new IllegalArgumentException("Element id is not HT_CAPABILITIES: " + ie.id); 509 } 510 if (ie.bytes.length < 26) { 511 if (DBG) { 512 Log.w(TAG, "Invalid HtCapabilities len: " + ie.bytes.length); 513 } 514 return; 515 } 516 int stream1 = ie.bytes[3] & Constants.BYTE_MASK; 517 int stream2 = ie.bytes[4] & Constants.BYTE_MASK; 518 int stream3 = ie.bytes[5] & Constants.BYTE_MASK; 519 int stream4 = ie.bytes[6] & Constants.BYTE_MASK; 520 if (DBG) { 521 Log.d(TAG, "HT Rx MCS set4: " + Integer.toHexString(stream4)); 522 Log.d(TAG, "HT Rx MCS set3: " + Integer.toHexString(stream3)); 523 Log.d(TAG, "HT Rx MCS set2: " + Integer.toHexString(stream2)); 524 Log.d(TAG, "HT Rx MCS set1: " + Integer.toHexString(stream1)); 525 } 526 mMaxNumberSpatialStreams = (stream4 > 0) ? 4 : 527 ((stream3 > 0) ? 3 : 528 ((stream2 > 0) ? 2 : 1)); 529 mPresent = true; 530 } 531 } 532 533 /** 534 * VhtCapabilities: represents the VHT Capabilities IE 535 */ 536 public static class VhtCapabilities { 537 private int mMaxNumberSpatialStreams = 1; 538 private boolean mPresent = false; 539 /** Returns whether VHT Capabilities IE is present */ isPresent()540 public boolean isPresent() { 541 return mPresent; 542 } 543 /** 544 * Returns max number of spatial streams if VHT Capabilities IE is found and parsed, 545 * or 1 otherwise 546 */ getMaxNumberSpatialStreams()547 public int getMaxNumberSpatialStreams() { 548 return mMaxNumberSpatialStreams; 549 } 550 /** Parse VHT Capabilities IE */ from(InformationElement ie)551 public void from(InformationElement ie) { 552 if (ie.id != InformationElement.EID_VHT_CAPABILITIES) { 553 throw new IllegalArgumentException("Element id is not VHT_CAPABILITIES: " + ie.id); 554 } 555 if (ie.bytes.length < 12) { 556 if (DBG) { 557 Log.w(TAG, "Invalid VHT_CAPABILITIES len: " + ie.bytes.length); 558 } 559 return; 560 } 561 int mcsMap = ((ie.bytes[5] & Constants.BYTE_MASK) << 8) 562 + (ie.bytes[4] & Constants.BYTE_MASK); 563 mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap); 564 mPresent = true; 565 } 566 } 567 568 /** 569 * HeCapabilities: represents the HE Capabilities IE 570 */ 571 public static class HeCapabilities { 572 private int mMaxNumberSpatialStreams = 1; 573 private boolean mPresent = false; 574 /** Returns whether HE Capabilities IE is present */ isPresent()575 public boolean isPresent() { 576 return mPresent; 577 } 578 /** 579 * Returns max number of spatial streams if HE Capabilities IE is found and parsed, 580 * or 1 otherwise 581 */ getMaxNumberSpatialStreams()582 public int getMaxNumberSpatialStreams() { 583 return mMaxNumberSpatialStreams; 584 } 585 /** Parse HE Capabilities IE */ from(InformationElement ie)586 public void from(InformationElement ie) { 587 if (ie.id != InformationElement.EID_EXTENSION_PRESENT 588 || ie.idExt != InformationElement.EID_EXT_HE_CAPABILITIES) { 589 throw new IllegalArgumentException("Element id is not HE_CAPABILITIES: " + ie.id); 590 } 591 if (ie.bytes.length < 21) { 592 if (DBG) { 593 Log.w(TAG, "Invalid HE_CAPABILITIES len: " + ie.bytes.length); 594 } 595 return; 596 } 597 int mcsMap = ((ie.bytes[18] & Constants.BYTE_MASK) << 8) 598 + (ie.bytes[17] & Constants.BYTE_MASK); 599 mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap); 600 mPresent = true; 601 } 602 } 603 parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap)604 private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) { 605 int maxNumberSpatialStreams = 1; 606 for (int i = 8; i >= 1; --i) { 607 int streamMap = mcsMapToStreamMap(mcsMap, i); 608 // 3 means unsupported 609 if (streamMap != 3) { 610 maxNumberSpatialStreams = i; 611 break; 612 } 613 } 614 if (DBG) { 615 for (int i = 8; i >= 1; --i) { 616 int streamMap = mcsMapToStreamMap(mcsMap, i); 617 Log.d(TAG, "Rx MCS set " + i + " : " + streamMap); 618 } 619 } 620 return maxNumberSpatialStreams; 621 } 622 mcsMapToStreamMap(int mcsMap, int i)623 private static int mcsMapToStreamMap(int mcsMap, int i) { 624 return (mcsMap >> ((i - 1) * 2)) & 0x3; 625 } 626 627 public static class Interworking { 628 public NetworkDetail.Ant ant = null; 629 public boolean internet = false; 630 public long hessid = 0L; 631 from(InformationElement ie)632 public void from(InformationElement ie) { 633 if (ie.id != InformationElement.EID_INTERWORKING) { 634 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id); 635 } 636 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 637 int anOptions = data.get() & Constants.BYTE_MASK; 638 ant = NetworkDetail.Ant.values()[anOptions & 0x0f]; 639 internet = (anOptions & 0x10) != 0; 640 // There are only three possible lengths for the Interworking IE: 641 // Len 1: Access Network Options only 642 // Len 3: Access Network Options & Venue Info 643 // Len 7: Access Network Options & HESSID 644 // Len 9: Access Network Options, Venue Info, & HESSID 645 if (ie.bytes.length != 1 646 && ie.bytes.length != 3 647 && ie.bytes.length != 7 648 && ie.bytes.length != 9) { 649 throw new IllegalArgumentException( 650 "Bad Interworking element length: " + ie.bytes.length); 651 } 652 653 if (ie.bytes.length == 3 || ie.bytes.length == 9) { 654 int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2); 655 } 656 657 if (ie.bytes.length == 7 || ie.bytes.length == 9) { 658 hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6); 659 } 660 } 661 } 662 663 public static class RoamingConsortium { 664 public int anqpOICount = 0; 665 666 private long[] roamingConsortiums = null; 667 getRoamingConsortiums()668 public long[] getRoamingConsortiums() { 669 return roamingConsortiums; 670 } 671 from(InformationElement ie)672 public void from(InformationElement ie) { 673 if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) { 674 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " 675 + ie.id); 676 } 677 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 678 anqpOICount = data.get() & Constants.BYTE_MASK; 679 680 int oi12Length = data.get() & Constants.BYTE_MASK; 681 int oi1Length = oi12Length & Constants.NIBBLE_MASK; 682 int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK; 683 int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length; 684 int oiCount = 0; 685 if (oi1Length > 0) { 686 oiCount++; 687 if (oi2Length > 0) { 688 oiCount++; 689 if (oi3Length > 0) { 690 oiCount++; 691 } 692 } 693 } 694 roamingConsortiums = new long[oiCount]; 695 if (oi1Length > 0 && roamingConsortiums.length > 0) { 696 roamingConsortiums[0] = 697 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); 698 } 699 if (oi2Length > 0 && roamingConsortiums.length > 1) { 700 roamingConsortiums[1] = 701 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); 702 } 703 if (oi3Length > 0 && roamingConsortiums.length > 2) { 704 roamingConsortiums[2] = 705 ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); 706 } 707 } 708 } 709 710 public static class Vsa { 711 private static final int ANQP_DOMAIN_ID_PRESENT_BIT = 0x04; 712 private static final int ANQP_PPS_MO_ID_BIT = 0x02; 713 private static final int OUI_WFA_ALLIANCE = 0x506F9a; 714 private static final int OUI_TYPE_HS20 = 0x10; 715 private static final int OUI_TYPE_MBO_OCE = 0x16; 716 717 public NetworkDetail.HSRelease hsRelease = null; 718 public int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP. 719 720 public boolean IsMboCapable = false; 721 public boolean IsMboApCellularDataAware = false; 722 public boolean IsOceCapable = false; 723 public int mboAssociationDisallowedReasonCode = 724 MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT; 725 parseVsaMboOce(InformationElement ie)726 private void parseVsaMboOce(InformationElement ie) { 727 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 728 729 // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying 730 // MBO-OCE OUI type. 731 data.getInt(); 732 733 while (data.remaining() > 1) { 734 int attrId = data.get() & Constants.BYTE_MASK; 735 int attrLen = data.get() & Constants.BYTE_MASK; 736 737 if ((attrLen == 0) || (attrLen > data.remaining())) { 738 return; 739 } 740 byte[] attrBytes = new byte[attrLen]; 741 data.get(attrBytes); 742 switch (attrId) { 743 case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION: 744 IsMboCapable = true; 745 IsMboApCellularDataAware = (attrBytes[0] 746 & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0; 747 break; 748 case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED: 749 mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK; 750 break; 751 case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION: 752 IsOceCapable = true; 753 break; 754 default: 755 break; 756 } 757 } 758 if (DBG) { 759 Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: " 760 + IsMboApCellularDataAware + " AssocDisAllowRC: " 761 + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable); 762 } 763 } 764 parseVsaHs20(InformationElement ie)765 private void parseVsaHs20(InformationElement ie) { 766 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 767 if (ie.bytes.length >= 5) { 768 // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying 769 // HS20 OUI type. 770 data.getInt(); 771 772 int hsConf = data.get() & Constants.BYTE_MASK; 773 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) { 774 case 0: 775 hsRelease = NetworkDetail.HSRelease.R1; 776 break; 777 case 1: 778 hsRelease = NetworkDetail.HSRelease.R2; 779 break; 780 case 2: 781 hsRelease = NetworkDetail.HSRelease.R3; 782 break; 783 default: 784 hsRelease = NetworkDetail.HSRelease.Unknown; 785 break; 786 } 787 if ((hsConf & ANQP_DOMAIN_ID_PRESENT_BIT) != 0) { 788 // According to Hotspot 2.0 Specification v3.0 section 3.1.1 HS2.0 Indication 789 // element, the size of the element is 5 bytes, and 2 bytes are optionally added 790 // for each optional field; ANQP PPS MO ID and ANQP Domain ID present. 791 int expectedSize = 7; 792 if ((hsConf & ANQP_PPS_MO_ID_BIT) != 0) { 793 expectedSize += 2; 794 if (ie.bytes.length < expectedSize) { 795 throw new IllegalArgumentException( 796 "HS20 indication element too short: " + ie.bytes.length); 797 } 798 data.getShort(); // Skip 2 bytes 799 } 800 if (ie.bytes.length < expectedSize) { 801 throw new IllegalArgumentException( 802 "HS20 indication element too short: " + ie.bytes.length); 803 } 804 anqpDomainID = data.getShort() & Constants.SHORT_MASK; 805 } 806 } 807 } 808 809 /** 810 * Parse the vendor specific information element to build 811 * InformationElemmentUtil.vsa object. 812 * 813 * @param ie -- Information Element 814 */ from(InformationElement ie)815 public void from(InformationElement ie) { 816 if (ie.bytes.length < 3) { 817 if (DBG) { 818 Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length); 819 } 820 return; 821 } 822 823 int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16) 824 | ((ie.bytes[1] & Constants.BYTE_MASK) << 8) 825 | ((ie.bytes[2] & Constants.BYTE_MASK))); 826 827 if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) { 828 int ouiType = ie.bytes[3]; 829 switch (ouiType) { 830 case OUI_TYPE_HS20: 831 parseVsaHs20(ie); 832 break; 833 case OUI_TYPE_MBO_OCE: 834 parseVsaMboOce(ie); 835 break; 836 default: 837 break; 838 } 839 } 840 } 841 } 842 843 /** 844 * This IE contained a bit field indicating the capabilities being advertised by the STA. 845 * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE. 846 * 847 * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each 848 * bit. 849 * 850 * Here is the wire format of this IE: 851 * | Element ID | Length | Capabilities | 852 * 1 1 n 853 */ 854 public static class ExtendedCapabilities { 855 private static final int RTT_RESP_ENABLE_BIT = 70; 856 private static final int SSID_UTF8_BIT = 48; 857 858 public BitSet capabilitiesBitSet; 859 860 /** 861 * @return true if SSID should be interpreted using UTF-8 encoding 862 */ isStrictUtf8()863 public boolean isStrictUtf8() { 864 return capabilitiesBitSet.get(SSID_UTF8_BIT); 865 } 866 867 /** 868 * @return true if 802.11 MC RTT Response is enabled 869 */ is80211McRTTResponder()870 public boolean is80211McRTTResponder() { 871 return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT); 872 } 873 ExtendedCapabilities()874 public ExtendedCapabilities() { 875 capabilitiesBitSet = new BitSet(); 876 } 877 ExtendedCapabilities(ExtendedCapabilities other)878 public ExtendedCapabilities(ExtendedCapabilities other) { 879 capabilitiesBitSet = other.capabilitiesBitSet; 880 } 881 882 /** 883 * Parse an ExtendedCapabilities from the IE containing raw bytes. 884 * 885 * @param ie The Information element data 886 */ from(InformationElement ie)887 public void from(InformationElement ie) { 888 capabilitiesBitSet = BitSet.valueOf(ie.bytes); 889 } 890 } 891 892 /** 893 * parse beacon to build the capabilities 894 * 895 * This class is used to build the capabilities string of the scan results coming 896 * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, 897 * and builds the ScanResult.capabilities String in a way that mirrors the values returned 898 * by wpa_supplicant. 899 */ 900 public static class Capabilities { 901 private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000; 902 private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000; 903 private static final short WPA_VENDOR_OUI_VERSION = 0x0001; 904 private static final int OWE_VENDOR_OUI_TYPE = 0x1c9a6f50; 905 private static final short RSNE_VERSION = 0x0001; 906 907 private static final int WPA_AKM_EAP = 0x01f25000; 908 private static final int WPA_AKM_PSK = 0x02f25000; 909 910 private static final int RSN_AKM_EAP = 0x01ac0f00; 911 private static final int RSN_AKM_PSK = 0x02ac0f00; 912 private static final int RSN_AKM_FT_EAP = 0x03ac0f00; 913 private static final int RSN_AKM_FT_PSK = 0x04ac0f00; 914 private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00; 915 private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00; 916 private static final int RSN_AKM_SAE = 0x08ac0f00; 917 private static final int RSN_AKM_FT_SAE = 0x09ac0f00; 918 private static final int RSN_AKM_OWE = 0x12ac0f00; 919 private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; 920 private static final int RSN_OSEN = 0x019a6f50; 921 private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00; 922 private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00; 923 924 private static final int WPA_CIPHER_NONE = 0x00f25000; 925 private static final int WPA_CIPHER_TKIP = 0x02f25000; 926 private static final int WPA_CIPHER_CCMP = 0x04f25000; 927 928 private static final int RSN_CIPHER_NONE = 0x00ac0f00; 929 private static final int RSN_CIPHER_TKIP = 0x02ac0f00; 930 private static final int RSN_CIPHER_CCMP = 0x04ac0f00; 931 private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00; 932 private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00; 933 private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00; 934 private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00; 935 private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00; 936 private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00; 937 938 // RSN capability bit definition 939 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6; 940 private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7; 941 942 public List<Integer> protocol; 943 public List<List<Integer>> keyManagement; 944 public List<List<Integer>> pairwiseCipher; 945 public List<Integer> groupCipher; 946 public List<Integer> groupManagementCipher; 947 public boolean isESS; 948 public boolean isIBSS; 949 public boolean isPrivacy; 950 public boolean isWPS; 951 public boolean isManagementFrameProtectionRequired; 952 public boolean isManagementFrameProtectionCapable; 953 Capabilities()954 public Capabilities() { 955 } 956 957 // RSNE format (size unit: byte) 958 // 959 // | Element ID | Length | Version | Group Data Cipher Suite | 960 // 1 1 2 4 961 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 962 // 2 4 * m 963 // | AKM Suite Count | AKM Suite List | RSN Capabilities | 964 // 2 4 * n 2 965 // | PMKID Count | PMKID List | Group Management Cipher Suite | 966 // 2 16 * s 4 967 // 968 // Note: InformationElement.bytes has 'Element ID' and 'Length' 969 // stripped off already parseRsnElement(InformationElement ie)970 private void parseRsnElement(InformationElement ie) { 971 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 972 973 try { 974 // version 975 if (buf.getShort() != RSNE_VERSION) { 976 // incorrect version 977 return; 978 } 979 980 // found the RSNE IE, hence start building the capability string 981 protocol.add(ScanResult.PROTOCOL_RSN); 982 983 // group data cipher suite 984 groupCipher.add(parseRsnCipher(buf.getInt())); 985 986 // pairwise cipher suite count 987 short cipherCount = buf.getShort(); 988 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>(); 989 // pairwise cipher suite list 990 for (int i = 0; i < cipherCount; i++) { 991 rsnPairwiseCipher.add(parseRsnCipher(buf.getInt())); 992 } 993 pairwiseCipher.add(rsnPairwiseCipher); 994 995 // AKM 996 // AKM suite count 997 short akmCount = buf.getShort(); 998 ArrayList<Integer> rsnKeyManagement = new ArrayList<>(); 999 1000 for (int i = 0; i < akmCount; i++) { 1001 int akm = buf.getInt(); 1002 switch (akm) { 1003 case RSN_AKM_EAP: 1004 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1005 break; 1006 case RSN_AKM_PSK: 1007 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1008 break; 1009 case RSN_AKM_FT_EAP: 1010 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP); 1011 break; 1012 case RSN_AKM_FT_PSK: 1013 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK); 1014 break; 1015 case RSN_AKM_EAP_SHA256: 1016 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256); 1017 break; 1018 case RSN_AKM_PSK_SHA256: 1019 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256); 1020 break; 1021 case RSN_AKM_SAE: 1022 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE); 1023 break; 1024 case RSN_AKM_FT_SAE: 1025 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE); 1026 break; 1027 case RSN_AKM_OWE: 1028 rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE); 1029 break; 1030 case RSN_AKM_EAP_SUITE_B_192: 1031 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); 1032 break; 1033 case RSN_OSEN: 1034 rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); 1035 break; 1036 case RSN_AKM_EAP_FILS_SHA256: 1037 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); 1038 break; 1039 case RSN_AKM_EAP_FILS_SHA384: 1040 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); 1041 break; 1042 default: 1043 rsnKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN); 1044 break; 1045 } 1046 } 1047 // Default AKM 1048 if (rsnKeyManagement.isEmpty()) { 1049 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1050 } 1051 keyManagement.add(rsnKeyManagement); 1052 1053 // RSN capabilities (optional), 1054 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016 1055 if (buf.remaining() < 2) return; 1056 int rsnCaps = buf.getShort(); 1057 1058 if (buf.remaining() < 2) return; 1059 // PMKID, it's not used, drop it if exists (optional). 1060 int rsnPmkIdCount = buf.getShort(); 1061 for (int i = 0; i < rsnPmkIdCount; i++) { 1062 // Each PMKID element length in the PMKID List is 16 bytes 1063 byte[] tmp = new byte[16]; 1064 buf.get(tmp); 1065 } 1066 1067 // Group management cipher suite (optional). 1068 if (buf.remaining() < 4) return; 1069 groupManagementCipher.add(parseRsnCipher(buf.getInt())); 1070 isManagementFrameProtectionRequired = !groupManagementCipher.isEmpty() 1071 && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps); 1072 isManagementFrameProtectionCapable = !groupManagementCipher.isEmpty() 1073 && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps); 1074 } catch (BufferUnderflowException e) { 1075 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow"); 1076 } 1077 } 1078 parseWpaCipher(int cipher)1079 private static @Cipher int parseWpaCipher(int cipher) { 1080 switch (cipher) { 1081 case WPA_CIPHER_NONE: 1082 return ScanResult.CIPHER_NONE; 1083 case WPA_CIPHER_TKIP: 1084 return ScanResult.CIPHER_TKIP; 1085 case WPA_CIPHER_CCMP: 1086 return ScanResult.CIPHER_CCMP; 1087 default: 1088 Log.w("IE_Capabilities", "Unknown WPA cipher suite: " 1089 + Integer.toHexString(cipher)); 1090 return ScanResult.CIPHER_NONE; 1091 } 1092 } 1093 parseRsnCipher(int cipher)1094 private static @Cipher int parseRsnCipher(int cipher) { 1095 switch (cipher) { 1096 case RSN_CIPHER_NONE: 1097 return ScanResult.CIPHER_NONE; 1098 case RSN_CIPHER_TKIP: 1099 return ScanResult.CIPHER_TKIP; 1100 case RSN_CIPHER_CCMP: 1101 return ScanResult.CIPHER_CCMP; 1102 case RSN_CIPHER_GCMP_256: 1103 return ScanResult.CIPHER_GCMP_256; 1104 case RSN_CIPHER_NO_GROUP_ADDRESSED: 1105 return ScanResult.CIPHER_NO_GROUP_ADDRESSED; 1106 case RSN_CIPHER_GCMP_128: 1107 return ScanResult.CIPHER_GCMP_128; 1108 case RSN_CIPHER_BIP_GMAC_128: 1109 return ScanResult.CIPHER_BIP_GMAC_128; 1110 case RSN_CIPHER_BIP_GMAC_256: 1111 return ScanResult.CIPHER_BIP_GMAC_256; 1112 case RSN_CIPHER_BIP_CMAC_256: 1113 return ScanResult.CIPHER_BIP_CMAC_256; 1114 default: 1115 Log.w("IE_Capabilities", "Unknown RSN cipher suite: " 1116 + Integer.toHexString(cipher)); 1117 return ScanResult.CIPHER_NONE; 1118 } 1119 } 1120 isWpsElement(InformationElement ie)1121 private static boolean isWpsElement(InformationElement ie) { 1122 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1123 try { 1124 // WPS OUI and type 1125 return (buf.getInt() == WPS_VENDOR_OUI_TYPE); 1126 } catch (BufferUnderflowException e) { 1127 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1128 return false; 1129 } 1130 } 1131 isWpaOneElement(InformationElement ie)1132 private static boolean isWpaOneElement(InformationElement ie) { 1133 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1134 1135 try { 1136 // WPA OUI and type 1137 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE); 1138 } catch (BufferUnderflowException e) { 1139 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1140 return false; 1141 } 1142 } 1143 1144 // WPA type 1 format (size unit: byte) 1145 // 1146 // | Element ID | Length | OUI | Type | Version | 1147 // 1 1 3 1 2 1148 // | Group Data Cipher Suite | 1149 // 4 1150 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1151 // 2 4 * m 1152 // | AKM Suite Count | AKM Suite List | 1153 // 2 4 * n 1154 // 1155 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1156 // stripped off already 1157 // parseWpaOneElement(InformationElement ie)1158 private void parseWpaOneElement(InformationElement ie) { 1159 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1160 1161 try { 1162 // skip WPA OUI and type parsing. isWpaOneElement() should have 1163 // been called for verification before we reach here. 1164 buf.getInt(); 1165 1166 // version 1167 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) { 1168 // incorrect version 1169 return; 1170 } 1171 1172 // start building the string 1173 protocol.add(ScanResult.PROTOCOL_WPA); 1174 1175 // group data cipher suite 1176 groupCipher.add(parseWpaCipher(buf.getInt())); 1177 1178 // pairwise cipher suite count 1179 short cipherCount = buf.getShort(); 1180 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>(); 1181 // pairwise chipher suite list 1182 for (int i = 0; i < cipherCount; i++) { 1183 wpaPairwiseCipher.add(parseWpaCipher(buf.getInt())); 1184 } 1185 pairwiseCipher.add(wpaPairwiseCipher); 1186 1187 // AKM 1188 // AKM suite count 1189 short akmCount = buf.getShort(); 1190 ArrayList<Integer> wpaKeyManagement = new ArrayList<>(); 1191 1192 // AKM suite list 1193 for (int i = 0; i < akmCount; i++) { 1194 int akm = buf.getInt(); 1195 switch (akm) { 1196 case WPA_AKM_EAP: 1197 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1198 break; 1199 case WPA_AKM_PSK: 1200 wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1201 break; 1202 default: 1203 wpaKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN); 1204 break; 1205 } 1206 } 1207 // Default AKM 1208 if (wpaKeyManagement.isEmpty()) { 1209 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1210 } 1211 keyManagement.add(wpaKeyManagement); 1212 } catch (BufferUnderflowException e) { 1213 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow"); 1214 } 1215 } 1216 1217 /** 1218 * Parse the Information Element and the 16-bit Capability Information field 1219 * to build the InformationElemmentUtil.capabilities object. 1220 * 1221 * @param ies -- Information Element array 1222 * @param beaconCap -- 16-bit Beacon Capability Information field 1223 * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device 1224 * @param freq -- Frequency on which frame/beacon was transmitted. 1225 * Some parsing may be affected such as DMG parameters in 1226 * DMG (60GHz) beacon. 1227 */ 1228 from(InformationElement[] ies, int beaconCap, boolean isOweSupported, int freq)1229 public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported, 1230 int freq) { 1231 protocol = new ArrayList<>(); 1232 keyManagement = new ArrayList<>(); 1233 groupCipher = new ArrayList<>(); 1234 pairwiseCipher = new ArrayList<>(); 1235 groupManagementCipher = new ArrayList<>(); 1236 1237 if (ies == null) { 1238 return; 1239 } 1240 isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0; 1241 if (ScanResult.is60GHz(freq)) { 1242 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */ 1243 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS) 1244 == NativeScanResult.BSS_CAPABILITY_DMG_ESS) { 1245 isESS = true; 1246 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) { 1247 isIBSS = true; 1248 } 1249 } else { 1250 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0; 1251 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0; 1252 } 1253 for (InformationElement ie : ies) { 1254 WifiNl80211Manager.OemSecurityType oemSecurityType = 1255 WifiNl80211Manager.parseOemSecurityTypeElement( 1256 ie.id, ie.idExt, ie.bytes); 1257 if (oemSecurityType != null 1258 && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) { 1259 protocol.add(oemSecurityType.protocol); 1260 keyManagement.add(oemSecurityType.keyManagement); 1261 pairwiseCipher.add(oemSecurityType.pairwiseCipher); 1262 groupCipher.add(oemSecurityType.groupCipher); 1263 } 1264 1265 if (ie.id == InformationElement.EID_RSN) { 1266 parseRsnElement(ie); 1267 } 1268 1269 if (ie.id == InformationElement.EID_VSA) { 1270 if (isWpaOneElement(ie)) { 1271 parseWpaOneElement(ie); 1272 } 1273 if (isWpsElement(ie)) { 1274 // TODO(b/62134557): parse WPS IE to provide finer granularity information. 1275 isWPS = true; 1276 } 1277 if (isOweSupported && isOweElement(ie)) { 1278 /* From RFC 8110: Once the client and AP have finished 802.11 association, 1279 they then complete the Diffie-Hellman key exchange and create a Pairwise 1280 Master Key (PMK) and its associated identifier, PMKID [IEEE802.11]. 1281 Upon completion of 802.11 association, the AP initiates the 4-way 1282 handshake to the client using the PMK generated above. The 4-way 1283 handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation 1284 Key (KCK), and a Message Integrity Code (MIC) to use for protection 1285 of the frames that define the 4-way handshake. 1286 1287 We check if OWE is supported here because we are adding the OWE 1288 capabilities to the Open BSS. Non-supporting devices need to see this 1289 open network and ignore this element. Supporting devices need to hide 1290 the Open BSS of OWE in transition mode and connect to the Hidden one. 1291 */ 1292 protocol.add(ScanResult.PROTOCOL_RSN); 1293 groupCipher.add(ScanResult.CIPHER_CCMP); 1294 ArrayList<Integer> owePairwiseCipher = new ArrayList<>(); 1295 owePairwiseCipher.add(ScanResult.CIPHER_CCMP); 1296 pairwiseCipher.add(owePairwiseCipher); 1297 ArrayList<Integer> oweKeyManagement = new ArrayList<>(); 1298 oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION); 1299 keyManagement.add(oweKeyManagement); 1300 } 1301 } 1302 } 1303 } 1304 isOweElement(InformationElement ie)1305 private static boolean isOweElement(InformationElement ie) { 1306 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1307 try { 1308 // OWE OUI and type 1309 return (buf.getInt() == OWE_VENDOR_OUI_TYPE); 1310 } catch (BufferUnderflowException e) { 1311 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1312 return false; 1313 } 1314 } 1315 protocolToString(@rotocol int protocol)1316 private String protocolToString(@Protocol int protocol) { 1317 switch (protocol) { 1318 case ScanResult.PROTOCOL_NONE: 1319 return "None"; 1320 case ScanResult.PROTOCOL_WPA: 1321 return "WPA"; 1322 case ScanResult.PROTOCOL_RSN: 1323 return "RSN"; 1324 case ScanResult.PROTOCOL_OSEN: 1325 return "OSEN"; 1326 case ScanResult.PROTOCOL_WAPI: 1327 return "WAPI"; 1328 default: 1329 return "?"; 1330 } 1331 } 1332 keyManagementToString(@eyMgmt int akm)1333 private String keyManagementToString(@KeyMgmt int akm) { 1334 switch (akm) { 1335 case ScanResult.KEY_MGMT_NONE: 1336 return "None"; 1337 case ScanResult.KEY_MGMT_PSK: 1338 return "PSK"; 1339 case ScanResult.KEY_MGMT_EAP: 1340 return "EAP/SHA1"; 1341 case ScanResult.KEY_MGMT_FT_EAP: 1342 return "FT/EAP"; 1343 case ScanResult.KEY_MGMT_FT_PSK: 1344 return "FT/PSK"; 1345 case ScanResult.KEY_MGMT_EAP_SHA256: 1346 return "EAP/SHA256"; 1347 case ScanResult.KEY_MGMT_PSK_SHA256: 1348 return "PSK-SHA256"; 1349 case ScanResult.KEY_MGMT_OWE: 1350 return "OWE"; 1351 case ScanResult.KEY_MGMT_OWE_TRANSITION: 1352 return "OWE_TRANSITION"; 1353 case ScanResult.KEY_MGMT_SAE: 1354 return "SAE"; 1355 case ScanResult.KEY_MGMT_FT_SAE: 1356 return "FT/SAE"; 1357 case ScanResult.KEY_MGMT_EAP_SUITE_B_192: 1358 return "EAP_SUITE_B_192"; 1359 case ScanResult.KEY_MGMT_OSEN: 1360 return "OSEN"; 1361 case ScanResult.KEY_MGMT_WAPI_PSK: 1362 return "WAPI-PSK"; 1363 case ScanResult.KEY_MGMT_WAPI_CERT: 1364 return "WAPI-CERT"; 1365 case ScanResult.KEY_MGMT_FILS_SHA256: 1366 return "EAP-FILS-SHA256"; 1367 case ScanResult.KEY_MGMT_FILS_SHA384: 1368 return "EAP-FILS-SHA384"; 1369 default: 1370 return "?"; 1371 } 1372 } 1373 cipherToString(@ipher int cipher)1374 private String cipherToString(@Cipher int cipher) { 1375 switch (cipher) { 1376 case ScanResult.CIPHER_NONE: 1377 return "None"; 1378 case ScanResult.CIPHER_CCMP: 1379 return "CCMP"; 1380 case ScanResult.CIPHER_GCMP_256: 1381 return "GCMP-256"; 1382 case ScanResult.CIPHER_TKIP: 1383 return "TKIP"; 1384 case ScanResult.CIPHER_SMS4: 1385 return "SMS4"; 1386 default: 1387 return "?"; 1388 } 1389 } 1390 1391 /** 1392 * Build the ScanResult.capabilities String. 1393 * 1394 * @return security string that mirrors what wpa_supplicant generates 1395 */ generateCapabilitiesString()1396 public String generateCapabilitiesString() { 1397 StringBuilder capabilities = new StringBuilder(); 1398 // private Beacon without an RSNE or WPA IE, hence WEP0 1399 boolean isWEP = (protocol.isEmpty()) && isPrivacy; 1400 1401 if (isWEP) { 1402 capabilities.append("[WEP]"); 1403 } 1404 for (int i = 0; i < protocol.size(); i++) { 1405 String capability = generateCapabilitiesStringPerProtocol(i); 1406 // add duplicate capabilities for WPA2 for backward compatibility: 1407 // duplicate "RSN" entries as "WPA2" 1408 String capWpa2 = generateWPA2CapabilitiesString(capability, i); 1409 capabilities.append(capWpa2); 1410 capabilities.append(capability); 1411 } 1412 if (isESS) { 1413 capabilities.append("[ESS]"); 1414 } 1415 if (isIBSS) { 1416 capabilities.append("[IBSS]"); 1417 } 1418 if (isWPS) { 1419 capabilities.append("[WPS]"); 1420 } 1421 if (!groupManagementCipher.isEmpty()) { 1422 if (isManagementFrameProtectionRequired) { 1423 capabilities.append("[MFPR]"); 1424 } 1425 if (isManagementFrameProtectionCapable) { 1426 capabilities.append("[MFPC]"); 1427 } 1428 } 1429 1430 return capabilities.toString(); 1431 } 1432 1433 /** 1434 * Build the Capability String for one protocol 1435 * @param index: index number of the protocol 1436 * @return security string for one protocol 1437 */ generateCapabilitiesStringPerProtocol(int index)1438 private String generateCapabilitiesStringPerProtocol(int index) { 1439 StringBuilder capability = new StringBuilder(); 1440 capability.append("[").append(protocolToString(protocol.get(index))); 1441 1442 if (index < keyManagement.size()) { 1443 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1444 capability.append((j == 0) ? "-" : "+").append( 1445 keyManagementToString(keyManagement.get(index).get(j))); 1446 } 1447 } 1448 if (index < pairwiseCipher.size()) { 1449 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1450 capability.append((j == 0) ? "-" : "+").append( 1451 cipherToString(pairwiseCipher.get(index).get(j))); 1452 } 1453 } 1454 capability.append("]"); 1455 return capability.toString(); 1456 } 1457 1458 /** 1459 * Build the duplicate Capability String for WPA2 1460 * @param cap: original capability String 1461 * @param index: index number of the protocol 1462 * @return security string for WPA2, empty String if protocol is not WPA2 1463 */ generateWPA2CapabilitiesString(String cap, int index)1464 private String generateWPA2CapabilitiesString(String cap, int index) { 1465 StringBuilder capWpa2 = new StringBuilder(); 1466 // if not WPA2, return empty String 1467 if (cap.contains("EAP_SUITE_B_192") 1468 || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP") 1469 && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) { 1470 return ""; 1471 } 1472 capWpa2.append("[").append("WPA2"); 1473 if (index < keyManagement.size()) { 1474 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1475 capWpa2.append((j == 0) ? "-" : "+").append( 1476 keyManagementToString(keyManagement.get(index).get(j))); 1477 // WPA3/WPA2 transition mode 1478 if (cap.contains("SAE")) { 1479 break; 1480 } 1481 } 1482 } 1483 if (index < pairwiseCipher.size()) { 1484 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1485 capWpa2.append((j == 0) ? "-" : "+").append( 1486 cipherToString(pairwiseCipher.get(index).get(j))); 1487 } 1488 } 1489 capWpa2.append("]"); 1490 return capWpa2.toString(); 1491 } 1492 } 1493 1494 1495 /** 1496 * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will 1497 * only be present in scan results that are derived from a Beacon Frame, not from the more 1498 * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct. 1499 */ 1500 public static class TrafficIndicationMap { 1501 private static final int MAX_TIM_LENGTH = 254; 1502 private boolean mValid = false; 1503 public int mLength = 0; 1504 public int mDtimCount = -1; 1505 //Negative DTIM Period means no TIM element was given this frame. 1506 public int mDtimPeriod = -1; 1507 public int mBitmapControl = 0; 1508 1509 /** 1510 * Is this a valid TIM information element. 1511 */ isValid()1512 public boolean isValid() { 1513 return mValid; 1514 } 1515 1516 // Traffic Indication Map format (size unit: byte) 1517 // 1518 //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap | 1519 // 1 1 1 1 1 1 - 251 1520 // 1521 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1522 // stripped off already 1523 // from(InformationElement ie)1524 public void from(InformationElement ie) { 1525 mValid = false; 1526 if (ie == null || ie.bytes == null) return; 1527 mLength = ie.bytes.length; 1528 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1529 try { 1530 mDtimCount = data.get() & Constants.BYTE_MASK; 1531 mDtimPeriod = data.get() & Constants.BYTE_MASK; 1532 mBitmapControl = data.get() & Constants.BYTE_MASK; 1533 //A valid TIM element must have atleast one more byte 1534 data.get(); 1535 } catch (BufferUnderflowException e) { 1536 return; 1537 } 1538 if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { 1539 mValid = true; 1540 } 1541 } 1542 } 1543 1544 /** 1545 * This util class determines the 802.11 standard (a/b/g/n/ac/ax) being used 1546 */ 1547 public static class WifiMode { 1548 public static final int MODE_UNDEFINED = 0; // Unknown/undefined 1549 public static final int MODE_11A = 1; // 802.11a 1550 public static final int MODE_11B = 2; // 802.11b 1551 public static final int MODE_11G = 3; // 802.11g 1552 public static final int MODE_11N = 4; // 802.11n 1553 public static final int MODE_11AC = 5; // 802.11ac 1554 public static final int MODE_11AX = 6; // 802.11ax 1555 //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A 1556 1557 /** 1558 * Use frequency, max supported rate, and the existence of HE, VHT, HT & ERP fields in scan 1559 * scan result to determine the 802.11 Wifi standard being used. 1560 */ determineMode(int frequency, int maxRate, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)1561 public static int determineMode(int frequency, int maxRate, boolean foundHe, 1562 boolean foundVht, boolean foundHt, boolean foundErp) { 1563 if (foundHe) { 1564 return MODE_11AX; 1565 } else if (!ScanResult.is24GHz(frequency) && foundVht) { 1566 // Do not include subset of VHT on 2.4 GHz vendor extension 1567 // in consideration for reporting VHT. 1568 return MODE_11AC; 1569 } else if (foundHt) { 1570 return MODE_11N; 1571 } else if (foundErp) { 1572 return MODE_11G; 1573 } else if (ScanResult.is24GHz(frequency)) { 1574 if (maxRate < 24000000) { 1575 return MODE_11B; 1576 } else { 1577 return MODE_11G; 1578 } 1579 } else { 1580 return MODE_11A; 1581 } 1582 } 1583 1584 /** 1585 * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC> 1586 */ toString(int mode)1587 public static String toString(int mode) { 1588 switch(mode) { 1589 case MODE_11A: 1590 return "MODE_11A"; 1591 case MODE_11B: 1592 return "MODE_11B"; 1593 case MODE_11G: 1594 return "MODE_11G"; 1595 case MODE_11N: 1596 return "MODE_11N"; 1597 case MODE_11AC: 1598 return "MODE_11AC"; 1599 case MODE_11AX: 1600 return "MODE_11AX"; 1601 default: 1602 return "MODE_UNDEFINED"; 1603 } 1604 } 1605 } 1606 1607 /** 1608 * Parser for both the Supported Rates & Extended Supported Rates Information Elements 1609 */ 1610 public static class SupportedRates { 1611 public static final int MASK = 0x7F; // 0111 1111 1612 public boolean mValid = false; 1613 public ArrayList<Integer> mRates; 1614 SupportedRates()1615 public SupportedRates() { 1616 mRates = new ArrayList<Integer>(); 1617 } 1618 1619 /** 1620 * Is this a valid Supported Rates information element. 1621 */ isValid()1622 public boolean isValid() { 1623 return mValid; 1624 } 1625 1626 /** 1627 * get the Rate in bits/s from associated byteval 1628 */ getRateFromByte(int byteVal)1629 public static int getRateFromByte(int byteVal) { 1630 byteVal &= MASK; 1631 switch(byteVal) { 1632 case 2: 1633 return 1000000; 1634 case 4: 1635 return 2000000; 1636 case 11: 1637 return 5500000; 1638 case 12: 1639 return 6000000; 1640 case 18: 1641 return 9000000; 1642 case 22: 1643 return 11000000; 1644 case 24: 1645 return 12000000; 1646 case 36: 1647 return 18000000; 1648 case 44: 1649 return 22000000; 1650 case 48: 1651 return 24000000; 1652 case 66: 1653 return 33000000; 1654 case 72: 1655 return 36000000; 1656 case 96: 1657 return 48000000; 1658 case 108: 1659 return 54000000; 1660 default: 1661 //ERROR UNKNOWN RATE 1662 return -1; 1663 } 1664 } 1665 1666 // Supported Rates format (size unit: byte) 1667 // 1668 //| ElementID | Length | Supported Rates [7 Little Endian Info bits - 1 Flag bit] 1669 // 1 1 1 - 8 1670 // 1671 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1672 // stripped off already 1673 // from(InformationElement ie)1674 public void from(InformationElement ie) { 1675 mValid = false; 1676 if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1) { 1677 return; 1678 } 1679 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1680 try { 1681 for (int i = 0; i < ie.bytes.length; i++) { 1682 int rate = getRateFromByte(data.get()); 1683 if (rate > 0) { 1684 mRates.add(rate); 1685 } else { 1686 return; 1687 } 1688 } 1689 } catch (BufferUnderflowException e) { 1690 return; 1691 } 1692 mValid = true; 1693 return; 1694 } 1695 1696 /** 1697 * Lists the rates in a human readable string 1698 */ toString()1699 public String toString() { 1700 StringBuilder sbuf = new StringBuilder(); 1701 for (Integer rate : mRates) { 1702 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", "); 1703 } 1704 return sbuf.toString(); 1705 } 1706 } 1707 } 1708