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