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