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
17package android.net.wifi;
18
19import android.os.Parcelable;
20import android.os.Parcel;
21import android.net.NetworkInfo.DetailedState;
22import android.net.NetworkUtils;
23import android.text.TextUtils;
24
25import java.net.InetAddress;
26import java.net.Inet4Address;
27import java.net.UnknownHostException;
28import java.util.EnumMap;
29import 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 */
35public 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     * 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 */
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 */
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     */
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 */
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     */
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 */
251    public WifiSsid getWifiSsid() {
252        return mWifiSsid;
253    }
254
255    /** @hide */
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     */
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     */
278    public int getRssi() {
279        return mRssi;
280    }
281
282    /** @hide */
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     */
296    public int getLinkSpeed() {
297        return mLinkSpeed;
298    }
299
300    /** @hide */
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     */
310    public int getFrequency() {
311        return mFrequency;
312    }
313
314    /** @hide */
315    public void setFrequency(int frequency) {
316        this.mFrequency = frequency;
317    }
318
319    /**
320     * @hide
321     * TODO: makes real freq boundaries
322     */
323    public boolean is24GHz() {
324        return ScanResult.is24GHz(mFrequency);
325    }
326
327    /**
328     * @hide
329     * TODO: makes real freq boundaries
330     */
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     */
340    public void setMacAddress(String macAddress) {
341        this.mMacAddress = macAddress;
342    }
343
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     */
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     */
364    public void setMeteredHint(boolean meteredHint) {
365        mMeteredHint = meteredHint;
366    }
367
368    /** {@hide} */
369    public boolean getMeteredHint() {
370        return mMeteredHint;
371    }
372
373    /** {@hide} */
374    public void setEphemeral(boolean ephemeral) {
375        mEphemeral = ephemeral;
376    }
377
378    /** {@hide} */
379    public boolean isEphemeral() {
380        return mEphemeral;
381    }
382
383    /** @hide */
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     */
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     */
403    public SupplicantState getSupplicantState() {
404        return mSupplicantState;
405    }
406
407    /** @hide */
408    public void setSupplicantState(SupplicantState state) {
409        mSupplicantState = state;
410    }
411
412    /** @hide */
413    public void setInetAddress(InetAddress address) {
414        mIpAddress = address;
415    }
416
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     */
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     */
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     */
449    void setSupplicantState(String stateName) {
450        mSupplicantState = valueOf(stateName);
451    }
452
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} */
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
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} */
495    public int describeContents() {
496        return 0;
497    }
498
499    /** Implement the Parcelable interface {@hide} */
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