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