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.lang.Math;
26import java.net.InetAddress;
27import java.net.Inet4Address;
28import java.net.UnknownHostException;
29import java.util.EnumMap;
30import java.util.Locale;
31
32/**
33 * Describes the state of any Wifi connection that is active or
34 * is in the process of being set up.
35 */
36public class WifiInfo implements Parcelable {
37    private static final String TAG = "WifiInfo";
38    /**
39     * This is the map described in the Javadoc comment above. The positions
40     * of the elements of the array must correspond to the ordinal values
41     * of <code>DetailedState</code>.
42     */
43    private static final EnumMap<SupplicantState, DetailedState> stateMap =
44            new EnumMap<SupplicantState, DetailedState>(SupplicantState.class);
45
46    /**
47     * Default MAC address reported to a client that does not have the
48     * android.permission.LOCAL_MAC_ADDRESS permission.
49     *
50     * @hide
51     */
52    public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
53
54    static {
55        stateMap.put(SupplicantState.DISCONNECTED, DetailedState.DISCONNECTED);
56        stateMap.put(SupplicantState.INTERFACE_DISABLED, DetailedState.DISCONNECTED);
57        stateMap.put(SupplicantState.INACTIVE, DetailedState.IDLE);
58        stateMap.put(SupplicantState.SCANNING, DetailedState.SCANNING);
59        stateMap.put(SupplicantState.AUTHENTICATING, DetailedState.CONNECTING);
60        stateMap.put(SupplicantState.ASSOCIATING, DetailedState.CONNECTING);
61        stateMap.put(SupplicantState.ASSOCIATED, DetailedState.CONNECTING);
62        stateMap.put(SupplicantState.FOUR_WAY_HANDSHAKE, DetailedState.AUTHENTICATING);
63        stateMap.put(SupplicantState.GROUP_HANDSHAKE, DetailedState.AUTHENTICATING);
64        stateMap.put(SupplicantState.COMPLETED, DetailedState.OBTAINING_IPADDR);
65        stateMap.put(SupplicantState.DORMANT, DetailedState.DISCONNECTED);
66        stateMap.put(SupplicantState.UNINITIALIZED, DetailedState.IDLE);
67        stateMap.put(SupplicantState.INVALID, DetailedState.FAILED);
68    }
69
70    private SupplicantState mSupplicantState;
71    private String mBSSID;
72    private WifiSsid mWifiSsid;
73    private int mNetworkId;
74
75    /** @hide **/
76    public static final int INVALID_RSSI = -127;
77
78    /** @hide **/
79    public static final int MIN_RSSI = -126;
80
81    /** @hide **/
82    public static final int MAX_RSSI = 200;
83
84
85    /**
86     * Received Signal Strength Indicator
87     */
88    private int mRssi;
89
90    /**
91     * Link speed in Mbps
92     */
93    public static final String LINK_SPEED_UNITS = "Mbps";
94    private int mLinkSpeed;
95
96    /**
97     * Frequency in MHz
98     */
99    public static final String FREQUENCY_UNITS = "MHz";
100    private int mFrequency;
101
102    private InetAddress mIpAddress;
103    private String mMacAddress = DEFAULT_MAC_ADDRESS;
104
105    private boolean mEphemeral;
106
107    /**
108     * Running total count of lost (not ACKed) transmitted unicast data packets.
109     * @hide
110     */
111    public long txBad;
112    /**
113     * Running total count of transmitted unicast data retry packets.
114     * @hide
115     */
116    public long txRetries;
117    /**
118     * Running total count of successfully transmitted (ACKed) unicast data packets.
119     * @hide
120     */
121    public long txSuccess;
122    /**
123     * Running total count of received unicast data packets.
124     * @hide
125     */
126    public long rxSuccess;
127
128    /**
129     * Average rate of lost transmitted packets, in units of packets per 5 seconds.
130     * @hide
131     */
132    public double txBadRate;
133    /**
134     * Average rate of transmitted retry packets, in units of packets per 5 seconds.
135     * @hide
136     */
137    public double txRetriesRate;
138    /**
139     * Average rate of successfully transmitted unicast packets, in units of packets per 5 seconds.
140     * @hide
141     */
142    public double txSuccessRate;
143    /**
144     * Average rate of received unicast data packets, in units of packets per 5 seconds.
145     * @hide
146     */
147    public double rxSuccessRate;
148
149    private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
150    private static final long FILTER_TIME_CONSTANT = 3000;
151    /**
152     * This factor is used to adjust the rate output under the new algorithm
153     * such that the result is comparable to the previous algorithm.
154     * This actually converts from unit 'packets per second' to 'packets per 5 seconds'.
155     */
156    private static final long OUTPUT_SCALE_FACTOR = 5;
157    private long mLastPacketCountUpdateTimeStamp;
158
159    /**
160     * @hide
161     */
162    public int badRssiCount;
163
164    /**
165     * @hide
166     */
167    public int linkStuckCount;
168
169    /**
170     * @hide
171     */
172    public int lowRssiCount;
173
174    /**
175     * @hide
176     */
177    public int score;
178
179    /**
180     * @hide
181     */
182    public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) {
183        if (stats != null) {
184            long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
185            long txretries = stats.retries_be + stats.retries_bk
186                    + stats.retries_vi + stats.retries_vo;
187            long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo;
188            long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
189                    + stats.lostmpdu_vi + stats.lostmpdu_vo;
190
191            if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
192                    && mLastPacketCountUpdateTimeStamp < timeStamp
193                    && txBad <= txbad
194                    && txSuccess <= txgood
195                    && rxSuccess <= rxgood
196                    && txRetries <= txretries) {
197                    long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp;
198                    double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
199                    double currentSampleWeight = 1.0 - lastSampleWeight;
200
201                    txBadRate = txBadRate * lastSampleWeight
202                        + (txbad - txBad) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
203                        * currentSampleWeight;
204                    txSuccessRate = txSuccessRate * lastSampleWeight
205                        + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
206                        * currentSampleWeight;
207                    rxSuccessRate = rxSuccessRate * lastSampleWeight
208                        + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
209                        * currentSampleWeight;
210                    txRetriesRate = txRetriesRate * lastSampleWeight
211                        + (txretries - txRetries) * OUTPUT_SCALE_FACTOR * 1000/ timeDelta
212                        * currentSampleWeight;
213            } else {
214                txBadRate = 0;
215                txSuccessRate = 0;
216                rxSuccessRate = 0;
217                txRetriesRate = 0;
218            }
219            txBad = txbad;
220            txSuccess = txgood;
221            rxSuccess = rxgood;
222            txRetries = txretries;
223            mLastPacketCountUpdateTimeStamp = timeStamp;
224        } else {
225            txBad = 0;
226            txSuccess = 0;
227            rxSuccess = 0;
228            txRetries = 0;
229            txBadRate = 0;
230            txSuccessRate = 0;
231            rxSuccessRate = 0;
232            txRetriesRate = 0;
233            mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
234        }
235    }
236
237
238    /**
239     * This function is less powerful and used if the WifiLinkLayerStats API is not implemented
240     * at the Wifi HAL
241     * @hide
242     */
243    public void updatePacketRates(long txPackets, long rxPackets) {
244        //paranoia
245        txBad = 0;
246        txRetries = 0;
247        txBadRate = 0;
248        txRetriesRate = 0;
249        if (txSuccess <= txPackets && rxSuccess <= rxPackets) {
250            txSuccessRate = (txSuccessRate * 0.5)
251                    + ((double) (txPackets - txSuccess) * 0.5);
252            rxSuccessRate = (rxSuccessRate * 0.5)
253                    + ((double) (rxPackets - rxSuccess) * 0.5);
254        } else {
255            txBadRate = 0;
256            txRetriesRate = 0;
257        }
258        txSuccess = txPackets;
259        rxSuccess = rxPackets;
260    }
261
262        /**
263         * Flag indicating that AP has hinted that upstream connection is metered,
264         * and sensitive to heavy data transfers.
265         */
266    private boolean mMeteredHint;
267
268    /** @hide */
269    public WifiInfo() {
270        mWifiSsid = null;
271        mBSSID = null;
272        mNetworkId = -1;
273        mSupplicantState = SupplicantState.UNINITIALIZED;
274        mRssi = INVALID_RSSI;
275        mLinkSpeed = -1;
276        mFrequency = -1;
277        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
278    }
279
280    /** @hide */
281    public void reset() {
282        setInetAddress(null);
283        setBSSID(null);
284        setSSID(null);
285        setNetworkId(-1);
286        setRssi(INVALID_RSSI);
287        setLinkSpeed(-1);
288        setFrequency(-1);
289        setMeteredHint(false);
290        setEphemeral(false);
291        txBad = 0;
292        txSuccess = 0;
293        rxSuccess = 0;
294        txRetries = 0;
295        txBadRate = 0;
296        txSuccessRate = 0;
297        rxSuccessRate = 0;
298        txRetriesRate = 0;
299        lowRssiCount = 0;
300        badRssiCount = 0;
301        linkStuckCount = 0;
302        score = 0;
303        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
304    }
305
306    /**
307     * Copy constructor
308     * @hide
309     */
310    public WifiInfo(WifiInfo source) {
311        if (source != null) {
312            mSupplicantState = source.mSupplicantState;
313            mBSSID = source.mBSSID;
314            mWifiSsid = source.mWifiSsid;
315            mNetworkId = source.mNetworkId;
316            mRssi = source.mRssi;
317            mLinkSpeed = source.mLinkSpeed;
318            mFrequency = source.mFrequency;
319            mIpAddress = source.mIpAddress;
320            mMacAddress = source.mMacAddress;
321            mMeteredHint = source.mMeteredHint;
322            mEphemeral = source.mEphemeral;
323            txBad = source.txBad;
324            txRetries = source.txRetries;
325            txSuccess = source.txSuccess;
326            rxSuccess = source.rxSuccess;
327            txBadRate = source.txBadRate;
328            txRetriesRate = source.txRetriesRate;
329            txSuccessRate = source.txSuccessRate;
330            rxSuccessRate = source.rxSuccessRate;
331            mLastPacketCountUpdateTimeStamp =
332                source.mLastPacketCountUpdateTimeStamp;
333            score = source.score;
334            badRssiCount = source.badRssiCount;
335            lowRssiCount = source.lowRssiCount;
336            linkStuckCount = source.linkStuckCount;
337        }
338    }
339
340    /** @hide */
341    public void setSSID(WifiSsid wifiSsid) {
342        mWifiSsid = wifiSsid;
343    }
344
345    /**
346     * Returns the service set identifier (SSID) of the current 802.11 network.
347     * If the SSID can be decoded as UTF-8, it will be returned surrounded by double
348     * quotation marks. Otherwise, it is returned as a string of hex digits. The
349     * SSID may be &lt;unknown ssid&gt; if there is no network currently connected,
350     * or if the caller has insufficient permissions to access the SSID.
351     * @return the SSID
352     */
353    public String getSSID() {
354        if (mWifiSsid != null) {
355            String unicode = mWifiSsid.toString();
356            if (!TextUtils.isEmpty(unicode)) {
357                return "\"" + unicode + "\"";
358            } else {
359                String hex = mWifiSsid.getHexString();
360                return (hex != null) ? hex : WifiSsid.NONE;
361            }
362        }
363        return WifiSsid.NONE;
364    }
365
366    /** @hide */
367    public WifiSsid getWifiSsid() {
368        return mWifiSsid;
369    }
370
371    /** @hide */
372    public void setBSSID(String BSSID) {
373        mBSSID = BSSID;
374    }
375
376    /**
377     * Return the basic service set identifier (BSSID) of the current access point.
378     * The BSSID may be {@code null} if there is no network currently connected.
379     * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}
380     */
381    public String getBSSID() {
382        return mBSSID;
383    }
384
385    /**
386     * Returns the received signal strength indicator of the current 802.11
387     * network, in dBm.
388     *
389     * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into
390     * an absolute signal level which can be displayed to a user.
391     *
392     * @return the RSSI.
393     */
394    public int getRssi() {
395        return mRssi;
396    }
397
398    /** @hide */
399    public void setRssi(int rssi) {
400        if (rssi < INVALID_RSSI)
401            rssi = INVALID_RSSI;
402        if (rssi > MAX_RSSI)
403            rssi = MAX_RSSI;
404        mRssi = rssi;
405    }
406
407    /**
408     * Returns the current link speed in {@link #LINK_SPEED_UNITS}.
409     * @return the link speed.
410     * @see #LINK_SPEED_UNITS
411     */
412    public int getLinkSpeed() {
413        return mLinkSpeed;
414    }
415
416    /** @hide */
417    public void setLinkSpeed(int linkSpeed) {
418        this.mLinkSpeed = linkSpeed;
419    }
420
421    /**
422     * Returns the current frequency in {@link #FREQUENCY_UNITS}.
423     * @return the frequency.
424     * @see #FREQUENCY_UNITS
425     */
426    public int getFrequency() {
427        return mFrequency;
428    }
429
430    /** @hide */
431    public void setFrequency(int frequency) {
432        this.mFrequency = frequency;
433    }
434
435    /**
436     * @hide
437     * TODO: makes real freq boundaries
438     */
439    public boolean is24GHz() {
440        return ScanResult.is24GHz(mFrequency);
441    }
442
443    /**
444     * @hide
445     * TODO: makes real freq boundaries
446     */
447    public boolean is5GHz() {
448        return ScanResult.is5GHz(mFrequency);
449    }
450
451    /**
452     * @hide
453     * This returns txSuccessRate in packets per second.
454     */
455    public double getTxSuccessRatePps() {
456        return txSuccessRate / OUTPUT_SCALE_FACTOR;
457    }
458
459    /**
460     * @hide
461     * This returns rxSuccessRate in packets per second.
462     */
463    public double getRxSuccessRatePps() {
464        return rxSuccessRate / OUTPUT_SCALE_FACTOR;
465    }
466
467    /**
468     * Record the MAC address of the WLAN interface
469     * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
470     * @hide
471     */
472    public void setMacAddress(String macAddress) {
473        this.mMacAddress = macAddress;
474    }
475
476    public String getMacAddress() {
477        return mMacAddress;
478    }
479
480    /**
481     * @return true if {@link #getMacAddress()} has a real MAC address.
482     *
483     * @hide
484     */
485    public boolean hasRealMacAddress() {
486        return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress);
487    }
488
489    /**
490     * Indicates if we've dynamically detected this active network connection as
491     * being metered.
492     *
493     * @see WifiConfiguration#isMetered(WifiConfiguration, WifiInfo)
494     * @hide
495     */
496    public void setMeteredHint(boolean meteredHint) {
497        mMeteredHint = meteredHint;
498    }
499
500    /** {@hide} */
501    public boolean getMeteredHint() {
502        return mMeteredHint;
503    }
504
505    /** {@hide} */
506    public void setEphemeral(boolean ephemeral) {
507        mEphemeral = ephemeral;
508    }
509
510    /** {@hide} */
511    public boolean isEphemeral() {
512        return mEphemeral;
513    }
514
515    /** @hide */
516    public void setNetworkId(int id) {
517        mNetworkId = id;
518    }
519
520    /**
521     * Each configured network has a unique small integer ID, used to identify
522     * the network when performing operations on the supplicant. This method
523     * returns the ID for the currently connected network.
524     * @return the network ID, or -1 if there is no currently connected network
525     */
526    public int getNetworkId() {
527        return mNetworkId;
528    }
529
530    /**
531     * Return the detailed state of the supplicant's negotiation with an
532     * access point, in the form of a {@link SupplicantState SupplicantState} object.
533     * @return the current {@link SupplicantState SupplicantState}
534     */
535    public SupplicantState getSupplicantState() {
536        return mSupplicantState;
537    }
538
539    /** @hide */
540    public void setSupplicantState(SupplicantState state) {
541        mSupplicantState = state;
542    }
543
544    /** @hide */
545    public void setInetAddress(InetAddress address) {
546        mIpAddress = address;
547    }
548
549    public int getIpAddress() {
550        int result = 0;
551        if (mIpAddress instanceof Inet4Address) {
552            result = NetworkUtils.inetAddressToInt((Inet4Address)mIpAddress);
553        }
554        return result;
555    }
556
557    /**
558     * @return {@code true} if this network does not broadcast its SSID, so an
559     * SSID-specific probe request must be used for scans.
560     */
561    public boolean getHiddenSSID() {
562        if (mWifiSsid == null) return false;
563        return mWifiSsid.isHidden();
564    }
565
566   /**
567     * Map a supplicant state into a fine-grained network connectivity state.
568     * @param suppState the supplicant state
569     * @return the corresponding {@link DetailedState}
570     */
571    public static DetailedState getDetailedStateOf(SupplicantState suppState) {
572        return stateMap.get(suppState);
573    }
574
575    /**
576     * Set the <code>SupplicantState</code> from the string name
577     * of the state.
578     * @param stateName the name of the state, as a <code>String</code> returned
579     * in an event sent by {@code wpa_supplicant}.
580     */
581    void setSupplicantState(String stateName) {
582        mSupplicantState = valueOf(stateName);
583    }
584
585    static SupplicantState valueOf(String stateName) {
586        if ("4WAY_HANDSHAKE".equalsIgnoreCase(stateName))
587            return SupplicantState.FOUR_WAY_HANDSHAKE;
588        else {
589            try {
590                return SupplicantState.valueOf(stateName.toUpperCase(Locale.ROOT));
591            } catch (IllegalArgumentException e) {
592                return SupplicantState.INVALID;
593            }
594        }
595    }
596
597    /** {@hide} */
598    public static String removeDoubleQuotes(String string) {
599        if (string == null) return null;
600        final int length = string.length();
601        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
602            return string.substring(1, length - 1);
603        }
604        return string;
605    }
606
607    @Override
608    public String toString() {
609        StringBuffer sb = new StringBuffer();
610        String none = "<none>";
611
612        sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid).
613            append(", BSSID: ").append(mBSSID == null ? none : mBSSID).
614            append(", MAC: ").append(mMacAddress == null ? none : mMacAddress).
615            append(", Supplicant state: ").
616            append(mSupplicantState == null ? none : mSupplicantState).
617            append(", RSSI: ").append(mRssi).
618            append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS).
619            append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS).
620            append(", Net ID: ").append(mNetworkId).
621            append(", Metered hint: ").append(mMeteredHint).
622            append(", score: ").append(Integer.toString(score));
623        return sb.toString();
624    }
625
626    /** Implement the Parcelable interface {@hide} */
627    public int describeContents() {
628        return 0;
629    }
630
631    /** Implement the Parcelable interface {@hide} */
632    public void writeToParcel(Parcel dest, int flags) {
633        dest.writeInt(mNetworkId);
634        dest.writeInt(mRssi);
635        dest.writeInt(mLinkSpeed);
636        dest.writeInt(mFrequency);
637        if (mIpAddress != null) {
638            dest.writeByte((byte)1);
639            dest.writeByteArray(mIpAddress.getAddress());
640        } else {
641            dest.writeByte((byte)0);
642        }
643        if (mWifiSsid != null) {
644            dest.writeInt(1);
645            mWifiSsid.writeToParcel(dest, flags);
646        } else {
647            dest.writeInt(0);
648        }
649        dest.writeString(mBSSID);
650        dest.writeString(mMacAddress);
651        dest.writeInt(mMeteredHint ? 1 : 0);
652        dest.writeInt(mEphemeral ? 1 : 0);
653        dest.writeInt(score);
654        dest.writeDouble(txSuccessRate);
655        dest.writeDouble(txRetriesRate);
656        dest.writeDouble(txBadRate);
657        dest.writeDouble(rxSuccessRate);
658        dest.writeInt(badRssiCount);
659        dest.writeInt(lowRssiCount);
660        mSupplicantState.writeToParcel(dest, flags);
661    }
662
663    /** Implement the Parcelable interface {@hide} */
664    public static final Creator<WifiInfo> CREATOR =
665        new Creator<WifiInfo>() {
666            public WifiInfo createFromParcel(Parcel in) {
667                WifiInfo info = new WifiInfo();
668                info.setNetworkId(in.readInt());
669                info.setRssi(in.readInt());
670                info.setLinkSpeed(in.readInt());
671                info.setFrequency(in.readInt());
672                if (in.readByte() == 1) {
673                    try {
674                        info.setInetAddress(InetAddress.getByAddress(in.createByteArray()));
675                    } catch (UnknownHostException e) {}
676                }
677                if (in.readInt() == 1) {
678                    info.mWifiSsid = WifiSsid.CREATOR.createFromParcel(in);
679                }
680                info.mBSSID = in.readString();
681                info.mMacAddress = in.readString();
682                info.mMeteredHint = in.readInt() != 0;
683                info.mEphemeral = in.readInt() != 0;
684                info.score = in.readInt();
685                info.txSuccessRate = in.readDouble();
686                info.txRetriesRate = in.readDouble();
687                info.txBadRate = in.readDouble();
688                info.rxSuccessRate = in.readDouble();
689                info.badRssiCount = in.readInt();
690                info.lowRssiCount = in.readInt();
691                info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
692                return info;
693            }
694
695            public WifiInfo[] newArray(int size) {
696                return new WifiInfo[size];
697            }
698        };
699}
700