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 { 54 stateMap.put(SupplicantState.DISCONNECTED, DetailedState.DISCONNECTED); 55 stateMap.put(SupplicantState.INTERFACE_DISABLED, DetailedState.DISCONNECTED); 56 stateMap.put(SupplicantState.INACTIVE, DetailedState.IDLE); 57 stateMap.put(SupplicantState.SCANNING, DetailedState.SCANNING); 58 stateMap.put(SupplicantState.AUTHENTICATING, DetailedState.CONNECTING); 59 stateMap.put(SupplicantState.ASSOCIATING, DetailedState.CONNECTING); 60 stateMap.put(SupplicantState.ASSOCIATED, DetailedState.CONNECTING); 61 stateMap.put(SupplicantState.FOUR_WAY_HANDSHAKE, DetailedState.AUTHENTICATING); 62 stateMap.put(SupplicantState.GROUP_HANDSHAKE, DetailedState.AUTHENTICATING); 63 stateMap.put(SupplicantState.COMPLETED, DetailedState.OBTAINING_IPADDR); 64 stateMap.put(SupplicantState.DORMANT, DetailedState.DISCONNECTED); 65 stateMap.put(SupplicantState.UNINITIALIZED, DetailedState.IDLE); 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 317 public String getSSID() { 318 if (mWifiSsid != null) { 319 String unicode = mWifiSsid.toString(); 320 if (!TextUtils.isEmpty(unicode)) { 321 return "\"" + unicode + "\""; 322 } else { 323 return mWifiSsid.getHexString(); 324 } 325 } 326 return WifiSsid.NONE; 327 } 328 329 /** @hide */ 330 public WifiSsid getWifiSsid() { 331 return mWifiSsid; 332 } 333 334 /** @hide */ 335 public void setBSSID(String BSSID) { 336 mBSSID = BSSID; 337 } 338 339 /** 340 * Return the basic service set identifier (BSSID) of the current access point. 341 * The BSSID may be {@code null} if there is no network currently connected. 342 * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX} 343 */ 344 public String getBSSID() { 345 return mBSSID; 346 } 347 348 /** 349 * Returns the received signal strength indicator of the current 802.11 350 * network, in dBm. 351 * 352 * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into 353 * an absolute signal level which can be displayed to a user. 354 * 355 * @return the RSSI. 356 */ 357 public int getRssi() { 358 return mRssi; 359 } 360 361 /** @hide */ 362 public void setRssi(int rssi) { 363 if (rssi < INVALID_RSSI) 364 rssi = INVALID_RSSI; 365 if (rssi > MAX_RSSI) 366 rssi = MAX_RSSI; 367 mRssi = rssi; 368 } 369 370 /** 371 * Returns the current link speed in {@link #LINK_SPEED_UNITS}. 372 * @return the link speed. 373 * @see #LINK_SPEED_UNITS 374 */ 375 public int getLinkSpeed() { 376 return mLinkSpeed; 377 } 378 379 /** @hide */ 380 public void setLinkSpeed(int linkSpeed) { 381 this.mLinkSpeed = linkSpeed; 382 } 383 384 /** 385 * Returns the current frequency in {@link #FREQUENCY_UNITS}. 386 * @return the frequency. 387 * @see #FREQUENCY_UNITS 388 */ 389 public int getFrequency() { 390 return mFrequency; 391 } 392 393 /** @hide */ 394 public void setFrequency(int frequency) { 395 this.mFrequency = frequency; 396 } 397 398 /** 399 * @hide 400 * TODO: makes real freq boundaries 401 */ 402 public boolean is24GHz() { 403 return ScanResult.is24GHz(mFrequency); 404 } 405 406 /** 407 * @hide 408 * TODO: makes real freq boundaries 409 */ 410 public boolean is5GHz() { 411 return ScanResult.is5GHz(mFrequency); 412 } 413 414 /** 415 * Record the MAC address of the WLAN interface 416 * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form 417 * @hide 418 */ 419 public void setMacAddress(String macAddress) { 420 this.mMacAddress = macAddress; 421 } 422 423 public String getMacAddress() { 424 return mMacAddress; 425 } 426 427 /** {@hide} */ 428 public void setMeteredHint(boolean meteredHint) { 429 mMeteredHint = meteredHint; 430 } 431 432 /** {@hide} */ 433 public boolean getMeteredHint() { 434 return mMeteredHint; 435 } 436 437 /** {@hide} */ 438 public void setEphemeral(boolean ephemeral) { 439 mEphemeral = ephemeral; 440 } 441 442 /** {@hide} */ 443 public boolean isEphemeral() { 444 return mEphemeral; 445 } 446 447 /** @hide */ 448 public void setNetworkId(int id) { 449 mNetworkId = id; 450 } 451 452 /** 453 * Each configured network has a unique small integer ID, used to identify 454 * the network when performing operations on the supplicant. This method 455 * returns the ID for the currently connected network. 456 * @return the network ID, or -1 if there is no currently connected network 457 */ 458 public int getNetworkId() { 459 return mNetworkId; 460 } 461 462 /** 463 * Return the detailed state of the supplicant's negotiation with an 464 * access point, in the form of a {@link SupplicantState SupplicantState} object. 465 * @return the current {@link SupplicantState SupplicantState} 466 */ 467 public SupplicantState getSupplicantState() { 468 return mSupplicantState; 469 } 470 471 /** @hide */ 472 public void setSupplicantState(SupplicantState state) { 473 mSupplicantState = state; 474 } 475 476 /** @hide */ 477 public void setInetAddress(InetAddress address) { 478 mIpAddress = address; 479 } 480 481 public int getIpAddress() { 482 int result = 0; 483 if (mIpAddress instanceof Inet4Address) { 484 result = NetworkUtils.inetAddressToInt((Inet4Address)mIpAddress); 485 } 486 return result; 487 } 488 489 /** 490 * @return {@code true} if this network does not broadcast its SSID, so an 491 * SSID-specific probe request must be used for scans. 492 */ 493 public boolean getHiddenSSID() { 494 if (mWifiSsid == null) return false; 495 return mWifiSsid.isHidden(); 496 } 497 498 /** 499 * Map a supplicant state into a fine-grained network connectivity state. 500 * @param suppState the supplicant state 501 * @return the corresponding {@link DetailedState} 502 */ 503 public static DetailedState getDetailedStateOf(SupplicantState suppState) { 504 return stateMap.get(suppState); 505 } 506 507 /** 508 * Set the <code>SupplicantState</code> from the string name 509 * of the state. 510 * @param stateName the name of the state, as a <code>String</code> returned 511 * in an event sent by {@code wpa_supplicant}. 512 */ 513 void setSupplicantState(String stateName) { 514 mSupplicantState = valueOf(stateName); 515 } 516 517 static SupplicantState valueOf(String stateName) { 518 if ("4WAY_HANDSHAKE".equalsIgnoreCase(stateName)) 519 return SupplicantState.FOUR_WAY_HANDSHAKE; 520 else { 521 try { 522 return SupplicantState.valueOf(stateName.toUpperCase(Locale.ROOT)); 523 } catch (IllegalArgumentException e) { 524 return SupplicantState.INVALID; 525 } 526 } 527 } 528 529 /** {@hide} */ 530 public static String removeDoubleQuotes(String string) { 531 if (string == null) return null; 532 final int length = string.length(); 533 if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { 534 return string.substring(1, length - 1); 535 } 536 return string; 537 } 538 539 @Override 540 public String toString() { 541 StringBuffer sb = new StringBuffer(); 542 String none = "<none>"; 543 544 sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid). 545 append(", BSSID: ").append(mBSSID == null ? none : mBSSID). 546 append(", MAC: ").append(mMacAddress == null ? none : mMacAddress). 547 append(", Supplicant state: "). 548 append(mSupplicantState == null ? none : mSupplicantState). 549 append(", RSSI: ").append(mRssi). 550 append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS). 551 append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS). 552 append(", Net ID: ").append(mNetworkId). 553 append(", Metered hint: ").append(mMeteredHint). 554 append(", score: ").append(Integer.toString(score)); 555 return sb.toString(); 556 } 557 558 /** Implement the Parcelable interface {@hide} */ 559 public int describeContents() { 560 return 0; 561 } 562 563 /** Implement the Parcelable interface {@hide} */ 564 public void writeToParcel(Parcel dest, int flags) { 565 dest.writeInt(mNetworkId); 566 dest.writeInt(mRssi); 567 dest.writeInt(mLinkSpeed); 568 dest.writeInt(mFrequency); 569 if (mIpAddress != null) { 570 dest.writeByte((byte)1); 571 dest.writeByteArray(mIpAddress.getAddress()); 572 } else { 573 dest.writeByte((byte)0); 574 } 575 if (mWifiSsid != null) { 576 dest.writeInt(1); 577 mWifiSsid.writeToParcel(dest, flags); 578 } else { 579 dest.writeInt(0); 580 } 581 dest.writeString(mBSSID); 582 dest.writeString(mMacAddress); 583 dest.writeInt(mMeteredHint ? 1 : 0); 584 dest.writeInt(mEphemeral ? 1 : 0); 585 dest.writeInt(score); 586 dest.writeDouble(txSuccessRate); 587 dest.writeDouble(txRetriesRate); 588 dest.writeDouble(txBadRate); 589 dest.writeDouble(rxSuccessRate); 590 dest.writeInt(badRssiCount); 591 dest.writeInt(lowRssiCount); 592 mSupplicantState.writeToParcel(dest, flags); 593 } 594 595 /** Implement the Parcelable interface {@hide} */ 596 public static final Creator<WifiInfo> CREATOR = 597 new Creator<WifiInfo>() { 598 public WifiInfo createFromParcel(Parcel in) { 599 WifiInfo info = new WifiInfo(); 600 info.setNetworkId(in.readInt()); 601 info.setRssi(in.readInt()); 602 info.setLinkSpeed(in.readInt()); 603 info.setFrequency(in.readInt()); 604 if (in.readByte() == 1) { 605 try { 606 info.setInetAddress(InetAddress.getByAddress(in.createByteArray())); 607 } catch (UnknownHostException e) {} 608 } 609 if (in.readInt() == 1) { 610 info.mWifiSsid = WifiSsid.CREATOR.createFromParcel(in); 611 } 612 info.mBSSID = in.readString(); 613 info.mMacAddress = in.readString(); 614 info.mMeteredHint = in.readInt() != 0; 615 info.mEphemeral = in.readInt() != 0; 616 info.score = in.readInt(); 617 info.txSuccessRate = in.readDouble(); 618 info.txRetriesRate = in.readDouble(); 619 info.txBadRate = in.readDouble(); 620 info.rxSuccessRate = in.readDouble(); 621 info.badRssiCount = in.readInt(); 622 info.lowRssiCount = in.readInt(); 623 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); 624 return info; 625 } 626 627 public WifiInfo[] newArray(int size) { 628 return new WifiInfo[size]; 629 } 630 }; 631 } 632