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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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_FILS_SHA256 = 0x0eac0f00; 922 private static final int RSN_AKM_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 934 public List<Integer> protocol; 935 public List<List<Integer>> keyManagement; 936 public List<List<Integer>> pairwiseCipher; 937 public List<Integer> groupCipher; 938 public boolean isESS; 939 public boolean isIBSS; 940 public boolean isPrivacy; 941 public boolean isWPS; 942 Capabilities()943 public Capabilities() { 944 } 945 946 // RSNE format (size unit: byte) 947 // 948 // | Element ID | Length | Version | Group Data Cipher Suite | 949 // 1 1 2 4 950 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 951 // 2 4 * m 952 // | AKM Suite Count | AKM Suite List | RSN Capabilities | 953 // 2 4 * n 2 954 // | PMKID Count | PMKID List | Group Management Cipher Suite | 955 // 2 16 * s 4 956 // 957 // Note: InformationElement.bytes has 'Element ID' and 'Length' 958 // stripped off already parseRsnElement(InformationElement ie)959 private void parseRsnElement(InformationElement ie) { 960 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 961 962 try { 963 // version 964 if (buf.getShort() != RSNE_VERSION) { 965 // incorrect version 966 return; 967 } 968 969 // found the RSNE IE, hence start building the capability string 970 protocol.add(ScanResult.PROTOCOL_RSN); 971 972 // group data cipher suite 973 groupCipher.add(parseRsnCipher(buf.getInt())); 974 975 // pairwise cipher suite count 976 short cipherCount = buf.getShort(); 977 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>(); 978 // pairwise cipher suite list 979 for (int i = 0; i < cipherCount; i++) { 980 rsnPairwiseCipher.add(parseRsnCipher(buf.getInt())); 981 } 982 pairwiseCipher.add(rsnPairwiseCipher); 983 984 // AKM 985 // AKM suite count 986 short akmCount = buf.getShort(); 987 ArrayList<Integer> rsnKeyManagement = new ArrayList<>(); 988 989 for (int i = 0; i < akmCount; i++) { 990 int akm = buf.getInt(); 991 switch (akm) { 992 case RSN_AKM_EAP: 993 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 994 break; 995 case RSN_AKM_PSK: 996 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK); 997 break; 998 case RSN_AKM_FT_EAP: 999 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP); 1000 break; 1001 case RSN_AKM_FT_PSK: 1002 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK); 1003 break; 1004 case RSN_AKM_EAP_SHA256: 1005 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256); 1006 break; 1007 case RSN_AKM_PSK_SHA256: 1008 rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256); 1009 break; 1010 case RSN_AKM_SAE: 1011 rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE); 1012 break; 1013 case RSN_AKM_FT_SAE: 1014 rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE); 1015 break; 1016 case RSN_AKM_OWE: 1017 rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE); 1018 break; 1019 case RSN_AKM_EAP_SUITE_B_192: 1020 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192); 1021 break; 1022 case RSN_OSEN: 1023 rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); 1024 break; 1025 case RSN_AKM_FILS_SHA256: 1026 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); 1027 break; 1028 case RSN_AKM_FILS_SHA384: 1029 rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); 1030 break; 1031 default: 1032 // do nothing 1033 break; 1034 } 1035 } 1036 // Default AKM 1037 if (rsnKeyManagement.isEmpty()) { 1038 rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1039 } 1040 keyManagement.add(rsnKeyManagement); 1041 } catch (BufferUnderflowException e) { 1042 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow"); 1043 } 1044 } 1045 parseWpaCipher(int cipher)1046 private static @Cipher int parseWpaCipher(int cipher) { 1047 switch (cipher) { 1048 case WPA_CIPHER_NONE: 1049 return ScanResult.CIPHER_NONE; 1050 case WPA_CIPHER_TKIP: 1051 return ScanResult.CIPHER_TKIP; 1052 case WPA_CIPHER_CCMP: 1053 return ScanResult.CIPHER_CCMP; 1054 default: 1055 Log.w("IE_Capabilities", "Unknown WPA cipher suite: " 1056 + Integer.toHexString(cipher)); 1057 return ScanResult.CIPHER_NONE; 1058 } 1059 } 1060 parseRsnCipher(int cipher)1061 private static @Cipher int parseRsnCipher(int cipher) { 1062 switch (cipher) { 1063 case RSN_CIPHER_NONE: 1064 return ScanResult.CIPHER_NONE; 1065 case RSN_CIPHER_TKIP: 1066 return ScanResult.CIPHER_TKIP; 1067 case RSN_CIPHER_CCMP: 1068 return ScanResult.CIPHER_CCMP; 1069 case RSN_CIPHER_GCMP_256: 1070 return ScanResult.CIPHER_GCMP_256; 1071 case RSN_CIPHER_NO_GROUP_ADDRESSED: 1072 return ScanResult.CIPHER_NO_GROUP_ADDRESSED; 1073 default: 1074 Log.w("IE_Capabilities", "Unknown RSN cipher suite: " 1075 + Integer.toHexString(cipher)); 1076 return ScanResult.CIPHER_NONE; 1077 } 1078 } 1079 isWpsElement(InformationElement ie)1080 private static boolean isWpsElement(InformationElement ie) { 1081 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1082 try { 1083 // WPS OUI and type 1084 return (buf.getInt() == WPS_VENDOR_OUI_TYPE); 1085 } catch (BufferUnderflowException e) { 1086 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1087 return false; 1088 } 1089 } 1090 isWpaOneElement(InformationElement ie)1091 private static boolean isWpaOneElement(InformationElement ie) { 1092 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1093 1094 try { 1095 // WPA OUI and type 1096 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE); 1097 } catch (BufferUnderflowException e) { 1098 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1099 return false; 1100 } 1101 } 1102 1103 // WPA type 1 format (size unit: byte) 1104 // 1105 // | Element ID | Length | OUI | Type | Version | 1106 // 1 1 3 1 2 1107 // | Group Data Cipher Suite | 1108 // 4 1109 // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List | 1110 // 2 4 * m 1111 // | AKM Suite Count | AKM Suite List | 1112 // 2 4 * n 1113 // 1114 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1115 // stripped off already 1116 // parseWpaOneElement(InformationElement ie)1117 private void parseWpaOneElement(InformationElement ie) { 1118 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1119 1120 try { 1121 // skip WPA OUI and type parsing. isWpaOneElement() should have 1122 // been called for verification before we reach here. 1123 buf.getInt(); 1124 1125 // version 1126 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) { 1127 // incorrect version 1128 return; 1129 } 1130 1131 // start building the string 1132 protocol.add(ScanResult.PROTOCOL_WPA); 1133 1134 // group data cipher suite 1135 groupCipher.add(parseWpaCipher(buf.getInt())); 1136 1137 // pairwise cipher suite count 1138 short cipherCount = buf.getShort(); 1139 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>(); 1140 // pairwise chipher suite list 1141 for (int i = 0; i < cipherCount; i++) { 1142 wpaPairwiseCipher.add(parseWpaCipher(buf.getInt())); 1143 } 1144 pairwiseCipher.add(wpaPairwiseCipher); 1145 1146 // AKM 1147 // AKM suite count 1148 short akmCount = buf.getShort(); 1149 ArrayList<Integer> wpaKeyManagement = new ArrayList<>(); 1150 1151 // AKM suite list 1152 for (int i = 0; i < akmCount; i++) { 1153 int akm = buf.getInt(); 1154 switch (akm) { 1155 case WPA_AKM_EAP: 1156 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1157 break; 1158 case WPA_AKM_PSK: 1159 wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK); 1160 break; 1161 default: 1162 // do nothing 1163 break; 1164 } 1165 } 1166 // Default AKM 1167 if (wpaKeyManagement.isEmpty()) { 1168 wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP); 1169 } 1170 keyManagement.add(wpaKeyManagement); 1171 } catch (BufferUnderflowException e) { 1172 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow"); 1173 } 1174 } 1175 1176 /** 1177 * Parse the Information Element and the 16-bit Capability Information field 1178 * to build the InformationElemmentUtil.capabilities object. 1179 * 1180 * @param ies -- Information Element array 1181 * @param beaconCap -- 16-bit Beacon Capability Information field 1182 * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device 1183 */ 1184 from(InformationElement[] ies, int beaconCap, boolean isOweSupported)1185 public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported) { 1186 protocol = new ArrayList<>(); 1187 keyManagement = new ArrayList<>(); 1188 groupCipher = new ArrayList<>(); 1189 pairwiseCipher = new ArrayList<>(); 1190 1191 if (ies == null) { 1192 return; 1193 } 1194 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0; 1195 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0; 1196 isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0; 1197 for (InformationElement ie : ies) { 1198 WifiNl80211Manager.OemSecurityType oemSecurityType = 1199 WifiNl80211Manager.parseOemSecurityTypeElement( 1200 ie.id, ie.idExt, ie.bytes); 1201 if (oemSecurityType != null 1202 && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) { 1203 protocol.add(oemSecurityType.protocol); 1204 keyManagement.add(oemSecurityType.keyManagement); 1205 pairwiseCipher.add(oemSecurityType.pairwiseCipher); 1206 groupCipher.add(oemSecurityType.groupCipher); 1207 } 1208 1209 if (ie.id == InformationElement.EID_RSN) { 1210 parseRsnElement(ie); 1211 } 1212 1213 if (ie.id == InformationElement.EID_VSA) { 1214 if (isWpaOneElement(ie)) { 1215 parseWpaOneElement(ie); 1216 } 1217 if (isWpsElement(ie)) { 1218 // TODO(b/62134557): parse WPS IE to provide finer granularity information. 1219 isWPS = true; 1220 } 1221 if (isOweSupported && isOweElement(ie)) { 1222 /* From RFC 8110: Once the client and AP have finished 802.11 association, 1223 they then complete the Diffie-Hellman key exchange and create a Pairwise 1224 Master Key (PMK) and its associated identifier, PMKID [IEEE802.11]. 1225 Upon completion of 802.11 association, the AP initiates the 4-way 1226 handshake to the client using the PMK generated above. The 4-way 1227 handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation 1228 Key (KCK), and a Message Integrity Code (MIC) to use for protection 1229 of the frames that define the 4-way handshake. 1230 1231 We check if OWE is supported here because we are adding the OWE 1232 capabilities to the Open BSS. Non-supporting devices need to see this 1233 open network and ignore this element. Supporting devices need to hide 1234 the Open BSS of OWE in transition mode and connect to the Hidden one. 1235 */ 1236 protocol.add(ScanResult.PROTOCOL_RSN); 1237 groupCipher.add(ScanResult.CIPHER_CCMP); 1238 ArrayList<Integer> owePairwiseCipher = new ArrayList<>(); 1239 owePairwiseCipher.add(ScanResult.CIPHER_CCMP); 1240 pairwiseCipher.add(owePairwiseCipher); 1241 ArrayList<Integer> oweKeyManagement = new ArrayList<>(); 1242 oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION); 1243 keyManagement.add(oweKeyManagement); 1244 } 1245 } 1246 } 1247 } 1248 isOweElement(InformationElement ie)1249 private static boolean isOweElement(InformationElement ie) { 1250 ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1251 try { 1252 // OWE OUI and type 1253 return (buf.getInt() == OWE_VENDOR_OUI_TYPE); 1254 } catch (BufferUnderflowException e) { 1255 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow"); 1256 return false; 1257 } 1258 } 1259 protocolToString(@rotocol int protocol)1260 private String protocolToString(@Protocol int protocol) { 1261 switch (protocol) { 1262 case ScanResult.PROTOCOL_NONE: 1263 return "None"; 1264 case ScanResult.PROTOCOL_WPA: 1265 return "WPA"; 1266 case ScanResult.PROTOCOL_RSN: 1267 return "RSN"; 1268 case ScanResult.PROTOCOL_OSEN: 1269 return "OSEN"; 1270 case ScanResult.PROTOCOL_WAPI: 1271 return "WAPI"; 1272 default: 1273 return "?"; 1274 } 1275 } 1276 keyManagementToString(@eyMgmt int akm)1277 private String keyManagementToString(@KeyMgmt int akm) { 1278 switch (akm) { 1279 case ScanResult.KEY_MGMT_NONE: 1280 return "None"; 1281 case ScanResult.KEY_MGMT_PSK: 1282 return "PSK"; 1283 case ScanResult.KEY_MGMT_EAP: 1284 return "EAP"; 1285 case ScanResult.KEY_MGMT_FT_EAP: 1286 return "FT/EAP"; 1287 case ScanResult.KEY_MGMT_FT_PSK: 1288 return "FT/PSK"; 1289 case ScanResult.KEY_MGMT_EAP_SHA256: 1290 return "EAP-SHA256"; 1291 case ScanResult.KEY_MGMT_PSK_SHA256: 1292 return "PSK-SHA256"; 1293 case ScanResult.KEY_MGMT_OWE: 1294 return "OWE"; 1295 case ScanResult.KEY_MGMT_OWE_TRANSITION: 1296 return "OWE_TRANSITION"; 1297 case ScanResult.KEY_MGMT_SAE: 1298 return "SAE"; 1299 case ScanResult.KEY_MGMT_FT_SAE: 1300 return "FT/SAE"; 1301 case ScanResult.KEY_MGMT_EAP_SUITE_B_192: 1302 return "EAP_SUITE_B_192"; 1303 case ScanResult.KEY_MGMT_OSEN: 1304 return "OSEN"; 1305 case ScanResult.KEY_MGMT_WAPI_PSK: 1306 return "WAPI-PSK"; 1307 case ScanResult.KEY_MGMT_WAPI_CERT: 1308 return "WAPI-CERT"; 1309 case ScanResult.KEY_MGMT_FILS_SHA256: 1310 return "FILS-SHA256"; 1311 case ScanResult.KEY_MGMT_FILS_SHA384: 1312 return "FILS-SHA384"; 1313 default: 1314 return "?"; 1315 } 1316 } 1317 cipherToString(@ipher int cipher)1318 private String cipherToString(@Cipher int cipher) { 1319 switch (cipher) { 1320 case ScanResult.CIPHER_NONE: 1321 return "None"; 1322 case ScanResult.CIPHER_CCMP: 1323 return "CCMP"; 1324 case ScanResult.CIPHER_GCMP_256: 1325 return "GCMP-256"; 1326 case ScanResult.CIPHER_TKIP: 1327 return "TKIP"; 1328 case ScanResult.CIPHER_SMS4: 1329 return "SMS4"; 1330 default: 1331 return "?"; 1332 } 1333 } 1334 1335 /** 1336 * Build the ScanResult.capabilities String. 1337 * 1338 * @return security string that mirrors what wpa_supplicant generates 1339 */ generateCapabilitiesString()1340 public String generateCapabilitiesString() { 1341 StringBuilder capabilities = new StringBuilder(); 1342 // private Beacon without an RSNE or WPA IE, hence WEP0 1343 boolean isWEP = (protocol.isEmpty()) && isPrivacy; 1344 1345 if (isWEP) { 1346 capabilities.append("[WEP]"); 1347 } 1348 for (int i = 0; i < protocol.size(); i++) { 1349 String capability = generateCapabilitiesStringPerProtocol(i); 1350 // add duplicate capabilities for WPA2 for backward compatibility: 1351 // duplicate "RSN" entries as "WPA2" 1352 String capWpa2 = generateWPA2CapabilitiesString(capability, i); 1353 capabilities.append(capWpa2); 1354 capabilities.append(capability); 1355 } 1356 if (isESS) { 1357 capabilities.append("[ESS]"); 1358 } 1359 if (isIBSS) { 1360 capabilities.append("[IBSS]"); 1361 } 1362 if (isWPS) { 1363 capabilities.append("[WPS]"); 1364 } 1365 1366 return capabilities.toString(); 1367 } 1368 1369 /** 1370 * Build the Capability String for one protocol 1371 * @param index: index number of the protocol 1372 * @return security string for one protocol 1373 */ generateCapabilitiesStringPerProtocol(int index)1374 private String generateCapabilitiesStringPerProtocol(int index) { 1375 StringBuilder capability = new StringBuilder(); 1376 capability.append("[").append(protocolToString(protocol.get(index))); 1377 1378 if (index < keyManagement.size()) { 1379 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1380 capability.append((j == 0) ? "-" : "+").append( 1381 keyManagementToString(keyManagement.get(index).get(j))); 1382 } 1383 } 1384 if (index < pairwiseCipher.size()) { 1385 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1386 capability.append((j == 0) ? "-" : "+").append( 1387 cipherToString(pairwiseCipher.get(index).get(j))); 1388 } 1389 } 1390 capability.append("]"); 1391 return capability.toString(); 1392 } 1393 1394 /** 1395 * Build the duplicate Capability String for WPA2 1396 * @param cap: original capability String 1397 * @param index: index number of the protocol 1398 * @return security string for WPA2, empty String if protocol is not WPA2 1399 */ generateWPA2CapabilitiesString(String cap, int index)1400 private String generateWPA2CapabilitiesString(String cap, int index) { 1401 StringBuilder capWpa2 = new StringBuilder(); 1402 // if not WPA2, return empty String 1403 if (cap.contains("EAP_SUITE_B_192") 1404 || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP") 1405 && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) { 1406 return ""; 1407 } 1408 capWpa2.append("[").append("WPA2"); 1409 if (index < keyManagement.size()) { 1410 for (int j = 0; j < keyManagement.get(index).size(); j++) { 1411 capWpa2.append((j == 0) ? "-" : "+").append( 1412 keyManagementToString(keyManagement.get(index).get(j))); 1413 // WPA3/WPA2 transition mode 1414 if (cap.contains("SAE")) { 1415 break; 1416 } 1417 } 1418 } 1419 if (index < pairwiseCipher.size()) { 1420 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) { 1421 capWpa2.append((j == 0) ? "-" : "+").append( 1422 cipherToString(pairwiseCipher.get(index).get(j))); 1423 } 1424 } 1425 capWpa2.append("]"); 1426 return capWpa2.toString(); 1427 } 1428 } 1429 1430 1431 /** 1432 * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will 1433 * only be present in scan results that are derived from a Beacon Frame, not from the more 1434 * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct. 1435 */ 1436 public static class TrafficIndicationMap { 1437 private static final int MAX_TIM_LENGTH = 254; 1438 private boolean mValid = false; 1439 public int mLength = 0; 1440 public int mDtimCount = -1; 1441 //Negative DTIM Period means no TIM element was given this frame. 1442 public int mDtimPeriod = -1; 1443 public int mBitmapControl = 0; 1444 1445 /** 1446 * Is this a valid TIM information element. 1447 */ isValid()1448 public boolean isValid() { 1449 return mValid; 1450 } 1451 1452 // Traffic Indication Map format (size unit: byte) 1453 // 1454 //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap | 1455 // 1 1 1 1 1 1 - 251 1456 // 1457 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1458 // stripped off already 1459 // from(InformationElement ie)1460 public void from(InformationElement ie) { 1461 mValid = false; 1462 if (ie == null || ie.bytes == null) return; 1463 mLength = ie.bytes.length; 1464 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1465 try { 1466 mDtimCount = data.get() & Constants.BYTE_MASK; 1467 mDtimPeriod = data.get() & Constants.BYTE_MASK; 1468 mBitmapControl = data.get() & Constants.BYTE_MASK; 1469 //A valid TIM element must have atleast one more byte 1470 data.get(); 1471 } catch (BufferUnderflowException e) { 1472 return; 1473 } 1474 if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { 1475 mValid = true; 1476 } 1477 } 1478 } 1479 1480 /** 1481 * This util class determines the 802.11 standard (a/b/g/n/ac/ax) being used 1482 */ 1483 public static class WifiMode { 1484 public static final int MODE_UNDEFINED = 0; // Unknown/undefined 1485 public static final int MODE_11A = 1; // 802.11a 1486 public static final int MODE_11B = 2; // 802.11b 1487 public static final int MODE_11G = 3; // 802.11g 1488 public static final int MODE_11N = 4; // 802.11n 1489 public static final int MODE_11AC = 5; // 802.11ac 1490 public static final int MODE_11AX = 6; // 802.11ax 1491 //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A 1492 1493 /** 1494 * Use frequency, max supported rate, and the existence of HE, VHT, HT & ERP fields in scan 1495 * scan result to determine the 802.11 Wifi standard being used. 1496 */ determineMode(int frequency, int maxRate, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)1497 public static int determineMode(int frequency, int maxRate, boolean foundHe, 1498 boolean foundVht, boolean foundHt, boolean foundErp) { 1499 if (foundHe) { 1500 return MODE_11AX; 1501 } else if (!ScanResult.is24GHz(frequency) && foundVht) { 1502 // Do not include subset of VHT on 2.4 GHz vendor extension 1503 // in consideration for reporting VHT. 1504 return MODE_11AC; 1505 } else if (foundHt) { 1506 return MODE_11N; 1507 } else if (foundErp) { 1508 return MODE_11G; 1509 } else if (ScanResult.is24GHz(frequency)) { 1510 if (maxRate < 24000000) { 1511 return MODE_11B; 1512 } else { 1513 return MODE_11G; 1514 } 1515 } else { 1516 return MODE_11A; 1517 } 1518 } 1519 1520 /** 1521 * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC> 1522 */ toString(int mode)1523 public static String toString(int mode) { 1524 switch(mode) { 1525 case MODE_11A: 1526 return "MODE_11A"; 1527 case MODE_11B: 1528 return "MODE_11B"; 1529 case MODE_11G: 1530 return "MODE_11G"; 1531 case MODE_11N: 1532 return "MODE_11N"; 1533 case MODE_11AC: 1534 return "MODE_11AC"; 1535 case MODE_11AX: 1536 return "MODE_11AX"; 1537 default: 1538 return "MODE_UNDEFINED"; 1539 } 1540 } 1541 } 1542 1543 /** 1544 * Parser for both the Supported Rates & Extended Supported Rates Information Elements 1545 */ 1546 public static class SupportedRates { 1547 public static final int MASK = 0x7F; // 0111 1111 1548 public boolean mValid = false; 1549 public ArrayList<Integer> mRates; 1550 SupportedRates()1551 public SupportedRates() { 1552 mRates = new ArrayList<Integer>(); 1553 } 1554 1555 /** 1556 * Is this a valid Supported Rates information element. 1557 */ isValid()1558 public boolean isValid() { 1559 return mValid; 1560 } 1561 1562 /** 1563 * get the Rate in bits/s from associated byteval 1564 */ getRateFromByte(int byteVal)1565 public static int getRateFromByte(int byteVal) { 1566 byteVal &= MASK; 1567 switch(byteVal) { 1568 case 2: 1569 return 1000000; 1570 case 4: 1571 return 2000000; 1572 case 11: 1573 return 5500000; 1574 case 12: 1575 return 6000000; 1576 case 18: 1577 return 9000000; 1578 case 22: 1579 return 11000000; 1580 case 24: 1581 return 12000000; 1582 case 36: 1583 return 18000000; 1584 case 44: 1585 return 22000000; 1586 case 48: 1587 return 24000000; 1588 case 66: 1589 return 33000000; 1590 case 72: 1591 return 36000000; 1592 case 96: 1593 return 48000000; 1594 case 108: 1595 return 54000000; 1596 default: 1597 //ERROR UNKNOWN RATE 1598 return -1; 1599 } 1600 } 1601 1602 // Supported Rates format (size unit: byte) 1603 // 1604 //| ElementID | Length | Supported Rates [7 Little Endian Info bits - 1 Flag bit] 1605 // 1 1 1 - 8 1606 // 1607 // Note: InformationElement.bytes has 'Element ID' and 'Length' 1608 // stripped off already 1609 // from(InformationElement ie)1610 public void from(InformationElement ie) { 1611 mValid = false; 1612 if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1) { 1613 return; 1614 } 1615 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 1616 try { 1617 for (int i = 0; i < ie.bytes.length; i++) { 1618 int rate = getRateFromByte(data.get()); 1619 if (rate > 0) { 1620 mRates.add(rate); 1621 } else { 1622 return; 1623 } 1624 } 1625 } catch (BufferUnderflowException e) { 1626 return; 1627 } 1628 mValid = true; 1629 return; 1630 } 1631 1632 /** 1633 * Lists the rates in a human readable string 1634 */ toString()1635 public String toString() { 1636 StringBuilder sbuf = new StringBuilder(); 1637 for (Integer rate : mRates) { 1638 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", "); 1639 } 1640 return sbuf.toString(); 1641 } 1642 } 1643 } 1644