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