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