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 &lt;unknown ssid&gt; 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                 return mWifiSsid.getHexString();
324             }
325         }
326         return WifiSsid.NONE;
327     }
328 
329     /** @hide */
getWifiSsid()330     public WifiSsid getWifiSsid() {
331         return mWifiSsid;
332     }
333 
334     /** @hide */
setBSSID(String BSSID)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      */
getBSSID()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      */
getRssi()357     public int getRssi() {
358         return mRssi;
359     }
360 
361     /** @hide */
setRssi(int rssi)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      */
getLinkSpeed()375     public int getLinkSpeed() {
376         return mLinkSpeed;
377     }
378 
379     /** @hide */
setLinkSpeed(int linkSpeed)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      */
getFrequency()389     public int getFrequency() {
390         return mFrequency;
391     }
392 
393     /** @hide */
setFrequency(int frequency)394     public void setFrequency(int frequency) {
395         this.mFrequency = frequency;
396     }
397 
398     /**
399      * @hide
400      * TODO: makes real freq boundaries
401      */
is24GHz()402     public boolean is24GHz() {
403         return ScanResult.is24GHz(mFrequency);
404     }
405 
406     /**
407      * @hide
408      * TODO: makes real freq boundaries
409      */
is5GHz()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      */
setMacAddress(String macAddress)419     public void setMacAddress(String macAddress) {
420         this.mMacAddress = macAddress;
421     }
422 
getMacAddress()423     public String getMacAddress() {
424         return mMacAddress;
425     }
426 
427     /** {@hide} */
setMeteredHint(boolean meteredHint)428     public void setMeteredHint(boolean meteredHint) {
429         mMeteredHint = meteredHint;
430     }
431 
432     /** {@hide} */
getMeteredHint()433     public boolean getMeteredHint() {
434         return mMeteredHint;
435     }
436 
437     /** {@hide} */
setEphemeral(boolean ephemeral)438     public void setEphemeral(boolean ephemeral) {
439         mEphemeral = ephemeral;
440     }
441 
442     /** {@hide} */
isEphemeral()443     public boolean isEphemeral() {
444         return mEphemeral;
445     }
446 
447     /** @hide */
setNetworkId(int id)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      */
getNetworkId()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      */
getSupplicantState()467     public SupplicantState getSupplicantState() {
468         return mSupplicantState;
469     }
470 
471     /** @hide */
setSupplicantState(SupplicantState state)472     public void setSupplicantState(SupplicantState state) {
473         mSupplicantState = state;
474     }
475 
476     /** @hide */
setInetAddress(InetAddress address)477     public void setInetAddress(InetAddress address) {
478         mIpAddress = address;
479     }
480 
getIpAddress()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      */
getHiddenSSID()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      */
getDetailedStateOf(SupplicantState suppState)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      */
setSupplicantState(String stateName)513     void setSupplicantState(String stateName) {
514         mSupplicantState = valueOf(stateName);
515     }
516 
valueOf(String stateName)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} */
removeDoubleQuotes(String string)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
toString()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} */
describeContents()559     public int describeContents() {
560         return 0;
561     }
562 
563     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)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