1 /* 2 * Copyright (C) 2008 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 17 package android.net.wifi; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 22 import java.util.ArrayList; 23 import java.util.List; 24 25 /** 26 * Describes information about a detected access point. In addition 27 * to the attributes described here, the supplicant keeps track of 28 * {@code quality}, {@code noise}, and {@code maxbitrate} attributes, 29 * but does not currently report them to external clients. 30 */ 31 public class ScanResult implements Parcelable { 32 /** 33 * The network name. 34 */ 35 public String SSID; 36 37 /** 38 * Ascii encoded SSID. This will replace SSID when we deprecate it. @hide 39 */ 40 public WifiSsid wifiSsid; 41 42 /** 43 * The address of the access point. 44 */ 45 public String BSSID; 46 47 /** 48 * The HESSID from the beacon. 49 * @hide 50 */ 51 public long hessid; 52 53 /** 54 * The ANQP Domain ID from the Hotspot 2.0 Indication element, if present. 55 * @hide 56 */ 57 public int anqpDomainId; 58 59 /* 60 * This field is equivalent to the |flags|, rather than the |capabilities| field 61 * of the per-BSS scan results returned by WPA supplicant. See the definition of 62 * |struct wpa_bss| in wpa_supplicant/bss.h for more details. 63 */ 64 /** 65 * Describes the authentication, key management, and encryption schemes 66 * supported by the access point. 67 */ 68 public String capabilities; 69 /** 70 * The detected signal level in dBm, also known as the RSSI. 71 * 72 * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into 73 * an absolute signal level which can be displayed to a user. 74 */ 75 public int level; 76 /** 77 * The primary 20 MHz frequency (in MHz) of the channel over which the client is communicating 78 * with the access point. 79 */ 80 public int frequency; 81 82 /** 83 * AP Channel bandwidth is 20 MHZ 84 */ 85 public static final int CHANNEL_WIDTH_20MHZ = 0; 86 /** 87 * AP Channel bandwidth is 40 MHZ 88 */ 89 public static final int CHANNEL_WIDTH_40MHZ = 1; 90 /** 91 * AP Channel bandwidth is 80 MHZ 92 */ 93 public static final int CHANNEL_WIDTH_80MHZ = 2; 94 /** 95 * AP Channel bandwidth is 160 MHZ 96 */ 97 public static final int CHANNEL_WIDTH_160MHZ = 3; 98 /** 99 * AP Channel bandwidth is 160 MHZ, but 80MHZ + 80MHZ 100 */ 101 public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; 102 103 /** 104 * AP Channel bandwidth; one of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ}, 105 * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ} 106 * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}. 107 */ 108 public int channelWidth; 109 110 /** 111 * Not used if the AP bandwidth is 20 MHz 112 * If the AP use 40, 80 or 160 MHz, this is the center frequency (in MHz) 113 * if the AP use 80 + 80 MHz, this is the center frequency of the first segment (in MHz) 114 */ 115 public int centerFreq0; 116 117 /** 118 * Only used if the AP bandwidth is 80 + 80 MHz 119 * if the AP use 80 + 80 MHz, this is the center frequency of the second segment (in MHz) 120 */ 121 public int centerFreq1; 122 123 /** 124 * @deprecated use is80211mcResponder() instead 125 * @hide 126 */ 127 public boolean is80211McRTTResponder; 128 129 /** 130 * timestamp in microseconds (since boot) when 131 * this result was last seen. 132 */ 133 public long timestamp; 134 135 /** 136 * Timestamp representing date when this result was last seen, in milliseconds from 1970 137 * {@hide} 138 */ 139 public long seen; 140 141 /** 142 * If the scan result is a valid autojoin candidate 143 * {@hide} 144 */ 145 public int isAutoJoinCandidate; 146 147 /** 148 * @hide 149 * Update RSSI of the scan result 150 * @param previousRssi 151 * @param previousSeen 152 * @param maxAge 153 */ averageRssi(int previousRssi, long previousSeen, int maxAge)154 public void averageRssi(int previousRssi, long previousSeen, int maxAge) { 155 156 if (seen == 0) { 157 seen = System.currentTimeMillis(); 158 } 159 long age = seen - previousSeen; 160 161 if (previousSeen > 0 && age > 0 && age < maxAge/2) { 162 // Average the RSSI with previously seen instances of this scan result 163 double alpha = 0.5 - (double) age / (double) maxAge; 164 level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha); 165 } 166 } 167 168 /** 169 * num IP configuration failures 170 * @hide 171 */ 172 public int numIpConfigFailures; 173 174 /** 175 * @hide 176 * Last time we blacklisted the ScanResult 177 */ 178 public long blackListTimestamp; 179 180 /** 181 * Status: indicating the scan result is not a result 182 * that is part of user's saved configurations 183 * @hide 184 */ 185 public boolean untrusted; 186 187 /** 188 * Number of time we connected to it 189 * @hide 190 */ 191 public int numConnection; 192 193 /** 194 * Number of time autojoin used it 195 * @hide 196 */ 197 public int numUsage; 198 199 /** 200 * The approximate distance to the AP in centimeter, if available. Else 201 * {@link UNSPECIFIED}. 202 * {@hide} 203 */ 204 public int distanceCm; 205 206 /** 207 * The standard deviation of the distance to the access point, if available. 208 * Else {@link UNSPECIFIED}. 209 * {@hide} 210 */ 211 public int distanceSdCm; 212 213 /** {@hide} */ 214 public static final long FLAG_PASSPOINT_NETWORK = 0x0000000000000001; 215 216 /** {@hide} */ 217 public static final long FLAG_80211mc_RESPONDER = 0x0000000000000002; 218 219 /* 220 * These flags are specific to the ScanResult class, and are not related to the |flags| 221 * field of the per-BSS scan results from WPA supplicant. 222 */ 223 /** 224 * Defines flags; such as {@link #FLAG_PASSPOINT_NETWORK}. 225 * {@hide} 226 */ 227 public long flags; 228 229 /** 230 * sets a flag in {@link #flags} field 231 * @param flag flag to set 232 * @hide 233 */ setFlag(long flag)234 public void setFlag(long flag) { 235 flags |= flag; 236 } 237 238 /** 239 * clears a flag in {@link #flags} field 240 * @param flag flag to set 241 * @hide 242 */ clearFlag(long flag)243 public void clearFlag(long flag) { 244 flags &= ~flag; 245 } 246 is80211mcResponder()247 public boolean is80211mcResponder() { 248 return (flags & FLAG_80211mc_RESPONDER) != 0; 249 } 250 isPasspointNetwork()251 public boolean isPasspointNetwork() { 252 return (flags & FLAG_PASSPOINT_NETWORK) != 0; 253 } 254 255 /** 256 * Indicates venue name (such as 'San Francisco Airport') published by access point; only 257 * available on passpoint network and if published by access point. 258 */ 259 public CharSequence venueName; 260 261 /** 262 * Indicates passpoint operator name published by access point. 263 */ 264 public CharSequence operatorFriendlyName; 265 266 /** 267 * {@hide} 268 */ 269 public final static int UNSPECIFIED = -1; 270 /** 271 * @hide 272 */ is24GHz()273 public boolean is24GHz() { 274 return ScanResult.is24GHz(frequency); 275 } 276 277 /** 278 * @hide 279 * TODO: makes real freq boundaries 280 */ is24GHz(int freq)281 public static boolean is24GHz(int freq) { 282 return freq > 2400 && freq < 2500; 283 } 284 285 /** 286 * @hide 287 */ is5GHz()288 public boolean is5GHz() { 289 return ScanResult.is5GHz(frequency); 290 } 291 292 /** 293 * @hide 294 * TODO: makes real freq boundaries 295 */ is5GHz(int freq)296 public static boolean is5GHz(int freq) { 297 return freq > 4900 && freq < 5900; 298 } 299 300 /** 301 * @hide 302 * anqp lines from supplicant BSS response 303 */ 304 public List<String> anqpLines; 305 306 /** 307 * @hide 308 * storing the raw bytes of full result IEs 309 **/ 310 public byte[] bytes; 311 312 /** information elements from beacon 313 * @hide 314 */ 315 public static class InformationElement { 316 public static final int EID_SSID = 0; 317 public static final int EID_SUPPORTED_RATES = 1; 318 public static final int EID_TIM = 5; 319 public static final int EID_BSS_LOAD = 11; 320 public static final int EID_ERP = 42; 321 public static final int EID_RSN = 48; 322 public static final int EID_EXTENDED_SUPPORTED_RATES = 50; 323 public static final int EID_HT_OPERATION = 61; 324 public static final int EID_INTERWORKING = 107; 325 public static final int EID_ROAMING_CONSORTIUM = 111; 326 public static final int EID_EXTENDED_CAPS = 127; 327 public static final int EID_VHT_OPERATION = 192; 328 public static final int EID_VSA = 221; 329 330 public int id; 331 public byte[] bytes; 332 InformationElement()333 public InformationElement() { 334 } 335 InformationElement(InformationElement rhs)336 public InformationElement(InformationElement rhs) { 337 this.id = rhs.id; 338 this.bytes = rhs.bytes.clone(); 339 } 340 } 341 342 /** information elements found in the beacon 343 * @hide 344 */ 345 public InformationElement[] informationElements; 346 347 /** ANQP response elements. 348 * @hide 349 */ 350 public AnqpInformationElement[] anqpElements; 351 352 /** {@hide} */ ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId, byte[] osuProviders, String caps, int level, int frequency, long tsf)353 public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId, 354 byte[] osuProviders, String caps, int level, int frequency, long tsf) { 355 this.wifiSsid = wifiSsid; 356 this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; 357 this.BSSID = BSSID; 358 this.hessid = hessid; 359 this.anqpDomainId = anqpDomainId; 360 if (osuProviders != null) { 361 this.anqpElements = new AnqpInformationElement[1]; 362 this.anqpElements[0] = 363 new AnqpInformationElement(AnqpInformationElement.HOTSPOT20_VENDOR_ID, 364 AnqpInformationElement.HS_OSU_PROVIDERS, osuProviders); 365 } 366 this.capabilities = caps; 367 this.level = level; 368 this.frequency = frequency; 369 this.timestamp = tsf; 370 this.distanceCm = UNSPECIFIED; 371 this.distanceSdCm = UNSPECIFIED; 372 this.channelWidth = UNSPECIFIED; 373 this.centerFreq0 = UNSPECIFIED; 374 this.centerFreq1 = UNSPECIFIED; 375 this.flags = 0; 376 } 377 378 /** {@hide} */ ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, long tsf, int distCm, int distSdCm)379 public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, 380 long tsf, int distCm, int distSdCm) { 381 this.wifiSsid = wifiSsid; 382 this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; 383 this.BSSID = BSSID; 384 this.capabilities = caps; 385 this.level = level; 386 this.frequency = frequency; 387 this.timestamp = tsf; 388 this.distanceCm = distCm; 389 this.distanceSdCm = distSdCm; 390 this.channelWidth = UNSPECIFIED; 391 this.centerFreq0 = UNSPECIFIED; 392 this.centerFreq1 = UNSPECIFIED; 393 this.flags = 0; 394 } 395 396 /** {@hide} */ ScanResult(String Ssid, String BSSID, long hessid, int anqpDomainId, String caps, int level, int frequency, long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, boolean is80211McRTTResponder)397 public ScanResult(String Ssid, String BSSID, long hessid, int anqpDomainId, String caps, 398 int level, int frequency, 399 long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, 400 boolean is80211McRTTResponder) { 401 this.SSID = Ssid; 402 this.BSSID = BSSID; 403 this.hessid = hessid; 404 this.anqpDomainId = anqpDomainId; 405 this.capabilities = caps; 406 this.level = level; 407 this.frequency = frequency; 408 this.timestamp = tsf; 409 this.distanceCm = distCm; 410 this.distanceSdCm = distSdCm; 411 this.channelWidth = channelWidth; 412 this.centerFreq0 = centerFreq0; 413 this.centerFreq1 = centerFreq1; 414 if (is80211McRTTResponder) { 415 this.flags = FLAG_80211mc_RESPONDER; 416 } else { 417 this.flags = 0; 418 } 419 } 420 421 /** {@hide} */ ScanResult(WifiSsid wifiSsid, String Ssid, String BSSID, long hessid, int anqpDomainId, String caps, int level, int frequency, long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, boolean is80211McRTTResponder)422 public ScanResult(WifiSsid wifiSsid, String Ssid, String BSSID, long hessid, int anqpDomainId, 423 String caps, int level, 424 int frequency, long tsf, int distCm, int distSdCm, int channelWidth, 425 int centerFreq0, int centerFreq1, boolean is80211McRTTResponder) { 426 this(Ssid, BSSID, hessid, anqpDomainId, caps, level, frequency, tsf, distCm, 427 distSdCm, channelWidth, centerFreq0, centerFreq1, is80211McRTTResponder); 428 this.wifiSsid = wifiSsid; 429 } 430 431 /** copy constructor {@hide} */ ScanResult(ScanResult source)432 public ScanResult(ScanResult source) { 433 if (source != null) { 434 wifiSsid = source.wifiSsid; 435 SSID = source.SSID; 436 BSSID = source.BSSID; 437 hessid = source.hessid; 438 anqpDomainId = source.anqpDomainId; 439 informationElements = source.informationElements; 440 anqpElements = source.anqpElements; 441 capabilities = source.capabilities; 442 level = source.level; 443 frequency = source.frequency; 444 channelWidth = source.channelWidth; 445 centerFreq0 = source.centerFreq0; 446 centerFreq1 = source.centerFreq1; 447 timestamp = source.timestamp; 448 distanceCm = source.distanceCm; 449 distanceSdCm = source.distanceSdCm; 450 seen = source.seen; 451 untrusted = source.untrusted; 452 numConnection = source.numConnection; 453 numUsage = source.numUsage; 454 numIpConfigFailures = source.numIpConfigFailures; 455 isAutoJoinCandidate = source.isAutoJoinCandidate; 456 venueName = source.venueName; 457 operatorFriendlyName = source.operatorFriendlyName; 458 flags = source.flags; 459 } 460 } 461 462 /** empty scan result 463 * 464 * {@hide} 465 * */ ScanResult()466 public ScanResult() { 467 } 468 469 @Override toString()470 public String toString() { 471 StringBuffer sb = new StringBuffer(); 472 String none = "<none>"; 473 474 sb.append("SSID: "). 475 append(wifiSsid == null ? WifiSsid.NONE : wifiSsid). 476 append(", BSSID: "). 477 append(BSSID == null ? none : BSSID). 478 append(", capabilities: "). 479 append(capabilities == null ? none : capabilities). 480 append(", level: "). 481 append(level). 482 append(", frequency: "). 483 append(frequency). 484 append(", timestamp: "). 485 append(timestamp); 486 487 sb.append(", distance: ").append((distanceCm != UNSPECIFIED ? distanceCm : "?")). 488 append("(cm)"); 489 sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")). 490 append("(cm)"); 491 492 sb.append(", passpoint: "); 493 sb.append(((flags & FLAG_PASSPOINT_NETWORK) != 0) ? "yes" : "no"); 494 sb.append(", ChannelBandwidth: ").append(channelWidth); 495 sb.append(", centerFreq0: ").append(centerFreq0); 496 sb.append(", centerFreq1: ").append(centerFreq1); 497 sb.append(", 80211mcResponder: "); 498 sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported"); 499 return sb.toString(); 500 } 501 502 /** Implement the Parcelable interface {@hide} */ describeContents()503 public int describeContents() { 504 return 0; 505 } 506 507 /** Implement the Parcelable interface {@hide} */ writeToParcel(Parcel dest, int flags)508 public void writeToParcel(Parcel dest, int flags) { 509 if (wifiSsid != null) { 510 dest.writeInt(1); 511 wifiSsid.writeToParcel(dest, flags); 512 } else { 513 dest.writeInt(0); 514 } 515 dest.writeString(SSID); 516 dest.writeString(BSSID); 517 dest.writeLong(hessid); 518 dest.writeInt(anqpDomainId); 519 dest.writeString(capabilities); 520 dest.writeInt(level); 521 dest.writeInt(frequency); 522 dest.writeLong(timestamp); 523 dest.writeInt(distanceCm); 524 dest.writeInt(distanceSdCm); 525 dest.writeInt(channelWidth); 526 dest.writeInt(centerFreq0); 527 dest.writeInt(centerFreq1); 528 dest.writeLong(seen); 529 dest.writeInt(untrusted ? 1 : 0); 530 dest.writeInt(numConnection); 531 dest.writeInt(numUsage); 532 dest.writeInt(numIpConfigFailures); 533 dest.writeInt(isAutoJoinCandidate); 534 dest.writeString((venueName != null) ? venueName.toString() : ""); 535 dest.writeString((operatorFriendlyName != null) ? operatorFriendlyName.toString() : ""); 536 dest.writeLong(this.flags); 537 538 if (informationElements != null) { 539 dest.writeInt(informationElements.length); 540 for (int i = 0; i < informationElements.length; i++) { 541 dest.writeInt(informationElements[i].id); 542 dest.writeInt(informationElements[i].bytes.length); 543 dest.writeByteArray(informationElements[i].bytes); 544 } 545 } else { 546 dest.writeInt(0); 547 } 548 549 if (anqpLines != null) { 550 dest.writeInt(anqpLines.size()); 551 for (int i = 0; i < anqpLines.size(); i++) { 552 dest.writeString(anqpLines.get(i)); 553 } 554 } 555 else { 556 dest.writeInt(0); 557 } 558 if (anqpElements != null) { 559 dest.writeInt(anqpElements.length); 560 for (AnqpInformationElement element : anqpElements) { 561 dest.writeInt(element.getVendorId()); 562 dest.writeInt(element.getElementId()); 563 dest.writeInt(element.getPayload().length); 564 dest.writeByteArray(element.getPayload()); 565 } 566 } else { 567 dest.writeInt(0); 568 } 569 } 570 571 /** Implement the Parcelable interface {@hide} */ 572 public static final Creator<ScanResult> CREATOR = 573 new Creator<ScanResult>() { 574 public ScanResult createFromParcel(Parcel in) { 575 WifiSsid wifiSsid = null; 576 if (in.readInt() == 1) { 577 wifiSsid = WifiSsid.CREATOR.createFromParcel(in); 578 } 579 ScanResult sr = new ScanResult( 580 wifiSsid, 581 in.readString(), /* SSID */ 582 in.readString(), /* BSSID */ 583 in.readLong(), /* HESSID */ 584 in.readInt(), /* ANQP Domain ID */ 585 in.readString(), /* capabilities */ 586 in.readInt(), /* level */ 587 in.readInt(), /* frequency */ 588 in.readLong(), /* timestamp */ 589 in.readInt(), /* distanceCm */ 590 in.readInt(), /* distanceSdCm */ 591 in.readInt(), /* channelWidth */ 592 in.readInt(), /* centerFreq0 */ 593 in.readInt(), /* centerFreq1 */ 594 false /* rtt responder, 595 fixed with flags below */ 596 ); 597 598 sr.seen = in.readLong(); 599 sr.untrusted = in.readInt() != 0; 600 sr.numConnection = in.readInt(); 601 sr.numUsage = in.readInt(); 602 sr.numIpConfigFailures = in.readInt(); 603 sr.isAutoJoinCandidate = in.readInt(); 604 sr.venueName = in.readString(); 605 sr.operatorFriendlyName = in.readString(); 606 sr.flags = in.readLong(); 607 int n = in.readInt(); 608 if (n != 0) { 609 sr.informationElements = new InformationElement[n]; 610 for (int i = 0; i < n; i++) { 611 sr.informationElements[i] = new InformationElement(); 612 sr.informationElements[i].id = in.readInt(); 613 int len = in.readInt(); 614 sr.informationElements[i].bytes = new byte[len]; 615 in.readByteArray(sr.informationElements[i].bytes); 616 } 617 } 618 619 n = in.readInt(); 620 if (n != 0) { 621 sr.anqpLines = new ArrayList<String>(); 622 for (int i = 0; i < n; i++) { 623 sr.anqpLines.add(in.readString()); 624 } 625 } 626 n = in.readInt(); 627 if (n != 0) { 628 sr.anqpElements = new AnqpInformationElement[n]; 629 for (int i = 0; i < n; i++) { 630 int vendorId = in.readInt(); 631 int elementId = in.readInt(); 632 int len = in.readInt(); 633 byte[] payload = new byte[len]; 634 in.readByteArray(payload); 635 sr.anqpElements[i] = 636 new AnqpInformationElement(vendorId, elementId, payload); 637 } 638 } 639 return sr; 640 } 641 642 public ScanResult[] newArray(int size) { 643 return new ScanResult[size]; 644 } 645 }; 646 } 647