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                 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