WifiConfiguration.java revision adae06be9bfd8cb756e1793b52e682eb87258968
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.annotation.SystemApi;
20import android.net.IpConfiguration;
21import android.net.IpConfiguration.ProxySettings;
22import android.net.IpConfiguration.IpAssignment;
23import android.net.ProxyInfo;
24import android.net.LinkProperties;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.text.TextUtils;
28
29import java.util.HashMap;
30import java.util.BitSet;
31
32/**
33 * A class representing a configured Wi-Fi network, including the
34 * security configuration.
35 */
36public class WifiConfiguration implements Parcelable {
37    private static final String TAG = "WifiConfiguration";
38    /** {@hide} */
39    public static final String ssidVarName = "ssid";
40    /** {@hide} */
41    public static final String bssidVarName = "bssid";
42    /** {@hide} */
43    public static final String pskVarName = "psk";
44    /** {@hide} */
45    public static final String[] wepKeyVarNames = { "wep_key0", "wep_key1", "wep_key2", "wep_key3" };
46    /** {@hide} */
47    public static final String wepTxKeyIdxVarName = "wep_tx_keyidx";
48    /** {@hide} */
49    public static final String priorityVarName = "priority";
50    /** {@hide} */
51    public static final String hiddenSSIDVarName = "scan_ssid";
52    /** {@hide} */
53    public static final String pmfVarName = "ieee80211w";
54    /** {@hide} */
55    public static final String updateIdentiferVarName = "update_identifier";
56    /** {@hide} */
57    public static final int INVALID_NETWORK_ID = -1;
58    /**
59     * Recognized key management schemes.
60     */
61    public static class KeyMgmt {
62        private KeyMgmt() { }
63
64        /** WPA is not used; plaintext or static WEP could be used. */
65        public static final int NONE = 0;
66        /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */
67        public static final int WPA_PSK = 1;
68        /** WPA using EAP authentication. Generally used with an external authentication server. */
69        public static final int WPA_EAP = 2;
70        /** IEEE 802.1X using EAP authentication and (optionally) dynamically
71         * generated WEP keys. */
72        public static final int IEEE8021X = 3;
73
74        /** WPA2 pre-shared key for use with soft access point
75          * (requires {@code preSharedKey} to be specified).
76          * @hide
77          */
78        public static final int WPA2_PSK = 4;
79
80        public static final String varName = "key_mgmt";
81
82        public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X",
83                "WPA2_PSK" };
84    }
85
86    /**
87     * Recognized security protocols.
88     */
89    public static class Protocol {
90        private Protocol() { }
91
92        /** WPA/IEEE 802.11i/D3.0 */
93        public static final int WPA = 0;
94        /** WPA2/IEEE 802.11i */
95        public static final int RSN = 1;
96
97        public static final String varName = "proto";
98
99        public static final String[] strings = { "WPA", "RSN" };
100    }
101
102    /**
103     * Recognized IEEE 802.11 authentication algorithms.
104     */
105    public static class AuthAlgorithm {
106        private AuthAlgorithm() { }
107
108        /** Open System authentication (required for WPA/WPA2) */
109        public static final int OPEN = 0;
110        /** Shared Key authentication (requires static WEP keys) */
111        public static final int SHARED = 1;
112        /** LEAP/Network EAP (only used with LEAP) */
113        public static final int LEAP = 2;
114
115        public static final String varName = "auth_alg";
116
117        public static final String[] strings = { "OPEN", "SHARED", "LEAP" };
118    }
119
120    /**
121     * Recognized pairwise ciphers for WPA.
122     */
123    public static class PairwiseCipher {
124        private PairwiseCipher() { }
125
126        /** Use only Group keys (deprecated) */
127        public static final int NONE = 0;
128        /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
129        public static final int TKIP = 1;
130        /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
131        public static final int CCMP = 2;
132
133        public static final String varName = "pairwise";
134
135        public static final String[] strings = { "NONE", "TKIP", "CCMP" };
136    }
137
138    /**
139     * Recognized group ciphers.
140     * <pre>
141     * CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
142     * TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
143     * WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
144     * WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11)
145     * </pre>
146     */
147    public static class GroupCipher {
148        private GroupCipher() { }
149
150        /** WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11) */
151        public static final int WEP40 = 0;
152        /** WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key */
153        public static final int WEP104 = 1;
154        /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
155        public static final int TKIP = 2;
156        /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
157        public static final int CCMP = 3;
158
159        public static final String varName = "group";
160
161        public static final String[] strings = { "WEP40", "WEP104", "TKIP", "CCMP" };
162    }
163
164    /** Possible status of a network configuration. */
165    public static class Status {
166        private Status() { }
167
168        /** this is the network we are currently connected to */
169        public static final int CURRENT = 0;
170        /** supplicant will not attempt to use this network */
171        public static final int DISABLED = 1;
172        /** supplicant will consider this network available for association */
173        public static final int ENABLED = 2;
174
175        public static final String[] strings = { "current", "disabled", "enabled" };
176    }
177
178    /** @hide */
179    public static final int DISABLED_UNKNOWN_REASON                         = 0;
180    /** @hide */
181    public static final int DISABLED_DNS_FAILURE                            = 1;
182    /** @hide */
183    public static final int DISABLED_DHCP_FAILURE                           = 2;
184    /** @hide */
185    public static final int DISABLED_AUTH_FAILURE                           = 3;
186    /** @hide */
187    public static final int DISABLED_ASSOCIATION_REJECT                     = 4;
188
189    /**
190     * The ID number that the supplicant uses to identify this
191     * network configuration entry. This must be passed as an argument
192     * to most calls into the supplicant.
193     */
194    public int networkId;
195
196    /**
197     * The current status of this network configuration entry.
198     * @see Status
199     */
200    public int status;
201
202    /**
203     * The code referring to a reason for disabling the network
204     * Valid when {@link #status} == Status.DISABLED
205     * @hide
206     */
207    public int disableReason;
208
209    /**
210     * The network's SSID. Can either be an ASCII string,
211     * which must be enclosed in double quotation marks
212     * (e.g., {@code "MyNetwork"}, or a string of
213     * hex digits,which are not enclosed in quotes
214     * (e.g., {@code 01a243f405}).
215     */
216    public String SSID;
217    /**
218     * When set, this network configuration entry should only be used when
219     * associating with the AP having the specified BSSID. The value is
220     * a string in the format of an Ethernet MAC address, e.g.,
221     * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
222     */
223    public String BSSID;
224    /**
225     * Fully qualified domain name (FQDN), for Passpoint credential.
226     * e.g. {@code "mail.example.com"}.
227     * @hide
228     */
229    public String FQDN;
230    /**
231     * Network access identifier (NAI) realm, for Passpoint credential.
232     * e.g. {@code "myhost.example.com"}.
233     * @hide
234     */
235    public String naiRealm;
236
237    /**
238     * Pre-shared key for use with WPA-PSK.
239     * <p/>
240     * When the value of this key is read, the actual key is
241     * not returned, just a "*" if the key has a value, or the null
242     * string otherwise.
243     */
244    public String preSharedKey;
245    /**
246     * Up to four WEP keys. Either an ASCII string enclosed in double
247     * quotation marks (e.g., {@code "abcdef"} or a string
248     * of hex digits (e.g., {@code 0102030405}).
249     * <p/>
250     * When the value of one of these keys is read, the actual key is
251     * not returned, just a "*" if the key has a value, or the null
252     * string otherwise.
253     */
254    public String[] wepKeys;
255
256    /** Default WEP key index, ranging from 0 to 3. */
257    public int wepTxKeyIndex;
258
259    /**
260     * Priority determines the preference given to a network by {@code wpa_supplicant}
261     * when choosing an access point with which to associate.
262     */
263    public int priority;
264
265    /**
266     * This is a network that does not broadcast its SSID, so an
267     * SSID-specific probe request must be used for scans.
268     */
269    public boolean hiddenSSID;
270
271    /**
272     * This is a network that requries Protected Management Frames (PMF).
273     * @hide
274     */
275    public boolean requirePMF;
276
277    /**
278     * Update identifier, for Passpoint network.
279     * @hide
280     */
281    public String updateIdentifier;
282
283    /**
284     * The set of key management protocols supported by this configuration.
285     * See {@link KeyMgmt} for descriptions of the values.
286     * Defaults to WPA-PSK WPA-EAP.
287     */
288    public BitSet allowedKeyManagement;
289    /**
290     * The set of security protocols supported by this configuration.
291     * See {@link Protocol} for descriptions of the values.
292     * Defaults to WPA RSN.
293     */
294    public BitSet allowedProtocols;
295    /**
296     * The set of authentication protocols supported by this configuration.
297     * See {@link AuthAlgorithm} for descriptions of the values.
298     * Defaults to automatic selection.
299     */
300    public BitSet allowedAuthAlgorithms;
301    /**
302     * The set of pairwise ciphers for WPA supported by this configuration.
303     * See {@link PairwiseCipher} for descriptions of the values.
304     * Defaults to CCMP TKIP.
305     */
306    public BitSet allowedPairwiseCiphers;
307    /**
308     * The set of group ciphers supported by this configuration.
309     * See {@link GroupCipher} for descriptions of the values.
310     * Defaults to CCMP TKIP WEP104 WEP40.
311     */
312    public BitSet allowedGroupCiphers;
313    /**
314     * The enterprise configuration details specifying the EAP method,
315     * certificates and other settings associated with the EAP.
316     */
317    public WifiEnterpriseConfig enterpriseConfig;
318
319    /**
320     * @hide
321     */
322    private IpConfiguration mIpConfiguration;
323
324    /**
325     * @hide
326     * dhcp server MAC address if known
327     */
328    public String dhcpServer;
329
330    /**
331     * @hide
332     * default Gateway MAC address if known
333     */
334    public String defaultGwMacAddress;
335
336    /**
337     * @hide
338     * last failure
339     */
340    public String lastFailure;
341
342    /**
343     * @hide
344     * Uid of app creating the configuration
345     */
346    @SystemApi
347    public int creatorUid;
348
349    /**
350     * @hide
351     * Uid of last app issuing a connection related command
352     */
353    public int lastConnectUid;
354
355    /**
356     * @hide
357     * Uid of last app modifying the configuration
358     */
359    public int lastUpdateUid;
360
361    /**
362     * @hide
363     * Uid of app owning the BSSID
364     */
365    public int bssidOwnerUid;
366
367    /**
368     * @hide
369     * BSSID list on which this configuration was seen.
370     * TODO: prevent this list to grow infinitely, age-out the results
371     */
372    public HashMap<String, ScanResult> scanResultCache;
373
374    /** The Below RSSI thresholds are used to configure AutoJoin
375     *  - GOOD/LOW/BAD thresholds are used so as to calculate link score
376     *  - UNWANTED_SOFT are used by the blacklisting logic so as to handle the unwanted network message coming from CS
377     *  - UNBLACKLIST thresholds are used so as to tweak the speed at which the network is unblacklisted (i.e. if
378     *          it is seen with good RSSI, it is blacklisted faster)
379     *  - INITIAL_AUTOJOIN_ATTEMPT, used to determine how close from the network we need to be before autojoin kicks in
380     */
381    /** @hide **/
382    public static int INVALID_RSSI = -127;
383
384    /** @hide **/
385    public static int UNWANTED_BLACKLIST_SOFT_RSSI_24 = -80;
386
387    /** @hide **/
388    public static int UNWANTED_BLACKLIST_SOFT_RSSI_5 = -70;
389
390    /** @hide **/
391    public static int GOOD_RSSI_24 = -65;
392
393    /** @hide **/
394    public static int LOW_RSSI_24 = -77;
395
396    /** @hide **/
397    public static int BAD_RSSI_24 = -87;
398
399    /** @hide **/
400    public static int GOOD_RSSI_5 = -60;
401
402    /** @hide **/
403    public static int LOW_RSSI_5 = -72;
404
405    /** @hide **/
406    public static int BAD_RSSI_5 = -82;
407
408    /** @hide **/
409    public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
410
411    /** @hide **/
412    public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
413
414    /** @hide **/
415    public static int UNBLACKLIST_THRESHOLD_24_SOFT = -77;
416
417    /** @hide **/
418    public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
419
420    /** @hide **/
421    public static int UNBLACKLIST_THRESHOLD_5_SOFT = -63;
422
423    /** @hide **/
424    public static int UNBLACKLIST_THRESHOLD_5_HARD = -56;
425
426    /** @hide **/
427    public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_24 = -80;
428
429    /** @hide **/
430    public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70;
431
432    /** @hide
433     * 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/
434    public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
435
436    /** @hide
437     * 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
438    public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
439
440    /** @hide
441     * Boost given to RSSI on a home network for the purpose of calculating the score
442     * This adds stickiness to home networks, as defined by:
443     * - less than 4 known BSSIDs
444     * - PSK only
445     * - TODO: add a test to verify that all BSSIDs are behind same gateway
446     ***/
447    public static int HOME_NETWORK_RSSI_BOOST = 5;
448
449    /**
450     * @hide
451     * A summary of the RSSI and Band status for that configuration
452     * This is used as a temporary value by the auto-join controller
453     */
454    public final class Visibility {
455        public int rssi5;   // strongest 5GHz RSSI
456        public int rssi24;  // strongest 2.4GHz RSSI
457        public int num5;    // number of BSSIDs on 5GHz
458        public int num24;   // number of BSSIDs on 2.4GHz
459        public long age5;  // timestamp of the strongest 5GHz BSSID (last time it was seen)
460        public long age24;   // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
461
462        public Visibility() {
463            rssi5 = INVALID_RSSI;
464            rssi24 = INVALID_RSSI;
465        }
466
467        public Visibility(Visibility source) {
468            rssi5 = source.rssi5;
469            rssi24 = source.rssi24;
470            age24 = source.age24;
471            age5 = source.age5;
472            num24 = source.num24;
473            num5 = source.num5;
474        }
475
476        @Override
477        public String toString() {
478            StringBuilder sbuf = new StringBuilder();
479            sbuf.append("[");
480            if (rssi24 > INVALID_RSSI) {
481                sbuf.append(Integer.toString(rssi24));
482                sbuf.append(",");
483                sbuf.append(Integer.toString(num24));
484            } else {
485                sbuf.append("*");
486            }
487            sbuf.append(" - ");
488            if (rssi5 > INVALID_RSSI) {
489                sbuf.append(Integer.toString(rssi5));
490                sbuf.append(",");
491                sbuf.append(Integer.toString(num5));
492            }
493            sbuf.append("]");
494            return sbuf.toString();
495        }
496    }
497
498    /** @hide
499     * Cache the visibility status of this configuration.
500     * Visibility can change at any time depending on scan results availability.
501     * Owner of the WifiConfiguration is responsible to set this field based on
502     * recent scan results.
503     ***/
504    public Visibility visibility;
505
506    /** @hide
507     * calculate and set Visibility for that configuration.
508     *
509     * age in milliseconds: we will consider only ScanResults that are more recent,
510     * i.e. younger.
511     ***/
512    public Visibility setVisibility(long age) {
513        if (scanResultCache == null) {
514            visibility = null;
515            return null;
516        }
517
518        Visibility status = new Visibility();
519
520        long now_ms = System.currentTimeMillis();
521        for(ScanResult result : scanResultCache.values()) {
522            if (result.seen == 0)
523                continue;
524
525            if (result.is5GHz()) {
526                //strictly speaking: [4915, 5825]
527                //number of known BSSID on 5GHz band
528                status.num5 = status.num5 + 1;
529            } else if (result.is24GHz()) {
530                //strictly speaking: [2412, 2482]
531                //number of known BSSID on 2.4Ghz band
532                status.num24 = status.num24 + 1;
533            }
534
535            if ((now_ms - result.seen) > age) continue;
536
537            if (result.is5GHz()) {
538                if (result.level > status.rssi5) {
539                    status.rssi5 = result.level;
540                    status.age5 = result.seen;
541                }
542            } else if (result.is24GHz()) {
543                if (result.level > status.rssi24) {
544                    status.rssi24 = result.level;
545                    status.age24 = result.seen;
546                }
547            }
548        }
549        visibility = status;
550        return status;
551    }
552
553    /** @hide */
554    public static final int AUTO_JOIN_ENABLED                   = 0;
555    /**
556     * if this is set, the WifiConfiguration cannot use linkages so as to bump
557     * it's relative priority.
558     * - status between and 128 indicate various level of blacklisting depending
559     * on the severity or frequency of the connection error
560     * - deleted status indicates that the user is deleting the configuration, and so
561     * although it may have been self added we will not re-self-add it, ignore it,
562     * not return it to applications, and not connect to it
563     * */
564
565    /** @hide
566     * network was temporary disabled due to bad connection, most likely due
567     * to weak RSSI */
568    public static final int AUTO_JOIN_TEMPORARY_DISABLED  = 1;
569    /** @hide
570     * network was temporary disabled due to bad connection, which cant be attributed
571     * to weak RSSI */
572    public static final int AUTO_JOIN_TEMPORARY_DISABLED_LINK_ERRORS  = 32;
573    /** @hide */
574    public static final int AUTO_JOIN_TEMPORARY_DISABLED_AT_SUPPLICANT  = 64;
575    /** @hide */
576    public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE  = 128;
577    /** @hide */
578    public static final int AUTO_JOIN_DELETED  = 200;
579
580    /**
581     * @hide
582     */
583    public int autoJoinStatus;
584
585
586    /**
587     * @hide
588     */
589    public long blackListTimestamp;
590
591    /**
592     * @hide
593     * last time the system was connected to this configuration.
594     */
595    public long lastConnected;
596
597    /**
598     * @hide
599     * last time the system was disconnected to this configuration.
600     */
601    public long lastDisconnected;
602
603    /**
604     * Set if the configuration was self added by the framework
605     * This boolean is cleared if we get a connect/save/ update or
606     * any wifiManager command that indicate the user interacted with the configuration
607     * since we will now consider that the configuration belong to him.
608     * @hide
609     */
610    public boolean selfAdded;
611
612    /**
613     * Set if the configuration was self added by the framework
614     * This boolean is set once and never cleared. It is used
615     * so as we never loose track of who created the
616     * configuration in the first place.
617     * @hide
618     */
619    public boolean didSelfAdd;
620
621    /**
622     * peer WifiConfiguration this WifiConfiguration was added for
623     * @hide
624     */
625    public String peerWifiConfiguration;
626
627    /**
628     * @hide
629     * Indicate that a WifiConfiguration is temporary and should not be saved
630     * nor considered by AutoJoin.
631     */
632    public boolean ephemeral;
633
634    /**
635     * @hide
636     * Connect choices
637     *
638     * remember the keys identifying the known WifiConfiguration over which this configuration
639     * was preferred by user or a "WiFi Network Management app", that is,
640     * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration
641     * was visible to the user:
642     * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
643     *
644     * The integer represents the configuration's RSSI at that time (useful?)
645     *
646     * The overall auto-join algorithm make use of past connect choice so as to sort configuration,
647     * the exact algorithm still fluctuating as of 5/7/2014
648     *
649     */
650    public HashMap<String, Integer> connectChoices;
651
652    /**
653     * @hide
654     * Linked Configurations: represent the set of Wificonfigurations that are equivalent
655     * regarding roaming and auto-joining.
656     * The linked configuration may or may not have same SSID, and may or may not have same
657     * credentials.
658     * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server.
659     */
660    public HashMap<String, Integer>  linkedConfigurations;
661
662    public WifiConfiguration() {
663        networkId = INVALID_NETWORK_ID;
664        SSID = null;
665        BSSID = null;
666        FQDN = null;
667        naiRealm = null;
668        priority = 0;
669        hiddenSSID = false;
670        disableReason = DISABLED_UNKNOWN_REASON;
671        allowedKeyManagement = new BitSet();
672        allowedProtocols = new BitSet();
673        allowedAuthAlgorithms = new BitSet();
674        allowedPairwiseCiphers = new BitSet();
675        allowedGroupCiphers = new BitSet();
676        wepKeys = new String[4];
677        for (int i = 0; i < wepKeys.length; i++) {
678            wepKeys[i] = null;
679        }
680        enterpriseConfig = new WifiEnterpriseConfig();
681        autoJoinStatus = AUTO_JOIN_ENABLED;
682        selfAdded = false;
683        didSelfAdd = false;
684        ephemeral = false;
685        mIpConfiguration = new IpConfiguration();
686    }
687
688    /**
689     * indicates whether the configuration is valid
690     * @return true if valid, false otherwise
691     * @hide
692     */
693    public boolean isValid() {
694
695        if (allowedKeyManagement == null)
696            return false;
697
698        if (allowedKeyManagement.cardinality() > 1) {
699            if (allowedKeyManagement.cardinality() != 2) {
700                return false;
701            }
702            if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) == false) {
703                return false;
704            }
705            if ((allowedKeyManagement.get(KeyMgmt.IEEE8021X) == false)
706                    && (allowedKeyManagement.get(KeyMgmt.WPA_PSK) == false)) {
707                return false;
708            }
709        }
710
711        // TODO: Add more checks
712        return true;
713    }
714
715    /**
716     * Helper function, identify if a configuration is linked
717     * @hide
718     */
719    public boolean isLinked(WifiConfiguration config) {
720        if (config.linkedConfigurations != null && linkedConfigurations != null) {
721            if (config.linkedConfigurations.get(configKey()) != null
722                    && linkedConfigurations.get(config.configKey()) != null) {
723                return true;
724            }
725        }
726        return  false;
727    }
728
729    /**
730     * most recent time we have seen this configuration
731     * @return most recent scanResult
732     * @hide
733     */
734    public ScanResult lastSeen() {
735        ScanResult mostRecent = null;
736
737        if (scanResultCache == null) {
738            return null;
739        }
740
741        for (ScanResult result : scanResultCache.values()) {
742            if (mostRecent == null) {
743                if (result.seen != 0)
744                   mostRecent = result;
745            } else {
746                if (result.seen > mostRecent.seen) {
747                   mostRecent = result;
748                }
749            }
750        }
751        return mostRecent;
752    }
753
754    /** @hide **/
755    public void setAutoJoinStatus(int status) {
756        if (status < 0) status = 0;
757        if (status == 0) {
758            blackListTimestamp = 0;
759        }  else if (status > autoJoinStatus) {
760            blackListTimestamp = System.currentTimeMillis();
761        }
762        autoJoinStatus = status;
763    }
764
765    @Override
766    public String toString() {
767        StringBuilder sbuf = new StringBuilder();
768        if (this.status == WifiConfiguration.Status.CURRENT) {
769            sbuf.append("* ");
770        } else if (this.status == WifiConfiguration.Status.DISABLED) {
771            sbuf.append("- DSBLE: ").append(this.disableReason).append(" ");
772        }
773        sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
774                append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN).
775                append(" REALM: ").append(this.naiRealm).append(" PRIO: ").append(this.priority).
776                append('\n');
777        sbuf.append(" KeyMgmt:");
778        for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
779            if (this.allowedKeyManagement.get(k)) {
780                sbuf.append(" ");
781                if (k < KeyMgmt.strings.length) {
782                    sbuf.append(KeyMgmt.strings[k]);
783                } else {
784                    sbuf.append("??");
785                }
786            }
787        }
788        sbuf.append(" Protocols:");
789        for (int p = 0; p < this.allowedProtocols.size(); p++) {
790            if (this.allowedProtocols.get(p)) {
791                sbuf.append(" ");
792                if (p < Protocol.strings.length) {
793                    sbuf.append(Protocol.strings[p]);
794                } else {
795                    sbuf.append("??");
796                }
797            }
798        }
799        sbuf.append('\n');
800        sbuf.append(" AuthAlgorithms:");
801        for (int a = 0; a < this.allowedAuthAlgorithms.size(); a++) {
802            if (this.allowedAuthAlgorithms.get(a)) {
803                sbuf.append(" ");
804                if (a < AuthAlgorithm.strings.length) {
805                    sbuf.append(AuthAlgorithm.strings[a]);
806                } else {
807                    sbuf.append("??");
808                }
809            }
810        }
811        sbuf.append('\n');
812        sbuf.append(" PairwiseCiphers:");
813        for (int pc = 0; pc < this.allowedPairwiseCiphers.size(); pc++) {
814            if (this.allowedPairwiseCiphers.get(pc)) {
815                sbuf.append(" ");
816                if (pc < PairwiseCipher.strings.length) {
817                    sbuf.append(PairwiseCipher.strings[pc]);
818                } else {
819                    sbuf.append("??");
820                }
821            }
822        }
823        sbuf.append('\n');
824        sbuf.append(" GroupCiphers:");
825        for (int gc = 0; gc < this.allowedGroupCiphers.size(); gc++) {
826            if (this.allowedGroupCiphers.get(gc)) {
827                sbuf.append(" ");
828                if (gc < GroupCipher.strings.length) {
829                    sbuf.append(GroupCipher.strings[gc]);
830                } else {
831                    sbuf.append("??");
832                }
833            }
834        }
835        sbuf.append('\n').append(" PSK: ");
836        if (this.preSharedKey != null) {
837            sbuf.append('*');
838        }
839
840        sbuf.append(enterpriseConfig);
841        sbuf.append('\n');
842
843        sbuf.append(mIpConfiguration.toString());
844
845        if (selfAdded)  sbuf.append("selfAdded");
846        if (creatorUid != 0)  sbuf.append("uid=" + Integer.toString(creatorUid));
847
848        if (blackListTimestamp != 0) {
849            long now_ms = System.currentTimeMillis();
850            long diff = now_ms - blackListTimestamp;
851            if (diff <= 0) {
852                sbuf.append("blackListed since <incorrect>");
853            } else {
854                sbuf.append("blackListed since ").append(Long.toString(diff/1000)).append( "sec");
855            }
856        }
857
858        return sbuf.toString();
859    }
860
861    /**
862     * Construct a WifiConfiguration from a scanned network
863     * @param scannedAP the scan result used to construct the config entry
864     * TODO: figure out whether this is a useful way to construct a new entry.
865     *
866    public WifiConfiguration(ScanResult scannedAP) {
867        networkId = -1;
868        SSID = scannedAP.SSID;
869        BSSID = scannedAP.BSSID;
870    }
871    */
872
873    /** {@hide} */
874    public String getPrintableSsid() {
875        if (SSID == null) return "";
876        final int length = SSID.length();
877        if (length > 2 && (SSID.charAt(0) == '"') && SSID.charAt(length - 1) == '"') {
878            return SSID.substring(1, length - 1);
879        }
880
881        /** The ascii-encoded string format is P"<ascii-encoded-string>"
882         * The decoding is implemented in the supplicant for a newly configured
883         * network.
884         */
885        if (length > 3 && (SSID.charAt(0) == 'P') && (SSID.charAt(1) == '"') &&
886                (SSID.charAt(length-1) == '"')) {
887            WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(
888                    SSID.substring(2, length - 1));
889            return wifiSsid.toString();
890        }
891        return SSID;
892    }
893
894    /**
895     * Get an identifier for associating credentials with this config
896     * @param current configuration contains values for additional fields
897     *                that are not part of this configuration. Used
898     *                when a config with some fields is passed by an application.
899     * @throws IllegalStateException if config is invalid for key id generation
900     * @hide
901     */
902    public String getKeyIdForCredentials(WifiConfiguration current) {
903        String keyMgmt = null;
904
905        try {
906            // Get current config details for fields that are not initialized
907            if (TextUtils.isEmpty(SSID)) SSID = current.SSID;
908            if (allowedKeyManagement.cardinality() == 0) {
909                allowedKeyManagement = current.allowedKeyManagement;
910            }
911            if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
912                keyMgmt = KeyMgmt.strings[KeyMgmt.WPA_EAP];
913            }
914            if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
915                keyMgmt += KeyMgmt.strings[KeyMgmt.IEEE8021X];
916            }
917
918            if (TextUtils.isEmpty(keyMgmt)) {
919                throw new IllegalStateException("Not an EAP network");
920            }
921
922            return trimStringForKeyId(SSID) + "_" + keyMgmt + "_" +
923                    trimStringForKeyId(enterpriseConfig.getKeyId(current != null ?
924                            current.enterpriseConfig : null));
925        } catch (NullPointerException e) {
926            throw new IllegalStateException("Invalid config details");
927        }
928    }
929
930    private String trimStringForKeyId(String string) {
931        // Remove quotes and spaces
932        return string.replace("\"", "").replace(" ", "");
933    }
934
935    private static BitSet readBitSet(Parcel src) {
936        int cardinality = src.readInt();
937
938        BitSet set = new BitSet();
939        for (int i = 0; i < cardinality; i++) {
940            set.set(src.readInt());
941        }
942
943        return set;
944    }
945
946    private static void writeBitSet(Parcel dest, BitSet set) {
947        int nextSetBit = -1;
948
949        dest.writeInt(set.cardinality());
950
951        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
952            dest.writeInt(nextSetBit);
953        }
954    }
955
956    /** @hide */
957    public int getAuthType() {
958        if (isValid() == false) {
959            throw new IllegalStateException("Invalid configuration");
960        }
961        if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
962            return KeyMgmt.WPA_PSK;
963        } else if (allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
964            return KeyMgmt.WPA2_PSK;
965        } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
966            return KeyMgmt.WPA_EAP;
967        } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
968            return KeyMgmt.IEEE8021X;
969        }
970        return KeyMgmt.NONE;
971    }
972
973    /* @hide
974     * Cache the config key, this seems useful as a speed up since a lot of
975     * lookups in the config store are done and based on this key.
976     */
977    String mCachedConfigKey;
978
979    /** @hide
980     *  return the string used to calculate the hash in WifiConfigStore
981     *  and uniquely identify this WifiConfiguration
982     */
983    public String configKey(boolean allowCached) {
984        String key;
985        if (allowCached && mCachedConfigKey != null) {
986            key = mCachedConfigKey;
987        } else {
988            key = this.SSID;
989            if (key == null)
990                key = "";
991            if (this.wepKeys[0] != null) {
992                key = key + "-WEP";
993            }
994            if (this.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
995                key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
996            }
997            if (this.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
998                    this.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
999                key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1000            }
1001            mCachedConfigKey = key;
1002        }
1003        return key;
1004    }
1005
1006    /** @hide
1007     * get configKey, force calculating the config string
1008     */
1009    public String configKey() {
1010        return configKey(false);
1011    }
1012
1013    /** @hide
1014     * return the config key string based on a scan result
1015     */
1016    static public String configKey(ScanResult result) {
1017        String key = "\"" + result.SSID + "\"";
1018
1019        if (result.capabilities.contains("WEP")) {
1020            key = key + "-WEP";
1021        }
1022
1023        if (result.capabilities.contains("PSK")) {
1024            key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
1025        }
1026
1027        if (result.capabilities.contains("EAP")) {
1028            key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1029        }
1030
1031        return key;
1032    }
1033
1034    /** @hide */
1035    public IpConfiguration getIpConfiguration() {
1036        return mIpConfiguration;
1037    }
1038
1039    /** @hide */
1040    public void setIpConfiguration(IpConfiguration ipConfiguration) {
1041        mIpConfiguration = ipConfiguration;
1042    }
1043
1044    /** @hide */
1045    public LinkProperties getLinkProperties() {
1046        return mIpConfiguration.linkProperties;
1047    }
1048
1049    /** @hide */
1050    public void setLinkProperties(LinkProperties linkProperties) {
1051        mIpConfiguration.linkProperties = linkProperties;
1052    }
1053
1054    /** @hide */
1055    public IpConfiguration.IpAssignment getIpAssignment() {
1056        return mIpConfiguration.ipAssignment;
1057    }
1058
1059    /** @hide */
1060    public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
1061        mIpConfiguration.ipAssignment = ipAssignment;
1062    }
1063
1064    /** @hide */
1065    public IpConfiguration.ProxySettings getProxySettings() {
1066        return mIpConfiguration.proxySettings;
1067    }
1068
1069    /** @hide */
1070    public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
1071        mIpConfiguration.proxySettings = proxySettings;
1072    }
1073
1074    /** @hide */
1075    public void setProxy(ProxySettings settings, ProxyInfo proxy) {
1076        mIpConfiguration.proxySettings = settings;
1077        mIpConfiguration.linkProperties.setHttpProxy(proxy);
1078    }
1079
1080    /** Implement the Parcelable interface {@hide} */
1081    public int describeContents() {
1082        return 0;
1083    }
1084
1085    /** copy constructor {@hide} */
1086    public WifiConfiguration(WifiConfiguration source) {
1087        if (source != null) {
1088            networkId = source.networkId;
1089            status = source.status;
1090            disableReason = source.disableReason;
1091            disableReason = source.disableReason;
1092            SSID = source.SSID;
1093            BSSID = source.BSSID;
1094            FQDN = source.FQDN;
1095            naiRealm = source.naiRealm;
1096            preSharedKey = source.preSharedKey;
1097
1098            wepKeys = new String[4];
1099            for (int i = 0; i < wepKeys.length; i++) {
1100                wepKeys[i] = source.wepKeys[i];
1101            }
1102
1103            wepTxKeyIndex = source.wepTxKeyIndex;
1104            priority = source.priority;
1105            hiddenSSID = source.hiddenSSID;
1106            allowedKeyManagement   = (BitSet) source.allowedKeyManagement.clone();
1107            allowedProtocols       = (BitSet) source.allowedProtocols.clone();
1108            allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
1109            allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
1110            allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
1111
1112            enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
1113
1114            defaultGwMacAddress = source.defaultGwMacAddress;
1115
1116            mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
1117
1118            if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) {
1119                scanResultCache = new HashMap<String, ScanResult>();
1120                scanResultCache.putAll(source.scanResultCache);
1121            }
1122
1123            if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
1124                connectChoices = new HashMap<String, Integer>();
1125                connectChoices.putAll(source.connectChoices);
1126            }
1127
1128            if ((source.linkedConfigurations != null)
1129                    && (source.linkedConfigurations.size() > 0)) {
1130                linkedConfigurations = new HashMap<String, Integer>();
1131                linkedConfigurations.putAll(source.linkedConfigurations);
1132            }
1133            mCachedConfigKey = null; //force null configKey
1134            autoJoinStatus = source.autoJoinStatus;
1135            selfAdded = source.selfAdded;
1136
1137            if (source.visibility != null) {
1138                visibility = new Visibility(source.visibility);
1139            }
1140
1141            lastFailure = source.lastFailure;
1142            didSelfAdd = source.didSelfAdd;
1143            lastConnectUid = source.lastConnectUid;
1144            lastUpdateUid = source.lastUpdateUid;
1145            bssidOwnerUid = source.bssidOwnerUid;
1146            creatorUid = source.creatorUid;
1147            peerWifiConfiguration = source.peerWifiConfiguration;
1148            blackListTimestamp = source.blackListTimestamp;
1149            lastConnected = source.lastConnected;
1150            lastDisconnected = source.lastDisconnected;
1151        }
1152    }
1153
1154    /** {@hide} */
1155    //public static final int NOTHING_TAG = 0;
1156    /** {@hide} */
1157    //public static final int SCAN_CACHE_TAG = 1;
1158
1159    /** Implement the Parcelable interface {@hide} */
1160    @Override
1161    public void writeToParcel(Parcel dest, int flags) {
1162        dest.writeInt(networkId);
1163        dest.writeInt(status);
1164        dest.writeInt(disableReason);
1165        dest.writeString(SSID);
1166        dest.writeString(BSSID);
1167        dest.writeString(FQDN);
1168        dest.writeString(naiRealm);
1169        dest.writeString(preSharedKey);
1170        for (String wepKey : wepKeys) {
1171            dest.writeString(wepKey);
1172        }
1173        dest.writeInt(wepTxKeyIndex);
1174        dest.writeInt(priority);
1175        dest.writeInt(hiddenSSID ? 1 : 0);
1176        dest.writeInt(requirePMF ? 1 : 0);
1177        dest.writeString(updateIdentifier);
1178
1179        writeBitSet(dest, allowedKeyManagement);
1180        writeBitSet(dest, allowedProtocols);
1181        writeBitSet(dest, allowedAuthAlgorithms);
1182        writeBitSet(dest, allowedPairwiseCiphers);
1183        writeBitSet(dest, allowedGroupCiphers);
1184
1185        dest.writeParcelable(enterpriseConfig, flags);
1186
1187        dest.writeParcelable(mIpConfiguration, flags);
1188        dest.writeString(dhcpServer);
1189        dest.writeString(defaultGwMacAddress);
1190        dest.writeInt(autoJoinStatus);
1191        dest.writeInt(selfAdded ? 1 : 0);
1192        dest.writeInt(didSelfAdd ? 1 : 0);
1193        dest.writeInt(creatorUid);
1194        dest.writeInt(lastConnectUid);
1195        dest.writeInt(lastUpdateUid);
1196        dest.writeInt(bssidOwnerUid);
1197        dest.writeLong(blackListTimestamp);
1198    }
1199
1200    /** Implement the Parcelable interface {@hide} */
1201    public static final Creator<WifiConfiguration> CREATOR =
1202        new Creator<WifiConfiguration>() {
1203            public WifiConfiguration createFromParcel(Parcel in) {
1204                WifiConfiguration config = new WifiConfiguration();
1205                config.networkId = in.readInt();
1206                config.status = in.readInt();
1207                config.disableReason = in.readInt();
1208                config.SSID = in.readString();
1209                config.BSSID = in.readString();
1210                config.FQDN = in.readString();
1211                config.naiRealm = in.readString();
1212                config.preSharedKey = in.readString();
1213                for (int i = 0; i < config.wepKeys.length; i++) {
1214                    config.wepKeys[i] = in.readString();
1215                }
1216                config.wepTxKeyIndex = in.readInt();
1217                config.priority = in.readInt();
1218                config.hiddenSSID = in.readInt() != 0;
1219                config.requirePMF = in.readInt() != 0;
1220                config.updateIdentifier = in.readString();
1221
1222                config.allowedKeyManagement   = readBitSet(in);
1223                config.allowedProtocols       = readBitSet(in);
1224                config.allowedAuthAlgorithms  = readBitSet(in);
1225                config.allowedPairwiseCiphers = readBitSet(in);
1226                config.allowedGroupCiphers    = readBitSet(in);
1227
1228                config.enterpriseConfig = in.readParcelable(null);
1229
1230                config.mIpConfiguration = in.readParcelable(null);
1231                config.dhcpServer = in.readString();
1232                config.defaultGwMacAddress = in.readString();
1233                config.autoJoinStatus = in.readInt();
1234                config.selfAdded = in.readInt() != 0;
1235                config.didSelfAdd = in.readInt() != 0;
1236                config.creatorUid = in.readInt();
1237                config.lastConnectUid = in.readInt();
1238                config.lastUpdateUid = in.readInt();
1239                config.bssidOwnerUid = in.readInt();
1240                config.blackListTimestamp = in.readLong();
1241                return config;
1242            }
1243
1244            public WifiConfiguration[] newArray(int size) {
1245                return new WifiConfiguration[size];
1246            }
1247        };
1248}
1249