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     * @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     */
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     */
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 */
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 */
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     */
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 */
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     */
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 */
331    public WifiSsid getWifiSsid() {
332        return mWifiSsid;
333    }
334
335    /** @hide */
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     */
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     */
358    public int getRssi() {
359        return mRssi;
360    }
361
362    /** @hide */
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     */
376    public int getLinkSpeed() {
377        return mLinkSpeed;
378    }
379
380    /** @hide */
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     */
390    public int getFrequency() {
391        return mFrequency;
392    }
393
394    /** @hide */
395    public void setFrequency(int frequency) {
396        this.mFrequency = frequency;
397    }
398
399    /**
400     * @hide
401     * TODO: makes real freq boundaries
402     */
403    public boolean is24GHz() {
404        return ScanResult.is24GHz(mFrequency);
405    }
406
407    /**
408     * @hide
409     * TODO: makes real freq boundaries
410     */
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     */
420    public void setMacAddress(String macAddress) {
421        this.mMacAddress = macAddress;
422    }
423
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     */
433    public boolean hasRealMacAddress() {
434        return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress);
435    }
436
437    /** {@hide} */
438    public void setMeteredHint(boolean meteredHint) {
439        mMeteredHint = meteredHint;
440    }
441
442    /** {@hide} */
443    public boolean getMeteredHint() {
444        return mMeteredHint;
445    }
446
447    /** {@hide} */
448    public void setEphemeral(boolean ephemeral) {
449        mEphemeral = ephemeral;
450    }
451
452    /** {@hide} */
453    public boolean isEphemeral() {
454        return mEphemeral;
455    }
456
457    /** @hide */
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     */
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     */
477    public SupplicantState getSupplicantState() {
478        return mSupplicantState;
479    }
480
481    /** @hide */
482    public void setSupplicantState(SupplicantState state) {
483        mSupplicantState = state;
484    }
485
486    /** @hide */
487    public void setInetAddress(InetAddress address) {
488        mIpAddress = address;
489    }
490
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     */
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     */
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     */
523    void setSupplicantState(String stateName) {
524        mSupplicantState = valueOf(stateName);
525    }
526
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} */
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
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} */
569    public int describeContents() {
570        return 0;
571    }
572
573    /** Implement the Parcelable interface {@hide} */
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