NetworkDetail.java revision 243931f3474f6235cfcf5c1a55fa2f192ee264ae
1package com.android.server.wifi.hotspot2; 2 3import android.net.wifi.ScanResult; 4 5import com.android.server.wifi.anqp.ANQPElement; 6import com.android.server.wifi.anqp.Constants; 7import com.android.server.wifi.anqp.VenueNameElement; 8 9import java.net.ProtocolException; 10import java.nio.ByteBuffer; 11import java.nio.ByteOrder; 12import java.nio.charset.Charset; 13import java.nio.charset.StandardCharsets; 14import java.util.List; 15import java.util.Map; 16 17import static com.android.server.wifi.anqp.Constants.BYTES_IN_EUI48; 18import static com.android.server.wifi.anqp.Constants.BYTE_MASK; 19import static com.android.server.wifi.anqp.Constants.getInteger; 20import android.util.Log; 21public class NetworkDetail { 22 23 private static final int EID_SSID = 0; 24 private static final int EID_BSSLoad = 11; 25 private static final int EID_HT_OPERATION = 61; 26 private static final int EID_VHT_OPERATION = 192; 27 private static final int EID_Interworking = 107; 28 private static final int EID_RoamingConsortium = 111; 29 private static final int EID_ExtendedCaps = 127; 30 private static final int EID_VSA = 221; 31 private static final int ANQP_DOMID_BIT = 0x04; 32 private static final int RTT_RESP_ENABLE_BIT = 70; 33 34 private static final long SSID_UTF8_BIT = 0x0001000000000000L; 35 //turn off when SHIP 36 private static final boolean DBG = true; 37 private static final String TAG = "NetworkDetail:"; 38 39 public enum Ant { 40 Private, 41 PrivateWithGuest, 42 ChargeablePublic, 43 FreePublic, 44 Personal, 45 EmergencyOnly, 46 Resvd6, 47 Resvd7, 48 Resvd8, 49 Resvd9, 50 Resvd10, 51 Resvd11, 52 Resvd12, 53 Resvd13, 54 TestOrExperimental, 55 Wildcard 56 } 57 58 public enum HSRelease { 59 R1, 60 R2, 61 Unknown 62 } 63 64 // General identifiers: 65 private final String mSSID; 66 private final long mHESSID; 67 private final long mBSSID; 68 69 // BSS Load element: 70 private final int mStationCount; 71 private final int mChannelUtilization; 72 private final int mCapacity; 73 74 //channel detailed information 75 /* 76 * 0 -- 20 MHz 77 * 1 -- 40 MHz 78 * 2 -- 80 MHz 79 * 3 -- 160 MHz 80 * 4 -- 80 + 80 MHz 81 */ 82 private final int mChannelWidth; 83 private final int mPrimaryFreq; 84 private final int mCenterfreq0; 85 private final int mCenterfreq1; 86 private final boolean m80211McRTTResponder; 87 /* 88 * From Interworking element: 89 * mAnt non null indicates the presence of Interworking, i.e. 802.11u 90 * mVenueGroup and mVenueType may be null if not present in the Interworking element. 91 */ 92 private final Ant mAnt; 93 private final boolean mInternet; 94 private final VenueNameElement.VenueGroup mVenueGroup; 95 private final VenueNameElement.VenueType mVenueType; 96 97 /* 98 * From HS20 Indication element: 99 * mHSRelease is null only if the HS20 Indication element was not present. 100 * mAnqpDomainID is set to -1 if not present in the element. 101 */ 102 private final HSRelease mHSRelease; 103 private final int mAnqpDomainID; 104 105 /* 106 * From beacon: 107 * mAnqpOICount is how many additional OIs are available through ANQP. 108 * mRoamingConsortiums is either null, if the element was not present, or is an array of 109 * 1, 2 or 3 longs in which the roaming consortium values occupy the LSBs. 110 */ 111 private final int mAnqpOICount; 112 private final long[] mRoamingConsortiums; 113 114 private final Long mExtendedCapabilities; 115 116 private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements; 117 118 public NetworkDetail(String bssid, String infoElements, List<String> anqpLines, int freq) { 119 120 if (infoElements == null) { 121 throw new IllegalArgumentException("Null information element string"); 122 } 123 int separator = infoElements.indexOf('='); 124 if (separator<0) { 125 throw new IllegalArgumentException("No element separator"); 126 } 127 128 mBSSID = parseMac(bssid); 129 130 ByteBuffer data = ByteBuffer.wrap(Utils.hexToBytes(infoElements.substring(separator + 1))) 131 .order(ByteOrder.LITTLE_ENDIAN); 132 133 String ssid = null; 134 byte[] ssidOctets = null; 135 int stationCount = 0; 136 int channelUtilization = 0; 137 int capacity = 0; 138 139 Ant ant = null; 140 boolean internet = false; 141 VenueNameElement.VenueGroup venueGroup = null; 142 VenueNameElement.VenueType venueType = null; 143 long hessid = 0L; 144 145 int anqpOICount = 0; 146 long[] roamingConsortiums = null; 147 148 HSRelease hsRelease = null; 149 int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP. 150 151 Long extendedCapabilities = null; 152 153 int secondChanelOffset = 0; 154 int channelMode = 0; 155 int centerFreqIndex1 = 0; 156 int centerFreqIndex2 = 0; 157 boolean RTTResponder = false; 158 159 Log.e(TAG,"IE Length is %d" + data.remaining()); 160 while (data.hasRemaining()) { 161 int eid = data.get() & Constants.BYTE_MASK; 162 int elementLength = data.get() & Constants.BYTE_MASK; 163 Log.e(TAG,"eid is:" + eid + " elementLength:" + elementLength); 164 if (elementLength > data.remaining()) { 165 throw new IllegalArgumentException("Length out of bounds: " + elementLength +" ," + data.remaining()); 166 } 167 168 switch (eid) { 169 case EID_SSID: 170 ssidOctets = new byte[elementLength]; 171 data.get(ssidOctets); 172 break; 173 case EID_BSSLoad: 174 if (elementLength != 5) { 175 throw new IllegalArgumentException("BSS Load element length is not 5: " + 176 elementLength); 177 } 178 stationCount = data.getShort() & Constants.SHORT_MASK; 179 channelUtilization = data.get() & Constants.BYTE_MASK; 180 capacity = data.getShort() & Constants.SHORT_MASK; 181 break; 182 case EID_HT_OPERATION: 183 int primary_channel = data.get(); 184 byte tmp = data.get(); 185 secondChanelOffset = tmp & 0x3; 186 data.position(data.position() + elementLength - 2); 187 if(DBG) { 188 Log.d(TAG, "primary_channel: " + primary_channel + " secondChanelOffset:" 189 + tmp); 190 } 191 break; 192 case EID_VHT_OPERATION: 193 channelMode = data.get() & Constants.BYTE_MASK; 194 centerFreqIndex1 = data.get() & Constants.BYTE_MASK; 195 centerFreqIndex2 = data.get() & Constants.BYTE_MASK; 196 data.position(data.position() + elementLength - 3); 197 if(DBG) { 198 Log.d(TAG, "channelMode :" + channelMode + " centerFreqIndex1: " + 199 centerFreqIndex1 + " centerFreqIndex2: " + centerFreqIndex1); 200 } 201 break; 202 case EID_Interworking: 203 int anOptions = data.get() & Constants.BYTE_MASK; 204 ant = Ant.values()[anOptions&0x0f]; 205 internet = ( anOptions & 0x10 ) != 0; 206 // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID 207 if (elementLength == 3 || elementLength == 9) { 208 try { 209 ByteBuffer vinfo = data.duplicate(); 210 vinfo.limit(vinfo.position() + 2); 211 VenueNameElement vne = 212 new VenueNameElement(Constants.ANQPElementType.ANQPVenueName, 213 vinfo); 214 venueGroup = vne.getGroup(); 215 venueType = vne.getType(); 216 data.getShort(); 217 } 218 catch ( ProtocolException pe ) { 219 /*Cannot happen*/ 220 } 221 } 222 if (elementLength == 7 || elementLength == 9) { 223 hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6); 224 } 225 break; 226 case EID_RoamingConsortium: 227 anqpOICount = data.get() & Constants.BYTE_MASK; 228 229 int oi12Length = data.get() & Constants.BYTE_MASK; 230 int oi1Length = oi12Length & Constants.NIBBLE_MASK; 231 int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK; 232 int oi3Length = elementLength - 2 - oi1Length - oi2Length; 233 int oiCount = 0; 234 if (oi1Length > 0) { 235 oiCount++; 236 if (oi2Length > 0) { 237 oiCount++; 238 if (oi3Length > 0) { 239 oiCount++; 240 } 241 } 242 } 243 roamingConsortiums = new long[oiCount]; 244 if (oi1Length > 0 ) { 245 roamingConsortiums[0] = getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); 246 } 247 if (oi2Length > 0 ) { 248 roamingConsortiums[1] = getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); 249 } 250 if (oi3Length > 0 ) { 251 roamingConsortiums[2] = getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); 252 } 253 break; 254 case EID_VSA: 255 if (elementLength < 5) { 256 data.position(data.position() + elementLength); 257 } 258 else if (data.getInt() != Constants.HS20_FRAME_PREFIX) { 259 data.position(data.position() + elementLength - Constants.BYTES_IN_INT); 260 } 261 else { 262 int hsConf = data.get() & Constants.BYTE_MASK; 263 switch ((hsConf>>4) & Constants.NIBBLE_MASK) { 264 case 0: 265 hsRelease = HSRelease.R1; 266 break; 267 case 1: 268 hsRelease = HSRelease.R2; 269 break; 270 default: 271 hsRelease = HSRelease.Unknown; 272 break; 273 } 274 if ((hsConf & ANQP_DOMID_BIT) != 0) { 275 anqpDomainID = data.getShort() & Constants.SHORT_MASK; 276 } 277 } 278 break; 279 case EID_ExtendedCaps: 280 int position = data.position(); 281 extendedCapabilities = 282- Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, elementLength); 283 284 //recover 285 data.position(position); 286 int index = RTT_RESP_ENABLE_BIT / 8; 287 byte offset = RTT_RESP_ENABLE_BIT % 8; 288 289 if(elementLength < index + 1) { 290 RTTResponder = false; 291 data.position(data.position() + elementLength); 292 break; 293 } 294 295 data.position(data.position() + index); 296 elementLength -= index; 297 298 if ((data.get() & (0x1 << offset)) != 0) { 299 RTTResponder = true; 300 } else { 301 RTTResponder = false; 302 } 303 data.position(data.position()+ elementLength - 1); 304 break; 305 default: 306 data.position(data.position()+elementLength); 307 break; 308 } 309 } 310 311 if (ssidOctets != null) { 312 Charset encoding; 313 if (extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0) { 314 encoding = StandardCharsets.UTF_8; 315 } 316 else { 317 encoding = StandardCharsets.ISO_8859_1; 318 } 319 ssid = new String(ssidOctets, encoding); 320 } 321 322 mSSID = ssid; 323 mHESSID = hessid; 324 mStationCount = stationCount; 325 mChannelUtilization = channelUtilization; 326 mCapacity = capacity; 327 mAnt = ant; 328 mInternet = internet; 329 mVenueGroup = venueGroup; 330 mVenueType = venueType; 331 mHSRelease = hsRelease; 332 mAnqpDomainID = anqpDomainID; 333 mAnqpOICount = anqpOICount; 334 mRoamingConsortiums = roamingConsortiums; 335 mExtendedCapabilities = extendedCapabilities; 336 mANQPElements = SupplicantBridge.parseANQPLines(anqpLines); 337 //set up channel info 338 mPrimaryFreq = freq; 339 340 if (channelMode != 0) { 341 // 80 or 160 MHz 342 mChannelWidth = channelMode + 1; 343 mCenterfreq0 = (centerFreqIndex1 - 36) * 5 + 5180; 344 if(channelMode > 1) { //160MHz 345 mCenterfreq1 = (centerFreqIndex2 - 36) * 5 + 5180; 346 } else { 347 mCenterfreq1 = 0; 348 } 349 } else { 350 //20 or 40 MHz 351 if (secondChanelOffset != 0) {//40MHz 352 mChannelWidth = 1; 353 if (secondChanelOffset == 1) { 354 mCenterfreq0 = mPrimaryFreq + 20; 355 } else if (secondChanelOffset == 3) { 356 mCenterfreq0 = mPrimaryFreq - 20; 357 } else { 358 mCenterfreq0 = 0; 359 Log.e(TAG,"Error on secondChanelOffset"); 360 } 361 } else { 362 mCenterfreq0 = 0; 363 mChannelWidth = 0; 364 } 365 mCenterfreq1 = 0; 366 } 367 m80211McRTTResponder = RTTResponder; 368 if(DBG) { 369 Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " + mPrimaryFreq + 370 " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: " + mCenterfreq1 + 371 (m80211McRTTResponder ? "Support RTT reponder" : "Do not support RTT responder")); 372 } 373 } 374 375 private NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 376 mSSID = base.mSSID; 377 mBSSID = base.mBSSID; 378 mHESSID = base.mHESSID; 379 mStationCount = base.mStationCount; 380 mChannelUtilization = base.mChannelUtilization; 381 mCapacity = base.mCapacity; 382 mAnt = base.mAnt; 383 mInternet = base.mInternet; 384 mVenueGroup = base.mVenueGroup; 385 mVenueType = base.mVenueType; 386 mHSRelease = base.mHSRelease; 387 mAnqpDomainID = base.mAnqpDomainID; 388 mAnqpOICount = base.mAnqpOICount; 389 mRoamingConsortiums = base.mRoamingConsortiums; 390 mExtendedCapabilities = base.mExtendedCapabilities; 391 mANQPElements = anqpElements; 392 mChannelWidth = base.mChannelWidth; 393 mPrimaryFreq = base.mPrimaryFreq; 394 mCenterfreq0 = base.mCenterfreq0; 395 mCenterfreq1 = base.mCenterfreq1; 396 m80211McRTTResponder = base.m80211McRTTResponder; 397 } 398 399 public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 400 return new NetworkDetail(this, anqpElements); 401 } 402 403 private static long parseMac(String s) { 404 405 long mac = 0; 406 int count = 0; 407 for (int n = 0; n < s.length(); n++) { 408 int nibble = Utils.fromHex(s.charAt(n), true); 409 if (nibble >= 0) { 410 mac = (mac << 4) | nibble; 411 count++; 412 } 413 } 414 if (count < 12 || (count&1) == 1) { 415 throw new IllegalArgumentException("Bad MAC address: '" + s + "'"); 416 } 417 return mac; 418 } 419 420 public boolean has80211uInfo() { 421 return mAnt != null || mRoamingConsortiums != null || mHSRelease != null; 422 } 423 424 public boolean hasInterworking() { 425 return mAnt != null; 426 } 427 428 public String getSSID() { 429 return mSSID; 430 } 431 432 public long getHESSID() { 433 return mHESSID; 434 } 435 436 public long getBSSID() { 437 return mBSSID; 438 } 439 440 public int getStationCount() { 441 return mStationCount; 442 } 443 444 public int getChannelUtilization() { 445 return mChannelUtilization; 446 } 447 448 public int getCapacity() { 449 return mCapacity; 450 } 451 452 public boolean isInterworking() { 453 return mAnt != null; 454 } 455 456 public Ant getAnt() { 457 return mAnt; 458 } 459 460 public boolean isInternet() { 461 return mInternet; 462 } 463 464 public VenueNameElement.VenueGroup getVenueGroup() { 465 return mVenueGroup; 466 } 467 468 public VenueNameElement.VenueType getVenueType() { 469 return mVenueType; 470 } 471 472 public HSRelease getHSRelease() { 473 return mHSRelease; 474 } 475 476 public int getAnqpDomainID() { 477 return mAnqpDomainID; 478 } 479 480 public int getAnqpOICount() { 481 return mAnqpOICount; 482 } 483 484 public long[] getRoamingConsortiums() { 485 return mRoamingConsortiums; 486 } 487 488 public Long getExtendedCapabilities() { 489 return mExtendedCapabilities; 490 } 491 492 public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() { 493 return mANQPElements; 494 } 495 496 public int getChannelWidth() { 497 return mChannelWidth; 498 } 499 500 public int getCenterfreq0() { 501 return mCenterfreq0; 502 } 503 504 public int getCenterfreq1() { 505 return mCenterfreq1; 506 } 507 508 public boolean is80211McResponderSupport() { 509 return m80211McRTTResponder; 510 } 511 512 public boolean isSSID_UTF8() { 513 return mExtendedCapabilities != null && (mExtendedCapabilities & SSID_UTF8_BIT) != 0; 514 } 515 516 @Override 517 public boolean equals(Object thatObject) { 518 if (this == thatObject) { 519 return true; 520 } 521 if (thatObject == null || getClass() != thatObject.getClass()) { 522 return false; 523 } 524 525 NetworkDetail that = (NetworkDetail)thatObject; 526 527 return getSSID().equals(that.getSSID()) && getBSSID() == that.getBSSID(); 528 } 529 530 @Override 531 public int hashCode() { 532 return ((mSSID.hashCode() * 31) + (int)(mBSSID >>> 32)) * 31 + (int)mBSSID; 533 } 534 535 @Override 536 public String toString() { 537 return String.format("NetworkInfo{mSSID='%s', mHESSID=%x, mBSSID=%x, mStationCount=%d, " + 538 "mChannelUtilization=%d, mCapacity=%d, mAnt=%s, mInternet=%s, " + 539 "mVenueGroup=%s, mVenueType=%s, mHSRelease=%s, mAnqpDomainID=%d, " + 540 "mAnqpOICount=%d, mRoamingConsortiums=%s}", 541 mSSID, mHESSID, mBSSID, mStationCount, 542 mChannelUtilization, mCapacity, mAnt, mInternet, 543 mVenueGroup, mVenueType, mHSRelease, mAnqpDomainID, 544 mAnqpOICount, Utils.roamingConsortiumsToString(mRoamingConsortiums)); 545 } 546 547 public String toKeyString() { 548 return String.format("'%s':%s", mSSID, getBSSIDString()); 549 } 550 551 public String getBSSIDString() { 552 return toMACString(mBSSID); 553 } 554 555 public static String toMACString(long mac) { 556 StringBuilder sb = new StringBuilder(); 557 boolean first = true; 558 for (int n = BYTES_IN_EUI48 - 1; n >= 0; n--) { 559 if (first) { 560 first = false; 561 } else { 562 sb.append(':'); 563 } 564 sb.append(String.format("%02x", (mac >>> (n * Byte.SIZE)) & BYTE_MASK)); 565 } 566 return sb.toString(); 567 } 568 569 private static final String IE = "ie=" + 570 "000477696e67" + // SSID wing 571 "0b052a00cf611e" + // BSS Load 42:207:7777 572 "6b091e0a01610408621205" + // internet:Experimental:Vehicular:Auto:hessid 573 "6f0a0e530111112222222229" + // 14:111111:2222222229 574 "dd07506f9a10143a01"; // r2:314 575 576 private static final String IE2 = "ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000"; 577 578 public static void main(String[] args) { 579 ScanResult scanResult = new ScanResult(); 580 scanResult.SSID = "wing"; 581 scanResult.BSSID = "610408"; 582 NetworkDetail nwkDetail = new NetworkDetail(scanResult.BSSID, IE2, null, 0); 583 System.out.println(nwkDetail); 584 } 585} 586