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.Parcelable; 20 import android.os.Parcel; 21 import android.net.NetworkInfo.DetailedState; 22 import android.net.NetworkUtils; 23 import android.text.TextUtils; 24 25 import java.lang.Math; 26 import java.net.InetAddress; 27 import java.net.Inet4Address; 28 import java.net.UnknownHostException; 29 import java.util.EnumMap; 30 import java.util.Locale; 31 32 /** 33 * Describes the state of any Wifi connection that is active or 34 * is in the process of being set up. 35 */ 36 public class WifiInfo implements Parcelable { 37 private static final String TAG = "WifiInfo"; 38 /** 39 * This is the map described in the Javadoc comment above. The positions 40 * of the elements of the array must correspond to the ordinal values 41 * of <code>DetailedState</code>. 42 */ 43 private static final EnumMap<SupplicantState, DetailedState> stateMap = 44 new EnumMap<SupplicantState, DetailedState>(SupplicantState.class); 45 46 /** 47 * Default MAC address reported to a client that does not have the 48 * android.permission.LOCAL_MAC_ADDRESS permission. 49 * 50 * @hide 51 */ 52 public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; 53 54 static { 55 stateMap.put(SupplicantState.DISCONNECTED, DetailedState.DISCONNECTED); 56 stateMap.put(SupplicantState.INTERFACE_DISABLED, DetailedState.DISCONNECTED); 57 stateMap.put(SupplicantState.INACTIVE, DetailedState.IDLE); 58 stateMap.put(SupplicantState.SCANNING, DetailedState.SCANNING); 59 stateMap.put(SupplicantState.AUTHENTICATING, DetailedState.CONNECTING); 60 stateMap.put(SupplicantState.ASSOCIATING, DetailedState.CONNECTING); 61 stateMap.put(SupplicantState.ASSOCIATED, DetailedState.CONNECTING); 62 stateMap.put(SupplicantState.FOUR_WAY_HANDSHAKE, DetailedState.AUTHENTICATING); 63 stateMap.put(SupplicantState.GROUP_HANDSHAKE, DetailedState.AUTHENTICATING); 64 stateMap.put(SupplicantState.COMPLETED, DetailedState.OBTAINING_IPADDR); 65 stateMap.put(SupplicantState.DORMANT, DetailedState.DISCONNECTED); 66 stateMap.put(SupplicantState.UNINITIALIZED, DetailedState.IDLE); 67 stateMap.put(SupplicantState.INVALID, DetailedState.FAILED); 68 } 69 70 private SupplicantState mSupplicantState; 71 private String mBSSID; 72 private WifiSsid mWifiSsid; 73 private int mNetworkId; 74 75 /** @hide **/ 76 public static final int INVALID_RSSI = -127; 77 78 /** @hide **/ 79 public static final int MIN_RSSI = -126; 80 81 /** @hide **/ 82 public static final int MAX_RSSI = 200; 83 84 85 /** 86 * Received Signal Strength Indicator 87 */ 88 private int mRssi; 89 90 /** 91 * Link speed in Mbps 92 */ 93 public static final String LINK_SPEED_UNITS = "Mbps"; 94 private int mLinkSpeed; 95 96 /** 97 * Frequency in MHz 98 */ 99 public static final String FREQUENCY_UNITS = "MHz"; 100 private int mFrequency; 101 102 private InetAddress mIpAddress; 103 private String mMacAddress = DEFAULT_MAC_ADDRESS; 104 105 private boolean mEphemeral; 106 107 /** 108 * Running total count of lost (not ACKed) transmitted unicast data packets. 109 * @hide 110 */ 111 public long txBad; 112 /** 113 * Running total count of transmitted unicast data retry packets. 114 * @hide 115 */ 116 public long txRetries; 117 /** 118 * Running total count of successfully transmitted (ACKed) unicast data packets. 119 * @hide 120 */ 121 public long txSuccess; 122 /** 123 * Running total count of received unicast data packets. 124 * @hide 125 */ 126 public long rxSuccess; 127 128 /** 129 * Average rate of lost transmitted packets, in units of packets per 5 seconds. 130 * @hide 131 */ 132 public double txBadRate; 133 /** 134 * Average rate of transmitted retry packets, in units of packets per 5 seconds. 135 * @hide 136 */ 137 public double txRetriesRate; 138 /** 139 * Average rate of successfully transmitted unicast packets, in units of packets per 5 seconds. 140 * @hide 141 */ 142 public double txSuccessRate; 143 /** 144 * Average rate of received unicast data packets, in units of packets per 5 seconds. 145 * @hide 146 */ 147 public double rxSuccessRate; 148 149 private static final long RESET_TIME_STAMP = Long.MIN_VALUE; 150 private static final long FILTER_TIME_CONSTANT = 3000; 151 /** 152 * This factor is used to adjust the rate output under the new algorithm 153 * such that the result is comparable to the previous algorithm. 154 * This actually converts from unit 'packets per second' to 'packets per 5 seconds'. 155 */ 156 private static final long OUTPUT_SCALE_FACTOR = 5; 157 private long mLastPacketCountUpdateTimeStamp; 158 159 /** 160 * @hide 161 */ 162 public int badRssiCount; 163 164 /** 165 * @hide 166 */ 167 public int linkStuckCount; 168 169 /** 170 * @hide 171 */ 172 public int lowRssiCount; 173 174 /** 175 * @hide 176 */ 177 public int score; 178 179 /** 180 * @hide 181 */ 182 public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) { 183 if (stats != null) { 184 long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo; 185 long txretries = stats.retries_be + stats.retries_bk 186 + stats.retries_vi + stats.retries_vo; 187 long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo; 188 long txbad = stats.lostmpdu_be + stats.lostmpdu_bk 189 + stats.lostmpdu_vi + stats.lostmpdu_vo; 190 191 if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP 192 && mLastPacketCountUpdateTimeStamp < timeStamp 193 && txBad <= txbad 194 && txSuccess <= txgood 195 && rxSuccess <= rxgood 196 && txRetries <= txretries) { 197 long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp; 198 double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT); 199 double currentSampleWeight = 1.0 - lastSampleWeight; 200 201 txBadRate = txBadRate * lastSampleWeight 202 + (txbad - txBad) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta 203 * currentSampleWeight; 204 txSuccessRate = txSuccessRate * lastSampleWeight 205 + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta 206 * currentSampleWeight; 207 rxSuccessRate = rxSuccessRate * lastSampleWeight 208 + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta 209 * currentSampleWeight; 210 txRetriesRate = txRetriesRate * lastSampleWeight 211 + (txretries - txRetries) * OUTPUT_SCALE_FACTOR * 1000/ timeDelta 212 * currentSampleWeight; 213 } else { 214 txBadRate = 0; 215 txSuccessRate = 0; 216 rxSuccessRate = 0; 217 txRetriesRate = 0; 218 } 219 txBad = txbad; 220 txSuccess = txgood; 221 rxSuccess = rxgood; 222 txRetries = txretries; 223 mLastPacketCountUpdateTimeStamp = timeStamp; 224 } else { 225 txBad = 0; 226 txSuccess = 0; 227 rxSuccess = 0; 228 txRetries = 0; 229 txBadRate = 0; 230 txSuccessRate = 0; 231 rxSuccessRate = 0; 232 txRetriesRate = 0; 233 mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; 234 } 235 } 236 237 238 /** 239 * This function is less powerful and used if the WifiLinkLayerStats API is not implemented 240 * at the Wifi HAL 241 * @hide 242 */ 243 public void updatePacketRates(long txPackets, long rxPackets) { 244 //paranoia 245 txBad = 0; 246 txRetries = 0; 247 txBadRate = 0; 248 txRetriesRate = 0; 249 if (txSuccess <= txPackets && rxSuccess <= rxPackets) { 250 txSuccessRate = (txSuccessRate * 0.5) 251 + ((double) (txPackets - txSuccess) * 0.5); 252 rxSuccessRate = (rxSuccessRate * 0.5) 253 + ((double) (rxPackets - rxSuccess) * 0.5); 254 } else { 255 txBadRate = 0; 256 txRetriesRate = 0; 257 } 258 txSuccess = txPackets; 259 rxSuccess = rxPackets; 260 } 261 262 /** 263 * Flag indicating that AP has hinted that upstream connection is metered, 264 * and sensitive to heavy data transfers. 265 */ 266 private boolean mMeteredHint; 267 268 /** @hide */ 269 public WifiInfo() { 270 mWifiSsid = null; 271 mBSSID = null; 272 mNetworkId = -1; 273 mSupplicantState = SupplicantState.UNINITIALIZED; 274 mRssi = INVALID_RSSI; 275 mLinkSpeed = -1; 276 mFrequency = -1; 277 mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; 278 } 279 280 /** @hide */ 281 public void reset() { 282 setInetAddress(null); 283 setBSSID(null); 284 setSSID(null); 285 setNetworkId(-1); 286 setRssi(INVALID_RSSI); 287 setLinkSpeed(-1); 288 setFrequency(-1); 289 setMeteredHint(false); 290 setEphemeral(false); 291 txBad = 0; 292 txSuccess = 0; 293 rxSuccess = 0; 294 txRetries = 0; 295 txBadRate = 0; 296 txSuccessRate = 0; 297 rxSuccessRate = 0; 298 txRetriesRate = 0; 299 lowRssiCount = 0; 300 badRssiCount = 0; 301 linkStuckCount = 0; 302 score = 0; 303 mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; 304 } 305 306 /** 307 * Copy constructor 308 * @hide 309 */ 310 public WifiInfo(WifiInfo source) { 311 if (source != null) { 312 mSupplicantState = source.mSupplicantState; 313 mBSSID = source.mBSSID; 314 mWifiSsid = source.mWifiSsid; 315 mNetworkId = source.mNetworkId; 316 mRssi = source.mRssi; 317 mLinkSpeed = source.mLinkSpeed; 318 mFrequency = source.mFrequency; 319 mIpAddress = source.mIpAddress; 320 mMacAddress = source.mMacAddress; 321 mMeteredHint = source.mMeteredHint; 322 mEphemeral = source.mEphemeral; 323 txBad = source.txBad; 324 txRetries = source.txRetries; 325 txSuccess = source.txSuccess; 326 rxSuccess = source.rxSuccess; 327 txBadRate = source.txBadRate; 328 txRetriesRate = source.txRetriesRate; 329 txSuccessRate = source.txSuccessRate; 330 rxSuccessRate = source.rxSuccessRate; 331 mLastPacketCountUpdateTimeStamp = 332 source.mLastPacketCountUpdateTimeStamp; 333 score = source.score; 334 badRssiCount = source.badRssiCount; 335 lowRssiCount = source.lowRssiCount; 336 linkStuckCount = source.linkStuckCount; 337 } 338 } 339 340 /** @hide */ 341 public void setSSID(WifiSsid wifiSsid) { 342 mWifiSsid = wifiSsid; 343 } 344 345 /** 346 * Returns the service set identifier (SSID) of the current 802.11 network. 347 * If the SSID can be decoded as UTF-8, it will be returned surrounded by double 348 * quotation marks. Otherwise, it is returned as a string of hex digits. The 349 * SSID may be <unknown ssid> if there is no network currently connected, 350 * or if the caller has insufficient permissions to access the SSID. 351 * @return the SSID 352 */ 353 public String getSSID() { 354 if (mWifiSsid != null) { 355 String unicode = mWifiSsid.toString(); 356 if (!TextUtils.isEmpty(unicode)) { 357 return "\"" + unicode + "\""; 358 } else { 359 String hex = mWifiSsid.getHexString(); 360 return (hex != null) ? hex : WifiSsid.NONE; 361 } 362 } 363 return WifiSsid.NONE; 364 } 365 366 /** @hide */ 367 public WifiSsid getWifiSsid() { 368 return mWifiSsid; 369 } 370 371 /** @hide */ 372 public void setBSSID(String BSSID) { 373 mBSSID = BSSID; 374 } 375 376 /** 377 * Return the basic service set identifier (BSSID) of the current access point. 378 * The BSSID may be {@code null} if there is no network currently connected. 379 * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX} 380 */ 381 public String getBSSID() { 382 return mBSSID; 383 } 384 385 /** 386 * Returns the received signal strength indicator of the current 802.11 387 * network, in dBm. 388 * 389 * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into 390 * an absolute signal level which can be displayed to a user. 391 * 392 * @return the RSSI. 393 */ 394 public int getRssi() { 395 return mRssi; 396 } 397 398 /** @hide */ 399 public void setRssi(int rssi) { 400 if (rssi < INVALID_RSSI) 401 rssi = INVALID_RSSI; 402 if (rssi > MAX_RSSI) 403 rssi = MAX_RSSI; 404 mRssi = rssi; 405 } 406 407 /** 408 * Returns the current link speed in {@link #LINK_SPEED_UNITS}. 409 * @return the link speed. 410 * @see #LINK_SPEED_UNITS 411 */ 412 public int getLinkSpeed() { 413 return mLinkSpeed; 414 } 415 416 /** @hide */ 417 public void setLinkSpeed(int linkSpeed) { 418 this.mLinkSpeed = linkSpeed; 419 } 420 421 /** 422 * Returns the current frequency in {@link #FREQUENCY_UNITS}. 423 * @return the frequency. 424 * @see #FREQUENCY_UNITS 425 */ 426 public int getFrequency() { 427 return mFrequency; 428 } 429 430 /** @hide */ 431 public void setFrequency(int frequency) { 432 this.mFrequency = frequency; 433 } 434 435 /** 436 * @hide 437 * TODO: makes real freq boundaries 438 */ 439 public boolean is24GHz() { 440 return ScanResult.is24GHz(mFrequency); 441 } 442 443 /** 444 * @hide 445 * TODO: makes real freq boundaries 446 */ 447 public boolean is5GHz() { 448 return ScanResult.is5GHz(mFrequency); 449 } 450 451 /** 452 * @hide 453 * This returns txSuccessRate in packets per second. 454 */ 455 public double getTxSuccessRatePps() { 456 return txSuccessRate / OUTPUT_SCALE_FACTOR; 457 } 458 459 /** 460 * @hide 461 * This returns rxSuccessRate in packets per second. 462 */ 463 public double getRxSuccessRatePps() { 464 return rxSuccessRate / OUTPUT_SCALE_FACTOR; 465 } 466 467 /** 468 * Record the MAC address of the WLAN interface 469 * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form 470 * @hide 471 */ 472 public void setMacAddress(String macAddress) { 473 this.mMacAddress = macAddress; 474 } 475 476 public String getMacAddress() { 477 return mMacAddress; 478 } 479 480 /** 481 * @return true if {@link #getMacAddress()} has a real MAC address. 482 * 483 * @hide 484 */ 485 public boolean hasRealMacAddress() { 486 return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress); 487 } 488 489 /** 490 * Indicates if we've dynamically detected this active network connection as 491 * being metered. 492 * 493 * @see WifiConfiguration#isMetered(WifiConfiguration, WifiInfo) 494 * @hide 495 */ 496 public void setMeteredHint(boolean meteredHint) { 497 mMeteredHint = meteredHint; 498 } 499 500 /** {@hide} */ 501 public boolean getMeteredHint() { 502 return mMeteredHint; 503 } 504 505 /** {@hide} */ 506 public void setEphemeral(boolean ephemeral) { 507 mEphemeral = ephemeral; 508 } 509 510 /** {@hide} */ 511 public boolean isEphemeral() { 512 return mEphemeral; 513 } 514 515 /** @hide */ 516 public void setNetworkId(int id) { 517 mNetworkId = id; 518 } 519 520 /** 521 * Each configured network has a unique small integer ID, used to identify 522 * the network when performing operations on the supplicant. This method 523 * returns the ID for the currently connected network. 524 * @return the network ID, or -1 if there is no currently connected network 525 */ 526 public int getNetworkId() { 527 return mNetworkId; 528 } 529 530 /** 531 * Return the detailed state of the supplicant's negotiation with an 532 * access point, in the form of a {@link SupplicantState SupplicantState} object. 533 * @return the current {@link SupplicantState SupplicantState} 534 */ 535 public SupplicantState getSupplicantState() { 536 return mSupplicantState; 537 } 538 539 /** @hide */ 540 public void setSupplicantState(SupplicantState state) { 541 mSupplicantState = state; 542 } 543 544 /** @hide */ 545 public void setInetAddress(InetAddress address) { 546 mIpAddress = address; 547 } 548 549 public int getIpAddress() { 550 int result = 0; 551 if (mIpAddress instanceof Inet4Address) { 552 result = NetworkUtils.inetAddressToInt((Inet4Address)mIpAddress); 553 } 554 return result; 555 } 556 557 /** 558 * @return {@code true} if this network does not broadcast its SSID, so an 559 * SSID-specific probe request must be used for scans. 560 */ 561 public boolean getHiddenSSID() { 562 if (mWifiSsid == null) return false; 563 return mWifiSsid.isHidden(); 564 } 565 566 /** 567 * Map a supplicant state into a fine-grained network connectivity state. 568 * @param suppState the supplicant state 569 * @return the corresponding {@link DetailedState} 570 */ 571 public static DetailedState getDetailedStateOf(SupplicantState suppState) { 572 return stateMap.get(suppState); 573 } 574 575 /** 576 * Set the <code>SupplicantState</code> from the string name 577 * of the state. 578 * @param stateName the name of the state, as a <code>String</code> returned 579 * in an event sent by {@code wpa_supplicant}. 580 */ 581 void setSupplicantState(String stateName) { 582 mSupplicantState = valueOf(stateName); 583 } 584 585 static SupplicantState valueOf(String stateName) { 586 if ("4WAY_HANDSHAKE".equalsIgnoreCase(stateName)) 587 return SupplicantState.FOUR_WAY_HANDSHAKE; 588 else { 589 try { 590 return SupplicantState.valueOf(stateName.toUpperCase(Locale.ROOT)); 591 } catch (IllegalArgumentException e) { 592 return SupplicantState.INVALID; 593 } 594 } 595 } 596 597 /** {@hide} */ 598 public static String removeDoubleQuotes(String string) { 599 if (string == null) return null; 600 final int length = string.length(); 601 if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { 602 return string.substring(1, length - 1); 603 } 604 return string; 605 } 606 607 @Override 608 public String toString() { 609 StringBuffer sb = new StringBuffer(); 610 String none = "<none>"; 611 612 sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid). 613 append(", BSSID: ").append(mBSSID == null ? none : mBSSID). 614 append(", MAC: ").append(mMacAddress == null ? none : mMacAddress). 615 append(", Supplicant state: "). 616 append(mSupplicantState == null ? none : mSupplicantState). 617 append(", RSSI: ").append(mRssi). 618 append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS). 619 append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS). 620 append(", Net ID: ").append(mNetworkId). 621 append(", Metered hint: ").append(mMeteredHint). 622 append(", score: ").append(Integer.toString(score)); 623 return sb.toString(); 624 } 625 626 /** Implement the Parcelable interface {@hide} */ 627 public int describeContents() { 628 return 0; 629 } 630 631 /** Implement the Parcelable interface {@hide} */ 632 public void writeToParcel(Parcel dest, int flags) { 633 dest.writeInt(mNetworkId); 634 dest.writeInt(mRssi); 635 dest.writeInt(mLinkSpeed); 636 dest.writeInt(mFrequency); 637 if (mIpAddress != null) { 638 dest.writeByte((byte)1); 639 dest.writeByteArray(mIpAddress.getAddress()); 640 } else { 641 dest.writeByte((byte)0); 642 } 643 if (mWifiSsid != null) { 644 dest.writeInt(1); 645 mWifiSsid.writeToParcel(dest, flags); 646 } else { 647 dest.writeInt(0); 648 } 649 dest.writeString(mBSSID); 650 dest.writeString(mMacAddress); 651 dest.writeInt(mMeteredHint ? 1 : 0); 652 dest.writeInt(mEphemeral ? 1 : 0); 653 dest.writeInt(score); 654 dest.writeDouble(txSuccessRate); 655 dest.writeDouble(txRetriesRate); 656 dest.writeDouble(txBadRate); 657 dest.writeDouble(rxSuccessRate); 658 dest.writeInt(badRssiCount); 659 dest.writeInt(lowRssiCount); 660 mSupplicantState.writeToParcel(dest, flags); 661 } 662 663 /** Implement the Parcelable interface {@hide} */ 664 public static final Creator<WifiInfo> CREATOR = 665 new Creator<WifiInfo>() { 666 public WifiInfo createFromParcel(Parcel in) { 667 WifiInfo info = new WifiInfo(); 668 info.setNetworkId(in.readInt()); 669 info.setRssi(in.readInt()); 670 info.setLinkSpeed(in.readInt()); 671 info.setFrequency(in.readInt()); 672 if (in.readByte() == 1) { 673 try { 674 info.setInetAddress(InetAddress.getByAddress(in.createByteArray())); 675 } catch (UnknownHostException e) {} 676 } 677 if (in.readInt() == 1) { 678 info.mWifiSsid = WifiSsid.CREATOR.createFromParcel(in); 679 } 680 info.mBSSID = in.readString(); 681 info.mMacAddress = in.readString(); 682 info.mMeteredHint = in.readInt() != 0; 683 info.mEphemeral = in.readInt() != 0; 684 info.score = in.readInt(); 685 info.txSuccessRate = in.readDouble(); 686 info.txRetriesRate = in.readDouble(); 687 info.txBadRate = in.readDouble(); 688 info.rxSuccessRate = in.readDouble(); 689 info.badRssiCount = in.readInt(); 690 info.lowRssiCount = in.readInt(); 691 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); 692 return info; 693 } 694 695 public WifiInfo[] newArray(int size) { 696 return new WifiInfo[size]; 697 } 698 }; 699 } 700