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      * Running total count of lost (not ACKed) transmitted unicast data packets.
108      * @hide
109      */
110     public long txBad;
111     /**
112      * Running total count of transmitted unicast data retry packets.
113      * @hide
114      */
115     public long txRetries;
116     /**
117      * Running total count of successfully transmitted (ACKed) unicast data packets.
118      * @hide
119      */
120     public long txSuccess;
121     /**
122      * Running total count of received unicast data packets.
123      * @hide
124      */
125     public long rxSuccess;
126 
127     /**
128      * Average rate of lost transmitted packets, in units of packets per second.
129      * @hide
130      */
131     public double txBadRate;
132     /**
133      * Average rate of transmitted retry packets, in units of packets per second.
134      * @hide
135      */
136     public double txRetriesRate;
137     /**
138      * Average rate of successfully transmitted unicast packets, in units of packets per second.
139      * @hide
140      */
141     public double txSuccessRate;
142     /**
143      * Average rate of received unicast data packets, in units of packets per second.
144      * @hide
145      */
146     public double rxSuccessRate;
147 
148     /**
149      * @hide
150      */
151     public int score;
152 
153     /**
154      * Flag indicating that AP has hinted that upstream connection is metered,
155      * and sensitive to heavy data transfers.
156      */
157     private boolean mMeteredHint;
158 
159     /** @hide */
WifiInfo()160     public WifiInfo() {
161         mWifiSsid = null;
162         mBSSID = null;
163         mNetworkId = -1;
164         mSupplicantState = SupplicantState.UNINITIALIZED;
165         mRssi = INVALID_RSSI;
166         mLinkSpeed = -1;
167         mFrequency = -1;
168     }
169 
170     /** @hide */
reset()171     public void reset() {
172         setInetAddress(null);
173         setBSSID(null);
174         setSSID(null);
175         setNetworkId(-1);
176         setRssi(INVALID_RSSI);
177         setLinkSpeed(-1);
178         setFrequency(-1);
179         setMeteredHint(false);
180         setEphemeral(false);
181         txBad = 0;
182         txSuccess = 0;
183         rxSuccess = 0;
184         txRetries = 0;
185         txBadRate = 0;
186         txSuccessRate = 0;
187         rxSuccessRate = 0;
188         txRetriesRate = 0;
189         score = 0;
190     }
191 
192     /**
193      * Copy constructor
194      * @hide
195      */
WifiInfo(WifiInfo source)196     public WifiInfo(WifiInfo source) {
197         if (source != null) {
198             mSupplicantState = source.mSupplicantState;
199             mBSSID = source.mBSSID;
200             mWifiSsid = source.mWifiSsid;
201             mNetworkId = source.mNetworkId;
202             mRssi = source.mRssi;
203             mLinkSpeed = source.mLinkSpeed;
204             mFrequency = source.mFrequency;
205             mIpAddress = source.mIpAddress;
206             mMacAddress = source.mMacAddress;
207             mMeteredHint = source.mMeteredHint;
208             mEphemeral = source.mEphemeral;
209             txBad = source.txBad;
210             txRetries = source.txRetries;
211             txSuccess = source.txSuccess;
212             rxSuccess = source.rxSuccess;
213             txBadRate = source.txBadRate;
214             txRetriesRate = source.txRetriesRate;
215             txSuccessRate = source.txSuccessRate;
216             rxSuccessRate = source.rxSuccessRate;
217             score = source.score;
218         }
219     }
220 
221     /** @hide */
setSSID(WifiSsid wifiSsid)222     public void setSSID(WifiSsid wifiSsid) {
223         mWifiSsid = wifiSsid;
224     }
225 
226     /**
227      * Returns the service set identifier (SSID) of the current 802.11 network.
228      * If the SSID can be decoded as UTF-8, it will be returned surrounded by double
229      * quotation marks. Otherwise, it is returned as a string of hex digits. The
230      * SSID may be &lt;unknown ssid&gt; if there is no network currently connected,
231      * or if the caller has insufficient permissions to access the SSID.
232      *
233      * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
234      * always returned the SSID with no quotes around it.
235      * @return the SSID
236      */
getSSID()237     public String getSSID() {
238         if (mWifiSsid != null) {
239             String unicode = mWifiSsid.toString();
240             if (!TextUtils.isEmpty(unicode)) {
241                 return "\"" + unicode + "\"";
242             } else {
243                 String hex = mWifiSsid.getHexString();
244                 return (hex != null) ? hex : WifiSsid.NONE;
245             }
246         }
247         return WifiSsid.NONE;
248     }
249 
250     /** @hide */
getWifiSsid()251     public WifiSsid getWifiSsid() {
252         return mWifiSsid;
253     }
254 
255     /** @hide */
setBSSID(String BSSID)256     public void setBSSID(String BSSID) {
257         mBSSID = BSSID;
258     }
259 
260     /**
261      * Return the basic service set identifier (BSSID) of the current access point.
262      * The BSSID may be {@code null} if there is no network currently connected.
263      * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}
264      */
getBSSID()265     public String getBSSID() {
266         return mBSSID;
267     }
268 
269     /**
270      * Returns the received signal strength indicator of the current 802.11
271      * network, in dBm.
272      *
273      * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into
274      * an absolute signal level which can be displayed to a user.
275      *
276      * @return the RSSI.
277      */
getRssi()278     public int getRssi() {
279         return mRssi;
280     }
281 
282     /** @hide */
setRssi(int rssi)283     public void setRssi(int rssi) {
284         if (rssi < INVALID_RSSI)
285             rssi = INVALID_RSSI;
286         if (rssi > MAX_RSSI)
287             rssi = MAX_RSSI;
288         mRssi = rssi;
289     }
290 
291     /**
292      * Returns the current link speed in {@link #LINK_SPEED_UNITS}.
293      * @return the link speed.
294      * @see #LINK_SPEED_UNITS
295      */
getLinkSpeed()296     public int getLinkSpeed() {
297         return mLinkSpeed;
298     }
299 
300     /** @hide */
setLinkSpeed(int linkSpeed)301     public void setLinkSpeed(int linkSpeed) {
302         this.mLinkSpeed = linkSpeed;
303     }
304 
305     /**
306      * Returns the current frequency in {@link #FREQUENCY_UNITS}.
307      * @return the frequency.
308      * @see #FREQUENCY_UNITS
309      */
getFrequency()310     public int getFrequency() {
311         return mFrequency;
312     }
313 
314     /** @hide */
setFrequency(int frequency)315     public void setFrequency(int frequency) {
316         this.mFrequency = frequency;
317     }
318 
319     /**
320      * @hide
321      * TODO: makes real freq boundaries
322      */
is24GHz()323     public boolean is24GHz() {
324         return ScanResult.is24GHz(mFrequency);
325     }
326 
327     /**
328      * @hide
329      * TODO: makes real freq boundaries
330      */
is5GHz()331     public boolean is5GHz() {
332         return ScanResult.is5GHz(mFrequency);
333     }
334 
335     /**
336      * Record the MAC address of the WLAN interface
337      * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
338      * @hide
339      */
setMacAddress(String macAddress)340     public void setMacAddress(String macAddress) {
341         this.mMacAddress = macAddress;
342     }
343 
getMacAddress()344     public String getMacAddress() {
345         return mMacAddress;
346     }
347 
348     /**
349      * @return true if {@link #getMacAddress()} has a real MAC address.
350      *
351      * @hide
352      */
hasRealMacAddress()353     public boolean hasRealMacAddress() {
354         return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress);
355     }
356 
357     /**
358      * Indicates if we've dynamically detected this active network connection as
359      * being metered.
360      *
361      * @see WifiConfiguration#isMetered(WifiConfiguration, WifiInfo)
362      * @hide
363      */
setMeteredHint(boolean meteredHint)364     public void setMeteredHint(boolean meteredHint) {
365         mMeteredHint = meteredHint;
366     }
367 
368     /** {@hide} */
getMeteredHint()369     public boolean getMeteredHint() {
370         return mMeteredHint;
371     }
372 
373     /** {@hide} */
setEphemeral(boolean ephemeral)374     public void setEphemeral(boolean ephemeral) {
375         mEphemeral = ephemeral;
376     }
377 
378     /** {@hide} */
isEphemeral()379     public boolean isEphemeral() {
380         return mEphemeral;
381     }
382 
383     /** @hide */
setNetworkId(int id)384     public void setNetworkId(int id) {
385         mNetworkId = id;
386     }
387 
388     /**
389      * Each configured network has a unique small integer ID, used to identify
390      * the network when performing operations on the supplicant. This method
391      * returns the ID for the currently connected network.
392      * @return the network ID, or -1 if there is no currently connected network
393      */
getNetworkId()394     public int getNetworkId() {
395         return mNetworkId;
396     }
397 
398     /**
399      * Return the detailed state of the supplicant's negotiation with an
400      * access point, in the form of a {@link SupplicantState SupplicantState} object.
401      * @return the current {@link SupplicantState SupplicantState}
402      */
getSupplicantState()403     public SupplicantState getSupplicantState() {
404         return mSupplicantState;
405     }
406 
407     /** @hide */
setSupplicantState(SupplicantState state)408     public void setSupplicantState(SupplicantState state) {
409         mSupplicantState = state;
410     }
411 
412     /** @hide */
setInetAddress(InetAddress address)413     public void setInetAddress(InetAddress address) {
414         mIpAddress = address;
415     }
416 
getIpAddress()417     public int getIpAddress() {
418         int result = 0;
419         if (mIpAddress instanceof Inet4Address) {
420             result = NetworkUtils.inetAddressToInt((Inet4Address)mIpAddress);
421         }
422         return result;
423     }
424 
425     /**
426      * @return {@code true} if this network does not broadcast its SSID, so an
427      * SSID-specific probe request must be used for scans.
428      */
getHiddenSSID()429     public boolean getHiddenSSID() {
430         if (mWifiSsid == null) return false;
431         return mWifiSsid.isHidden();
432     }
433 
434    /**
435      * Map a supplicant state into a fine-grained network connectivity state.
436      * @param suppState the supplicant state
437      * @return the corresponding {@link DetailedState}
438      */
getDetailedStateOf(SupplicantState suppState)439     public static DetailedState getDetailedStateOf(SupplicantState suppState) {
440         return stateMap.get(suppState);
441     }
442 
443     /**
444      * Set the <code>SupplicantState</code> from the string name
445      * of the state.
446      * @param stateName the name of the state, as a <code>String</code> returned
447      * in an event sent by {@code wpa_supplicant}.
448      */
setSupplicantState(String stateName)449     void setSupplicantState(String stateName) {
450         mSupplicantState = valueOf(stateName);
451     }
452 
valueOf(String stateName)453     static SupplicantState valueOf(String stateName) {
454         if ("4WAY_HANDSHAKE".equalsIgnoreCase(stateName))
455             return SupplicantState.FOUR_WAY_HANDSHAKE;
456         else {
457             try {
458                 return SupplicantState.valueOf(stateName.toUpperCase(Locale.ROOT));
459             } catch (IllegalArgumentException e) {
460                 return SupplicantState.INVALID;
461             }
462         }
463     }
464 
465     /** {@hide} */
removeDoubleQuotes(String string)466     public static String removeDoubleQuotes(String string) {
467         if (string == null) return null;
468         final int length = string.length();
469         if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
470             return string.substring(1, length - 1);
471         }
472         return string;
473     }
474 
475     @Override
toString()476     public String toString() {
477         StringBuffer sb = new StringBuffer();
478         String none = "<none>";
479 
480         sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid).
481             append(", BSSID: ").append(mBSSID == null ? none : mBSSID).
482             append(", MAC: ").append(mMacAddress == null ? none : mMacAddress).
483             append(", Supplicant state: ").
484             append(mSupplicantState == null ? none : mSupplicantState).
485             append(", RSSI: ").append(mRssi).
486             append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS).
487             append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS).
488             append(", Net ID: ").append(mNetworkId).
489             append(", Metered hint: ").append(mMeteredHint).
490             append(", score: ").append(Integer.toString(score));
491         return sb.toString();
492     }
493 
494     /** Implement the Parcelable interface {@hide} */
describeContents()495     public int describeContents() {
496         return 0;
497     }
498 
499     /** Implement the Parcelable interface {@hide} */
writeToParcel(Parcel dest, int flags)500     public void writeToParcel(Parcel dest, int flags) {
501         dest.writeInt(mNetworkId);
502         dest.writeInt(mRssi);
503         dest.writeInt(mLinkSpeed);
504         dest.writeInt(mFrequency);
505         if (mIpAddress != null) {
506             dest.writeByte((byte)1);
507             dest.writeByteArray(mIpAddress.getAddress());
508         } else {
509             dest.writeByte((byte)0);
510         }
511         if (mWifiSsid != null) {
512             dest.writeInt(1);
513             mWifiSsid.writeToParcel(dest, flags);
514         } else {
515             dest.writeInt(0);
516         }
517         dest.writeString(mBSSID);
518         dest.writeString(mMacAddress);
519         dest.writeInt(mMeteredHint ? 1 : 0);
520         dest.writeInt(mEphemeral ? 1 : 0);
521         dest.writeInt(score);
522         dest.writeDouble(txSuccessRate);
523         dest.writeDouble(txRetriesRate);
524         dest.writeDouble(txBadRate);
525         dest.writeDouble(rxSuccessRate);
526         mSupplicantState.writeToParcel(dest, flags);
527     }
528 
529     /** Implement the Parcelable interface {@hide} */
530     public static final Creator<WifiInfo> CREATOR =
531         new Creator<WifiInfo>() {
532             public WifiInfo createFromParcel(Parcel in) {
533                 WifiInfo info = new WifiInfo();
534                 info.setNetworkId(in.readInt());
535                 info.setRssi(in.readInt());
536                 info.setLinkSpeed(in.readInt());
537                 info.setFrequency(in.readInt());
538                 if (in.readByte() == 1) {
539                     try {
540                         info.setInetAddress(InetAddress.getByAddress(in.createByteArray()));
541                     } catch (UnknownHostException e) {}
542                 }
543                 if (in.readInt() == 1) {
544                     info.mWifiSsid = WifiSsid.CREATOR.createFromParcel(in);
545                 }
546                 info.mBSSID = in.readString();
547                 info.mMacAddress = in.readString();
548                 info.mMeteredHint = in.readInt() != 0;
549                 info.mEphemeral = in.readInt() != 0;
550                 info.score = in.readInt();
551                 info.txSuccessRate = in.readDouble();
552                 info.txRetriesRate = in.readDouble();
553                 info.txBadRate = in.readDouble();
554                 info.rxSuccessRate = in.readDouble();
555                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
556                 return info;
557             }
558 
559             public WifiInfo[] newArray(int size) {
560                 return new WifiInfo[size];
561             }
562         };
563 }
564