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.content.pm.PackageManager;
21import android.net.IpConfiguration;
22import android.net.IpConfiguration.ProxySettings;
23import android.net.ProxyInfo;
24import android.net.StaticIpConfiguration;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.os.UserHandle;
28import android.text.TextUtils;
29import android.util.BackupUtils;
30
31import java.io.ByteArrayOutputStream;
32import java.io.DataInputStream;
33import java.io.DataOutputStream;
34import java.io.IOException;
35import java.util.Arrays;
36import java.util.BitSet;
37import java.util.HashMap;
38
39/**
40 * A class representing a configured Wi-Fi network, including the
41 * security configuration.
42 */
43public class WifiConfiguration implements Parcelable {
44    private static final String TAG = "WifiConfiguration";
45    /**
46     * Current Version of the Backup Serializer.
47    */
48    private static final int BACKUP_VERSION = 2;
49    /** {@hide} */
50    public static final String ssidVarName = "ssid";
51    /** {@hide} */
52    public static final String bssidVarName = "bssid";
53    /** {@hide} */
54    public static final String pskVarName = "psk";
55    /** {@hide} */
56    public static final String[] wepKeyVarNames = { "wep_key0", "wep_key1", "wep_key2", "wep_key3" };
57    /** {@hide} */
58    public static final String wepTxKeyIdxVarName = "wep_tx_keyidx";
59    /** {@hide} */
60    public static final String priorityVarName = "priority";
61    /** {@hide} */
62    public static final String hiddenSSIDVarName = "scan_ssid";
63    /** {@hide} */
64    public static final String pmfVarName = "ieee80211w";
65    /** {@hide} */
66    public static final String updateIdentiferVarName = "update_identifier";
67    /** {@hide} */
68    public static final int INVALID_NETWORK_ID = -1;
69
70    /** {@hide} */
71    private String mPasspointManagementObjectTree;
72
73    /**
74     * Recognized key management schemes.
75     */
76    public static class KeyMgmt {
77        private KeyMgmt() { }
78
79        /** WPA is not used; plaintext or static WEP could be used. */
80        public static final int NONE = 0;
81        /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */
82        public static final int WPA_PSK = 1;
83        /** WPA using EAP authentication. Generally used with an external authentication server. */
84        public static final int WPA_EAP = 2;
85        /** IEEE 802.1X using EAP authentication and (optionally) dynamically
86         * generated WEP keys. */
87        public static final int IEEE8021X = 3;
88
89        /** WPA2 pre-shared key for use with soft access point
90          * (requires {@code preSharedKey} to be specified).
91          * @hide
92          */
93        @SystemApi
94        public static final int WPA2_PSK = 4;
95        /**
96         * Hotspot 2.0 r2 OSEN:
97         * @hide
98         */
99        public static final int OSEN = 5;
100
101        public static final String varName = "key_mgmt";
102
103        public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X",
104                "WPA2_PSK", "OSEN" };
105    }
106
107    /**
108     * Recognized security protocols.
109     */
110    public static class Protocol {
111        private Protocol() { }
112
113        /** WPA/IEEE 802.11i/D3.0 */
114        public static final int WPA = 0;
115        /** WPA2/IEEE 802.11i */
116        public static final int RSN = 1;
117        /** HS2.0 r2 OSEN
118         * @hide
119         */
120        public static final int OSEN = 2;
121
122        public static final String varName = "proto";
123
124        public static final String[] strings = { "WPA", "RSN", "OSEN" };
125    }
126
127    /**
128     * Recognized IEEE 802.11 authentication algorithms.
129     */
130    public static class AuthAlgorithm {
131        private AuthAlgorithm() { }
132
133        /** Open System authentication (required for WPA/WPA2) */
134        public static final int OPEN = 0;
135        /** Shared Key authentication (requires static WEP keys) */
136        public static final int SHARED = 1;
137        /** LEAP/Network EAP (only used with LEAP) */
138        public static final int LEAP = 2;
139
140        public static final String varName = "auth_alg";
141
142        public static final String[] strings = { "OPEN", "SHARED", "LEAP" };
143    }
144
145    /**
146     * Recognized pairwise ciphers for WPA.
147     */
148    public static class PairwiseCipher {
149        private PairwiseCipher() { }
150
151        /** Use only Group keys (deprecated) */
152        public static final int NONE = 0;
153        /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
154        public static final int TKIP = 1;
155        /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
156        public static final int CCMP = 2;
157
158        public static final String varName = "pairwise";
159
160        public static final String[] strings = { "NONE", "TKIP", "CCMP" };
161    }
162
163    /**
164     * Recognized group ciphers.
165     * <pre>
166     * CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
167     * TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
168     * WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
169     * WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11)
170     * </pre>
171     */
172    public static class GroupCipher {
173        private GroupCipher() { }
174
175        /** WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11) */
176        public static final int WEP40 = 0;
177        /** WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key */
178        public static final int WEP104 = 1;
179        /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
180        public static final int TKIP = 2;
181        /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
182        public static final int CCMP = 3;
183        /** Hotspot 2.0 r2 OSEN
184         * @hide
185         */
186        public static final int GTK_NOT_USED = 4;
187
188        public static final String varName = "group";
189
190        public static final String[] strings =
191                { "WEP40", "WEP104", "TKIP", "CCMP", "GTK_NOT_USED" };
192    }
193
194    /** Possible status of a network configuration. */
195    public static class Status {
196        private Status() { }
197
198        /** this is the network we are currently connected to */
199        public static final int CURRENT = 0;
200        /** supplicant will not attempt to use this network */
201        public static final int DISABLED = 1;
202        /** supplicant will consider this network available for association */
203        public static final int ENABLED = 2;
204
205        public static final String[] strings = { "current", "disabled", "enabled" };
206    }
207
208    /** @hide */
209    public static final int UNKNOWN_UID = -1;
210
211    /**
212     * The ID number that the supplicant uses to identify this
213     * network configuration entry. This must be passed as an argument
214     * to most calls into the supplicant.
215     */
216    public int networkId;
217
218    /**
219     * The current status of this network configuration entry.
220     * Fixme We need remove this field to use only Quality network selection status only
221     * @see Status
222     */
223    public int status;
224
225    /**
226     * The network's SSID. Can either be an ASCII string,
227     * which must be enclosed in double quotation marks
228     * (e.g., {@code "MyNetwork"}, or a string of
229     * hex digits,which are not enclosed in quotes
230     * (e.g., {@code 01a243f405}).
231     */
232    public String SSID;
233    /**
234     * When set, this network configuration entry should only be used when
235     * associating with the AP having the specified BSSID. The value is
236     * a string in the format of an Ethernet MAC address, e.g.,
237     * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
238     */
239    public String BSSID;
240
241    /**
242     * 2GHz band.
243     * @hide
244     */
245    public static final int AP_BAND_2GHZ = 0;
246
247    /**
248     * 5GHz band.
249     * @hide
250     */
251    public static final int AP_BAND_5GHZ = 1;
252
253    /**
254     * The band which AP resides on
255     * 0-2G  1-5G
256     * By default, 2G is chosen
257     * @hide
258     */
259    public int apBand = AP_BAND_2GHZ;
260
261    /**
262     * The channel which AP resides on,currently, US only
263     * 2G  1-11
264     * 5G  36,40,44,48,149,153,157,161,165
265     * 0 - find a random available channel according to the apBand
266     * @hide
267     */
268    public int apChannel = 0;
269
270    /**
271     * Pre-shared key for use with WPA-PSK.
272     * <p/>
273     * When the value of this key is read, the actual key is
274     * not returned, just a "*" if the key has a value, or the null
275     * string otherwise.
276     */
277    public String preSharedKey;
278    /**
279     * Up to four WEP keys. Either an ASCII string enclosed in double
280     * quotation marks (e.g., {@code "abcdef"} or a string
281     * of hex digits (e.g., {@code 0102030405}).
282     * <p/>
283     * When the value of one of these keys is read, the actual key is
284     * not returned, just a "*" if the key has a value, or the null
285     * string otherwise.
286     */
287    public String[] wepKeys;
288
289    /** Default WEP key index, ranging from 0 to 3. */
290    public int wepTxKeyIndex;
291
292    /**
293     * Priority determines the preference given to a network by {@code wpa_supplicant}
294     * when choosing an access point with which to associate.
295     */
296    public int priority;
297
298    /**
299     * This is a network that does not broadcast its SSID, so an
300     * SSID-specific probe request must be used for scans.
301     */
302    public boolean hiddenSSID;
303
304    /**
305     * This is a network that requries Protected Management Frames (PMF).
306     * @hide
307     */
308    public boolean requirePMF;
309
310    /**
311     * Update identifier, for Passpoint network.
312     * @hide
313     */
314    public String updateIdentifier;
315
316    /**
317     * The set of key management protocols supported by this configuration.
318     * See {@link KeyMgmt} for descriptions of the values.
319     * Defaults to WPA-PSK WPA-EAP.
320     */
321    public BitSet allowedKeyManagement;
322    /**
323     * The set of security protocols supported by this configuration.
324     * See {@link Protocol} for descriptions of the values.
325     * Defaults to WPA RSN.
326     */
327    public BitSet allowedProtocols;
328    /**
329     * The set of authentication protocols supported by this configuration.
330     * See {@link AuthAlgorithm} for descriptions of the values.
331     * Defaults to automatic selection.
332     */
333    public BitSet allowedAuthAlgorithms;
334    /**
335     * The set of pairwise ciphers for WPA supported by this configuration.
336     * See {@link PairwiseCipher} for descriptions of the values.
337     * Defaults to CCMP TKIP.
338     */
339    public BitSet allowedPairwiseCiphers;
340    /**
341     * The set of group ciphers supported by this configuration.
342     * See {@link GroupCipher} for descriptions of the values.
343     * Defaults to CCMP TKIP WEP104 WEP40.
344     */
345    public BitSet allowedGroupCiphers;
346    /**
347     * The enterprise configuration details specifying the EAP method,
348     * certificates and other settings associated with the EAP.
349     */
350    public WifiEnterpriseConfig enterpriseConfig;
351
352    /**
353     * Fully qualified domain name of a passpoint configuration
354     */
355    public String FQDN;
356
357    /**
358     * Name of passpoint credential provider
359     */
360    public String providerFriendlyName;
361
362    /**
363     * Roaming Consortium Id list for passpoint credential; identifies a set of networks where
364     * passpoint credential will be considered valid
365     */
366    public long[] roamingConsortiumIds;
367
368    /**
369     * @hide
370     * This network configuration is visible to and usable by other users on the
371     * same device.
372     */
373    public boolean shared;
374
375    /**
376     * @hide
377     */
378    private IpConfiguration mIpConfiguration;
379
380    /**
381     * @hide
382     * dhcp server MAC address if known
383     */
384    public String dhcpServer;
385
386    /**
387     * @hide
388     * default Gateway MAC address if known
389     */
390    public String defaultGwMacAddress;
391
392    /**
393     * @hide
394     * last failure
395     */
396    public String lastFailure;
397
398    /**
399     * @hide
400     * last time we connected, this configuration had validated internet access
401     */
402    public boolean validatedInternetAccess;
403
404    /**
405     * @hide
406     * The number of beacon intervals between Delivery Traffic Indication Maps (DTIM)
407     * This value is populated from scan results that contain Beacon Frames, which are infrequent.
408     * The value is not guaranteed to be set or current (Although it SHOULDNT change once set)
409     * Valid values are from 1 - 255. Initialized here as 0, use this to check if set.
410     */
411    public int dtimInterval = 0;
412
413    /**
414     * @hide
415     * Uid of app creating the configuration
416     */
417    @SystemApi
418    public int creatorUid;
419
420    /**
421     * @hide
422     * Uid of last app issuing a connection related command
423     */
424    public int lastConnectUid;
425
426    /**
427     * @hide
428     * Uid of last app modifying the configuration
429     */
430    @SystemApi
431    public int lastUpdateUid;
432
433    /**
434     * @hide
435     * Universal name for app creating the configuration
436     *    see {#link {@link PackageManager#getNameForUid(int)}
437     */
438    @SystemApi
439    public String creatorName;
440
441    /**
442     * @hide
443     * Universal name for app updating the configuration
444     *    see {#link {@link PackageManager#getNameForUid(int)}
445     */
446    @SystemApi
447    public String lastUpdateName;
448
449    /**
450     * @hide
451     * Status of user approval for connection
452     */
453    public int userApproved = USER_UNSPECIFIED;
454
455    /** The Below RSSI thresholds are used to configure AutoJoin
456     *  - GOOD/LOW/BAD thresholds are used so as to calculate link score
457     *  - UNWANTED_SOFT are used by the blacklisting logic so as to handle
458     *  the unwanted network message coming from CS
459     *  - UNBLACKLIST thresholds are used so as to tweak the speed at which
460     *  the network is unblacklisted (i.e. if
461     *          it is seen with good RSSI, it is blacklisted faster)
462     *  - INITIAL_AUTOJOIN_ATTEMPT, used to determine how close from
463     *  the network we need to be before autojoin kicks in
464     */
465    /** @hide **/
466    public static int INVALID_RSSI = -127;
467
468    /**
469     * @hide
470     * A summary of the RSSI and Band status for that configuration
471     * This is used as a temporary value by the auto-join controller
472     */
473    public static final class Visibility {
474        public int rssi5;   // strongest 5GHz RSSI
475        public int rssi24;  // strongest 2.4GHz RSSI
476        public int num5;    // number of BSSIDs on 5GHz
477        public int num24;   // number of BSSIDs on 2.4GHz
478        public long age5;   // timestamp of the strongest 5GHz BSSID (last time it was seen)
479        public long age24;  // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
480        public String BSSID24;
481        public String BSSID5;
482        public int score; // Debug only, indicate last score used for autojoin/cell-handover
483        public int currentNetworkBoost; // Debug only, indicate boost applied to RSSI if current
484        public int bandPreferenceBoost; // Debug only, indicate boost applied to RSSI if current
485        public int lastChoiceBoost; // Debug only, indicate last choice applied to this configuration
486        public String lastChoiceConfig; // Debug only, indicate last choice applied to this configuration
487
488        public Visibility() {
489            rssi5 = INVALID_RSSI;
490            rssi24 = INVALID_RSSI;
491        }
492
493        public Visibility(Visibility source) {
494            rssi5 = source.rssi5;
495            rssi24 = source.rssi24;
496            age24 = source.age24;
497            age5 = source.age5;
498            num24 = source.num24;
499            num5 = source.num5;
500            BSSID5 = source.BSSID5;
501            BSSID24 = source.BSSID24;
502        }
503
504        @Override
505        public String toString() {
506            StringBuilder sbuf = new StringBuilder();
507            sbuf.append("[");
508            if (rssi24 > INVALID_RSSI) {
509                sbuf.append(Integer.toString(rssi24));
510                sbuf.append(",");
511                sbuf.append(Integer.toString(num24));
512                if (BSSID24 != null) sbuf.append(",").append(BSSID24);
513            }
514            sbuf.append("; ");
515            if (rssi5 > INVALID_RSSI) {
516                sbuf.append(Integer.toString(rssi5));
517                sbuf.append(",");
518                sbuf.append(Integer.toString(num5));
519                if (BSSID5 != null) sbuf.append(",").append(BSSID5);
520            }
521            if (score != 0) {
522                sbuf.append("; ").append(score);
523                sbuf.append(", ").append(currentNetworkBoost);
524                sbuf.append(", ").append(bandPreferenceBoost);
525                if (lastChoiceConfig != null) {
526                    sbuf.append(", ").append(lastChoiceBoost);
527                    sbuf.append(", ").append(lastChoiceConfig);
528                }
529            }
530            sbuf.append("]");
531            return sbuf.toString();
532        }
533    }
534
535    /** @hide
536     * Cache the visibility status of this configuration.
537     * Visibility can change at any time depending on scan results availability.
538     * Owner of the WifiConfiguration is responsible to set this field based on
539     * recent scan results.
540     ***/
541    public Visibility visibility;
542
543    /** @hide
544     * calculate and set Visibility for that configuration.
545     *
546     * age in milliseconds: we will consider only ScanResults that are more recent,
547     * i.e. younger.
548     ***/
549    public void setVisibility(Visibility status) {
550        visibility = status;
551    }
552
553    // States for the userApproved field
554    /**
555     * @hide
556     * User hasn't specified if connection is okay
557     */
558    public static final int USER_UNSPECIFIED = 0;
559    /**
560     * @hide
561     * User has approved this for connection
562     */
563    public static final int USER_APPROVED = 1;
564    /**
565     * @hide
566     * User has banned this from connection
567     */
568    public static final int USER_BANNED = 2;
569    /**
570     * @hide
571     * Waiting for user input
572     */
573    public static final int USER_PENDING = 3;
574
575    /**
576     * @hide
577     * Number of reports indicating no Internet Access
578     */
579    public int numNoInternetAccessReports;
580
581    /**
582     * @hide
583     * For debug: date at which the config was last updated
584     */
585    public String updateTime;
586
587    /**
588     * @hide
589     * For debug: date at which the config was last updated
590     */
591    public String creationTime;
592
593    /**
594     * @hide
595     * The WiFi configuration is considered to have no internet access for purpose of autojoining
596     * if there has been a report of it having no internet access, and, it never have had
597     * internet access in the past.
598     */
599    public boolean hasNoInternetAccess() {
600        return numNoInternetAccessReports > 0 && !validatedInternetAccess;
601    }
602
603    /**
604     * The WiFi configuration is expected not to have Internet access (e.g., a wireless printer, a
605     * Chromecast hotspot, etc.). This will be set if the user explicitly confirms a connection to
606     * this configuration and selects "don't ask again".
607     * @hide
608     */
609    public boolean noInternetAccessExpected;
610
611    /**
612     * @hide
613     * Last time the system was connected to this configuration.
614     */
615    public long lastConnected;
616
617    /**
618     * @hide
619     * Last time the system tried to connect and failed.
620     */
621    public long lastConnectionFailure;
622
623    /**
624     * @hide
625     * Last time the system tried to roam and failed because of authentication failure or DHCP
626     * RENEW failure.
627     */
628    public long lastRoamingFailure;
629
630    /** @hide */
631    public static int ROAMING_FAILURE_IP_CONFIG = 1;
632    /** @hide */
633    public static int ROAMING_FAILURE_AUTH_FAILURE = 2;
634
635    /**
636     * @hide
637     * Initial amount of time this Wifi configuration gets blacklisted for network switching
638     * because of roaming failure
639     */
640    public long roamingFailureBlackListTimeMilli = 1000;
641
642    /**
643     * @hide
644     * Last roaming failure reason code
645     */
646    public int lastRoamingFailureReason;
647
648    /**
649     * @hide
650     * Last time the system was disconnected to this configuration.
651     */
652    public long lastDisconnected;
653
654    /**
655     * Set if the configuration was self added by the framework
656     * This boolean is cleared if we get a connect/save/ update or
657     * any wifiManager command that indicate the user interacted with the configuration
658     * since we will now consider that the configuration belong to him.
659     * @hide
660     */
661    public boolean selfAdded;
662
663    /**
664     * Set if the configuration was self added by the framework
665     * This boolean is set once and never cleared. It is used
666     * so as we never loose track of who created the
667     * configuration in the first place.
668     * @hide
669     */
670    public boolean didSelfAdd;
671
672    /**
673     * Peer WifiConfiguration this WifiConfiguration was added for
674     * @hide
675     */
676    public String peerWifiConfiguration;
677
678    /**
679     * @hide
680     * Indicate that a WifiConfiguration is temporary and should not be saved
681     * nor considered by AutoJoin.
682     */
683    public boolean ephemeral;
684
685    /**
686     * @hide
687     * A hint about whether or not the network represented by this WifiConfiguration
688     * is metered.
689     */
690    public boolean meteredHint;
691
692    /**
693     * @hide
694     * Setting this value will force scan results associated with this configuration to
695     * be included in the bucket of networks that are externally scored.
696     * If not set, associated scan results will be treated as legacy saved networks and
697     * will take precedence over networks in the scored category.
698     */
699    @SystemApi
700    public boolean useExternalScores;
701
702    /**
703     * @hide
704     * Number of time the scorer overrode a the priority based choice, when comparing two
705     * WifiConfigurations, note that since comparing WifiConfiguration happens very often
706     * potentially at every scan, this number might become very large, even on an idle
707     * system.
708     */
709    @SystemApi
710    public int numScorerOverride;
711
712    /**
713     * @hide
714     * Number of time the scorer overrode a the priority based choice, and the comparison
715     * triggered a network switch
716     */
717    @SystemApi
718    public int numScorerOverrideAndSwitchedNetwork;
719
720    /**
721     * @hide
722     * Number of time we associated to this configuration.
723     */
724    @SystemApi
725    public int numAssociation;
726
727    /**
728     * @hide
729     * Number of time user disabled WiFi while associated to this configuration with Low RSSI.
730     */
731    public int numUserTriggeredWifiDisableLowRSSI;
732
733    /**
734     * @hide
735     * Number of time user disabled WiFi while associated to this configuration with Bad RSSI.
736     */
737    public int numUserTriggeredWifiDisableBadRSSI;
738
739    /**
740     * @hide
741     * Number of time user disabled WiFi while associated to this configuration
742     * and RSSI was not HIGH.
743     */
744    public int numUserTriggeredWifiDisableNotHighRSSI;
745
746    /**
747     * @hide
748     * Number of ticks associated to this configuration with Low RSSI.
749     */
750    public int numTicksAtLowRSSI;
751
752    /**
753     * @hide
754     * Number of ticks associated to this configuration with Bad RSSI.
755     */
756    public int numTicksAtBadRSSI;
757
758    /**
759     * @hide
760     * Number of ticks associated to this configuration
761     * and RSSI was not HIGH.
762     */
763    public int numTicksAtNotHighRSSI;
764    /**
765     * @hide
766     * Number of time user (WifiManager) triggered association to this configuration.
767     * TODO: count this only for Wifi Settings uuid, so as to not count 3rd party apps
768     */
769    public int numUserTriggeredJoinAttempts;
770
771    /** @hide
772     * Boost given to RSSI on a home network for the purpose of calculating the score
773     * This adds stickiness to home networks, as defined by:
774     * - less than 4 known BSSIDs
775     * - PSK only
776     * - TODO: add a test to verify that all BSSIDs are behind same gateway
777     ***/
778    public static final int HOME_NETWORK_RSSI_BOOST = 5;
779
780    /**
781     * @hide
782     * This class is used to contain all the information and API used for quality network selection
783     */
784    public static class NetworkSelectionStatus {
785        /**
786         * Quality Network Selection Status enable, temporary disabled, permanently disabled
787         */
788        /**
789         * This network is allowed to join Quality Network Selection
790         */
791        public static final int NETWORK_SELECTION_ENABLED = 0;
792        /**
793         * network was temporary disabled. Can be re-enabled after a time period expire
794         */
795        public static final int NETWORK_SELECTION_TEMPORARY_DISABLED  = 1;
796        /**
797         * network was permanently disabled.
798         */
799        public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED  = 2;
800        /**
801         * Maximum Network selection status
802         */
803        public static final int NETWORK_SELECTION_STATUS_MAX = 3;
804
805        /**
806         * Quality network selection status String (for debug purpose). Use Quality network
807         * selection status value as index to extec the corresponding debug string
808         */
809        private static final String[] QUALITY_NETWORK_SELECTION_STATUS = {
810                "NETWORK_SELECTION_ENABLED",
811                "NETWORK_SELECTION_TEMPORARY_DISABLED",
812                "NETWORK_SELECTION_PERMANENTLY_DISABLED"};
813
814        //Quality Network disabled reasons
815        /**
816         * Default value. Means not disabled
817         */
818        public static final int NETWORK_SELECTION_ENABLE = 0;
819        /**
820         * This network is disabled because higher layer (>2) network is bad
821         */
822        public static final int DISABLED_BAD_LINK = 1;
823        /**
824         * This network is disabled because multiple association rejects
825         */
826        public static final int DISABLED_ASSOCIATION_REJECTION = 2;
827        /**
828         * This network is disabled because multiple authentication failure
829         */
830        public static final int DISABLED_AUTHENTICATION_FAILURE = 3;
831        /**
832         * This network is disabled because multiple DHCP failure
833         */
834        public static final int DISABLED_DHCP_FAILURE = 4;
835        /**
836         * This network is disabled because of security network but no credentials
837         */
838        public static final int DISABLED_DNS_FAILURE = 5;
839        /**
840         * This network is disabled because EAP-TLS failure
841         */
842        public static final int DISABLED_TLS_VERSION_MISMATCH = 6;
843        /**
844         * This network is disabled due to WifiManager disable it explicitly
845         */
846        public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 7;
847        /**
848         * This network is disabled because no Internet connected and user do not want
849         */
850        public static final int DISABLED_NO_INTERNET = 8;
851        /**
852         * This network is disabled due to WifiManager disable it explicitly
853         */
854        public static final int DISABLED_BY_WIFI_MANAGER = 9;
855        /**
856         * This Maximum disable reason value
857         */
858        public static final int NETWORK_SELECTION_DISABLED_MAX = 10;
859
860        /**
861         * Quality network selection disable reason String (for debug purpose)
862         */
863        private static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
864                "NETWORK_SELECTION_ENABLE",
865                "NETWORK_SELECTION_DISABLED_BAD_LINK",
866                "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ",
867                "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE",
868                "NETWORK_SELECTION_DISABLED_DHCP_FAILURE",
869                "NETWORK_SELECTION_DISABLED_DNS_FAILURE",
870                "NETWORK_SELECTION_DISABLED_TLS_VERSION",
871                "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS",
872                "NETWORK_SELECTION_DISABLED_NO_INTERNET",
873                "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER"};
874
875        /**
876         * Invalid time stamp for network selection disable
877         */
878        public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L;
879
880        /**
881         *  This constant indicates the current configuration has connect choice set
882         */
883        private static final int CONNECT_CHOICE_EXISTS = 1;
884
885        /**
886         *  This constant indicates the current configuration does not have connect choice set
887         */
888        private static final int CONNECT_CHOICE_NOT_EXISTS = -1;
889
890        // fields for QualityNetwork Selection
891        /**
892         * Network selection status, should be in one of three status: enable, temporaily disabled
893         * or permanently disabled
894         */
895        private int mStatus;
896
897        /**
898         * Reason for disable this network
899         */
900        private int mNetworkSelectionDisableReason;
901
902        /**
903         * Last time we temporarily disabled the configuration
904         */
905        private long mTemporarilyDisabledTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
906
907        /**
908         * counter for each Network selection disable reason
909         */
910        private int[] mNetworkSeclectionDisableCounter = new int[NETWORK_SELECTION_DISABLED_MAX];
911
912        /**
913         * Connect Choice over this configuration
914         *
915         * When current wifi configuration is visible to the user but user explicitly choose to
916         * connect to another network X, the another networks X's configure key will be stored here.
917         * We will consider user has a preference of X over this network. And in the future,
918         * network selection will always give X a higher preference over this configuration.
919         * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
920         */
921        private String mConnectChoice;
922
923        /**
924         * The system timestamp when we records the connectChoice. This value is obtained from
925         * System.currentTimeMillis
926         */
927        private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
928
929        /**
930         * Used to cache the temporary candidate during the network selection procedure. It will be
931         * kept updating once a new scan result has a higher score than current one
932         */
933        private ScanResult mCandidate;
934
935        /**
936         * Used to cache the score of the current temporary candidate during the network
937         * selection procedure.
938         */
939        private int mCandidateScore;
940
941        /**
942         * Indicate whether this network is visible in latest Qualified Network Selection. This
943         * means there is scan result found related to this Configuration and meet the minimum
944         * requirement. The saved network need not join latest Qualified Network Selection. For
945         * example, it is disabled. True means network is visible in latest Qualified Network
946         * Selection and false means network is invisible
947         */
948        private boolean mSeenInLastQualifiedNetworkSelection;
949
950        /**
951         * Boolean indicating if we have ever successfully connected to this network.
952         *
953         * This value will be set to true upon a successful connection.
954         * This value will be set to false if a previous value was not stored in the config or if
955         * the credentials are updated (ex. a password change).
956         */
957        private boolean mHasEverConnected;
958
959        /**
960         * set whether this network is visible in latest Qualified Network Selection
961         * @param seen value set to candidate
962         */
963        public void setSeenInLastQualifiedNetworkSelection(boolean seen) {
964            mSeenInLastQualifiedNetworkSelection =  seen;
965        }
966
967        /**
968         * get whether this network is visible in latest Qualified Network Selection
969         * @return returns true -- network is visible in latest Qualified Network Selection
970         *         false -- network is invisible in latest Qualified Network Selection
971         */
972        public boolean getSeenInLastQualifiedNetworkSelection() {
973            return mSeenInLastQualifiedNetworkSelection;
974        }
975        /**
976         * set the temporary candidate of current network selection procedure
977         * @param scanCandidate {@link ScanResult} the candidate set to mCandidate
978         */
979        public void setCandidate(ScanResult scanCandidate) {
980            mCandidate = scanCandidate;
981        }
982
983        /**
984         * get the temporary candidate of current network selection procedure
985         * @return  returns {@link ScanResult} temporary candidate of current network selection
986         * procedure
987         */
988        public ScanResult getCandidate() {
989            return mCandidate;
990        }
991
992        /**
993         * set the score of the temporary candidate of current network selection procedure
994         * @param score value set to mCandidateScore
995         */
996        public void setCandidateScore(int score) {
997            mCandidateScore = score;
998        }
999
1000        /**
1001         * get the score of the temporary candidate of current network selection procedure
1002         * @return returns score of the temporary candidate of current network selection procedure
1003         */
1004        public int getCandidateScore() {
1005            return mCandidateScore;
1006        }
1007
1008        /**
1009         * get user preferred choice over this configuration
1010         *@return returns configKey of user preferred choice over this configuration
1011         */
1012        public String getConnectChoice() {
1013            return mConnectChoice;
1014        }
1015
1016        /**
1017         * set user preferred choice over this configuration
1018         * @param newConnectChoice, the configKey of user preferred choice over this configuration
1019         */
1020        public void setConnectChoice(String newConnectChoice) {
1021            mConnectChoice = newConnectChoice;
1022        }
1023
1024        /**
1025         * get the timeStamp when user select a choice over this configuration
1026         * @return returns when current connectChoice is set (time from System.currentTimeMillis)
1027         */
1028        public long getConnectChoiceTimestamp() {
1029            return mConnectChoiceTimestamp;
1030        }
1031
1032        /**
1033         * set the timeStamp when user select a choice over this configuration
1034         * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should
1035         *        be obtained from System.currentTimeMillis
1036         */
1037        public void setConnectChoiceTimestamp(long timeStamp) {
1038            mConnectChoiceTimestamp = timeStamp;
1039        }
1040
1041        /**
1042         * get current Quality network selection status
1043         * @return returns current Quality network selection status in String (for debug purpose)
1044         */
1045        public String getNetworkStatusString() {
1046            return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
1047        }
1048
1049        public void setHasEverConnected(boolean value) {
1050            mHasEverConnected = value;
1051        }
1052
1053        public boolean getHasEverConnected() {
1054            return mHasEverConnected;
1055        }
1056
1057        private NetworkSelectionStatus() {
1058            // previously stored configs will not have this parameter, so we default to false.
1059            mHasEverConnected = false;
1060        };
1061
1062        /**
1063         * @param reason specific error reason
1064         * @return  corresponding network disable reason String (for debug purpose)
1065         */
1066        public static String getNetworkDisableReasonString(int reason) {
1067            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1068                return QUALITY_NETWORK_SELECTION_DISABLE_REASON[reason];
1069            } else {
1070                return null;
1071            }
1072        }
1073        /**
1074         * get current network disable reason
1075         * @return current network disable reason in String (for debug purpose)
1076         */
1077        public String getNetworkDisableReasonString() {
1078            return QUALITY_NETWORK_SELECTION_DISABLE_REASON[mNetworkSelectionDisableReason];
1079        }
1080
1081        /**
1082         * get current network network selection status
1083         * @return return current network network selection status
1084         */
1085        public int getNetworkSelectionStatus() {
1086            return mStatus;
1087        }
1088        /**
1089         * @return whether current network is enabled to join network selection
1090         */
1091        public boolean isNetworkEnabled() {
1092            return mStatus == NETWORK_SELECTION_ENABLED;
1093        }
1094
1095        /**
1096         * @return whether current network is temporary disabled
1097         */
1098        public boolean isNetworkTemporaryDisabled() {
1099            return mStatus == NETWORK_SELECTION_TEMPORARY_DISABLED;
1100        }
1101
1102        /**
1103         * @return returns whether current network is permanently disabled
1104         */
1105        public boolean isNetworkPermanentlyDisabled() {
1106            return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED;
1107        }
1108
1109        /**
1110         * set current networ work selection status
1111         * @param status network selection status to set
1112         */
1113        public void setNetworkSelectionStatus(int status) {
1114            if (status >= 0 && status < NETWORK_SELECTION_STATUS_MAX) {
1115                mStatus = status;
1116            }
1117        }
1118
1119        /**
1120         * @return returns current network's disable reason
1121         */
1122        public int getNetworkSelectionDisableReason() {
1123            return mNetworkSelectionDisableReason;
1124        }
1125
1126        /**
1127         * set Network disable reason
1128         * @param  reason Network disable reason
1129         */
1130        public void setNetworkSelectionDisableReason(int reason) {
1131            if (reason >= 0 && reason < NETWORK_SELECTION_DISABLED_MAX) {
1132                mNetworkSelectionDisableReason = reason;
1133            } else {
1134                throw new IllegalArgumentException("Illegal reason value: " + reason);
1135            }
1136        }
1137
1138        /**
1139         * check whether network is disabled by this reason
1140         * @param reason a specific disable reason
1141         * @return true -- network is disabled for this reason
1142         *         false -- network is not disabled for this reason
1143         */
1144        public boolean isDisabledByReason(int reason) {
1145            return mNetworkSelectionDisableReason == reason;
1146        }
1147
1148        /**
1149         * @param timeStamp Set when current network is disabled in millisecond since January 1,
1150         * 1970 00:00:00.0 UTC
1151         */
1152        public void setDisableTime(long timeStamp) {
1153            mTemporarilyDisabledTimestamp = timeStamp;
1154        }
1155
1156        /**
1157         * @return returns when current network is disabled in millisecond since January 1,
1158         * 1970 00:00:00.0 UTC
1159         */
1160        public long getDisableTime() {
1161            return mTemporarilyDisabledTimestamp;
1162        }
1163
1164        /**
1165         * get the disable counter of a specific reason
1166         * @param  reason specific failure reason
1167         * @exception throw IllegalArgumentException for illegal input
1168         * @return counter number for specific error reason.
1169         */
1170        public int getDisableReasonCounter(int reason) {
1171            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1172                return mNetworkSeclectionDisableCounter[reason];
1173            } else {
1174                throw new IllegalArgumentException("Illegal reason value: " + reason);
1175            }
1176        }
1177
1178        /**
1179         * set the counter of a specific failure reason
1180         * @param reason reason for disable error
1181         * @param value the counter value for this specific reason
1182         * @exception throw IllegalArgumentException for illegal input
1183         */
1184        public void setDisableReasonCounter(int reason, int value) {
1185            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1186                mNetworkSeclectionDisableCounter[reason] = value;
1187            } else {
1188                throw new IllegalArgumentException("Illegal reason value: " + reason);
1189            }
1190        }
1191
1192        /**
1193         * increment the counter of a specific failure reason
1194         * @param reason a specific failure reason
1195         * @exception throw IllegalArgumentException for illegal input
1196         */
1197        public void incrementDisableReasonCounter(int reason) {
1198            if (reason >= NETWORK_SELECTION_ENABLE  && reason < NETWORK_SELECTION_DISABLED_MAX) {
1199                mNetworkSeclectionDisableCounter[reason]++;
1200            } else {
1201                throw new IllegalArgumentException("Illegal reason value: " + reason);
1202            }
1203        }
1204
1205        /**
1206         * clear the counter of a specific failure reason
1207         * @hide
1208         * @param reason a specific failure reason
1209         * @exception throw IllegalArgumentException for illegal input
1210         */
1211        public void clearDisableReasonCounter(int reason) {
1212            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1213                mNetworkSeclectionDisableCounter[reason] = NETWORK_SELECTION_ENABLE;
1214            } else {
1215                throw new IllegalArgumentException("Illegal reason value: " + reason);
1216            }
1217        }
1218
1219        /**
1220         * clear all the failure reason counters
1221         */
1222        public void clearDisableReasonCounter() {
1223            Arrays.fill(mNetworkSeclectionDisableCounter, NETWORK_SELECTION_ENABLE);
1224        }
1225
1226        /**
1227         * BSSID for connection to this network (through network selection procedure)
1228         */
1229        private String mNetworkSelectionBSSID;
1230
1231        /**
1232         * get current network Selection BSSID
1233         * @return current network Selection BSSID
1234         */
1235        public String getNetworkSelectionBSSID() {
1236            return mNetworkSelectionBSSID;
1237        }
1238
1239        /**
1240         * set network Selection BSSID
1241         * @param bssid The target BSSID for assocaition
1242         */
1243        public void setNetworkSelectionBSSID(String bssid) {
1244            mNetworkSelectionBSSID = bssid;
1245        }
1246
1247        public void copy(NetworkSelectionStatus source) {
1248            mStatus = source.mStatus;
1249            mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason;
1250            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
1251                    index++) {
1252                mNetworkSeclectionDisableCounter[index] =
1253                        source.mNetworkSeclectionDisableCounter[index];
1254            }
1255            mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
1256            mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
1257            setConnectChoice(source.getConnectChoice());
1258            setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
1259            setHasEverConnected(source.getHasEverConnected());
1260        }
1261
1262        public void writeToParcel(Parcel dest) {
1263            dest.writeInt(getNetworkSelectionStatus());
1264            dest.writeInt(getNetworkSelectionDisableReason());
1265            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
1266                    index++) {
1267                dest.writeInt(getDisableReasonCounter(index));
1268            }
1269            dest.writeLong(getDisableTime());
1270            dest.writeString(getNetworkSelectionBSSID());
1271            if (getConnectChoice() != null) {
1272                dest.writeInt(CONNECT_CHOICE_EXISTS);
1273                dest.writeString(getConnectChoice());
1274                dest.writeLong(getConnectChoiceTimestamp());
1275            } else {
1276                dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
1277            }
1278            dest.writeInt(getHasEverConnected() ? 1 : 0);
1279        }
1280
1281        public void readFromParcel(Parcel in) {
1282            setNetworkSelectionStatus(in.readInt());
1283            setNetworkSelectionDisableReason(in.readInt());
1284            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
1285                    index++) {
1286                setDisableReasonCounter(index, in.readInt());
1287            }
1288            setDisableTime(in.readLong());
1289            setNetworkSelectionBSSID(in.readString());
1290            if (in.readInt() == CONNECT_CHOICE_EXISTS) {
1291                setConnectChoice(in.readString());
1292                setConnectChoiceTimestamp(in.readLong());
1293            } else {
1294                setConnectChoice(null);
1295                setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1296            }
1297            setHasEverConnected(in.readInt() != 0);
1298        }
1299    }
1300
1301    /**
1302     * @hide
1303     * network selection related member
1304     */
1305    private final NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
1306
1307    /**
1308     * @hide
1309     * @return network selection status
1310     */
1311    public NetworkSelectionStatus getNetworkSelectionStatus() {
1312        return mNetworkSelectionStatus;
1313    }
1314    /**
1315     * @hide
1316     * Linked Configurations: represent the set of Wificonfigurations that are equivalent
1317     * regarding roaming and auto-joining.
1318     * The linked configuration may or may not have same SSID, and may or may not have same
1319     * credentials.
1320     * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server.
1321     */
1322    public HashMap<String, Integer>  linkedConfigurations;
1323
1324    public WifiConfiguration() {
1325        networkId = INVALID_NETWORK_ID;
1326        SSID = null;
1327        BSSID = null;
1328        FQDN = null;
1329        roamingConsortiumIds = new long[0];
1330        priority = 0;
1331        hiddenSSID = false;
1332        allowedKeyManagement = new BitSet();
1333        allowedProtocols = new BitSet();
1334        allowedAuthAlgorithms = new BitSet();
1335        allowedPairwiseCiphers = new BitSet();
1336        allowedGroupCiphers = new BitSet();
1337        wepKeys = new String[4];
1338        for (int i = 0; i < wepKeys.length; i++) {
1339            wepKeys[i] = null;
1340        }
1341        enterpriseConfig = new WifiEnterpriseConfig();
1342        selfAdded = false;
1343        didSelfAdd = false;
1344        ephemeral = false;
1345        meteredHint = false;
1346        useExternalScores = false;
1347        validatedInternetAccess = false;
1348        mIpConfiguration = new IpConfiguration();
1349        lastUpdateUid = -1;
1350        creatorUid = -1;
1351        shared = true;
1352        dtimInterval = 0;
1353    }
1354
1355    /**
1356     * Identify if this configuration represents a passpoint network
1357     */
1358    public boolean isPasspoint() {
1359        return !TextUtils.isEmpty(FQDN)
1360                && !TextUtils.isEmpty(providerFriendlyName)
1361                && enterpriseConfig != null
1362                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
1363    }
1364
1365    /**
1366     * Helper function, identify if a configuration is linked
1367     * @hide
1368     */
1369    public boolean isLinked(WifiConfiguration config) {
1370        if (config != null) {
1371            if (config.linkedConfigurations != null && linkedConfigurations != null) {
1372                if (config.linkedConfigurations.get(configKey()) != null
1373                        && linkedConfigurations.get(config.configKey()) != null) {
1374                    return true;
1375                }
1376            }
1377        }
1378        return  false;
1379    }
1380
1381    /**
1382     * Helper function, idenfity if a configuration should be treated as an enterprise network
1383     * @hide
1384     */
1385    public boolean isEnterprise() {
1386        return allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1387            allowedKeyManagement.get(KeyMgmt.IEEE8021X);
1388    }
1389
1390    @Override
1391    public String toString() {
1392        StringBuilder sbuf = new StringBuilder();
1393        if (this.status == WifiConfiguration.Status.CURRENT) {
1394            sbuf.append("* ");
1395        } else if (this.status == WifiConfiguration.Status.DISABLED) {
1396            sbuf.append("- DSBLE ");
1397        }
1398        sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
1399                append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
1400                append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
1401                .append(" PRIO: ").append(this.priority)
1402                .append(" HIDDEN: ").append(this.hiddenSSID)
1403                .append('\n');
1404
1405
1406        sbuf.append(" NetworkSelectionStatus ")
1407                .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n");
1408        if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) {
1409            sbuf.append(" mNetworkSelectionDisableReason ")
1410                    .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
1411
1412            for (int index = mNetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
1413                    index < mNetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
1414                if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
1415                    sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
1416                            + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
1417                            + "\n");
1418                }
1419            }
1420        }
1421        if (mNetworkSelectionStatus.getConnectChoice() != null) {
1422            sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
1423            sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
1424                    .getConnectChoiceTimestamp());
1425        }
1426        sbuf.append(" hasEverConnected: ")
1427                .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
1428
1429        if (this.numAssociation > 0) {
1430            sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
1431        }
1432        if (this.numNoInternetAccessReports > 0) {
1433            sbuf.append(" numNoInternetAccessReports ");
1434            sbuf.append(this.numNoInternetAccessReports).append("\n");
1435        }
1436        if (this.updateTime != null) {
1437            sbuf.append("update ").append(this.updateTime).append("\n");
1438        }
1439        if (this.creationTime != null) {
1440            sbuf.append("creation").append(this.creationTime).append("\n");
1441        }
1442        if (this.didSelfAdd) sbuf.append(" didSelfAdd");
1443        if (this.selfAdded) sbuf.append(" selfAdded");
1444        if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
1445        if (this.ephemeral) sbuf.append(" ephemeral");
1446        if (this.meteredHint) sbuf.append(" meteredHint");
1447        if (this.useExternalScores) sbuf.append(" useExternalScores");
1448        if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
1449            || this.ephemeral || this.meteredHint || this.useExternalScores) {
1450            sbuf.append("\n");
1451        }
1452        sbuf.append(" KeyMgmt:");
1453        for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
1454            if (this.allowedKeyManagement.get(k)) {
1455                sbuf.append(" ");
1456                if (k < KeyMgmt.strings.length) {
1457                    sbuf.append(KeyMgmt.strings[k]);
1458                } else {
1459                    sbuf.append("??");
1460                }
1461            }
1462        }
1463        sbuf.append(" Protocols:");
1464        for (int p = 0; p < this.allowedProtocols.size(); p++) {
1465            if (this.allowedProtocols.get(p)) {
1466                sbuf.append(" ");
1467                if (p < Protocol.strings.length) {
1468                    sbuf.append(Protocol.strings[p]);
1469                } else {
1470                    sbuf.append("??");
1471                }
1472            }
1473        }
1474        sbuf.append('\n');
1475        sbuf.append(" AuthAlgorithms:");
1476        for (int a = 0; a < this.allowedAuthAlgorithms.size(); a++) {
1477            if (this.allowedAuthAlgorithms.get(a)) {
1478                sbuf.append(" ");
1479                if (a < AuthAlgorithm.strings.length) {
1480                    sbuf.append(AuthAlgorithm.strings[a]);
1481                } else {
1482                    sbuf.append("??");
1483                }
1484            }
1485        }
1486        sbuf.append('\n');
1487        sbuf.append(" PairwiseCiphers:");
1488        for (int pc = 0; pc < this.allowedPairwiseCiphers.size(); pc++) {
1489            if (this.allowedPairwiseCiphers.get(pc)) {
1490                sbuf.append(" ");
1491                if (pc < PairwiseCipher.strings.length) {
1492                    sbuf.append(PairwiseCipher.strings[pc]);
1493                } else {
1494                    sbuf.append("??");
1495                }
1496            }
1497        }
1498        sbuf.append('\n');
1499        sbuf.append(" GroupCiphers:");
1500        for (int gc = 0; gc < this.allowedGroupCiphers.size(); gc++) {
1501            if (this.allowedGroupCiphers.get(gc)) {
1502                sbuf.append(" ");
1503                if (gc < GroupCipher.strings.length) {
1504                    sbuf.append(GroupCipher.strings[gc]);
1505                } else {
1506                    sbuf.append("??");
1507                }
1508            }
1509        }
1510        sbuf.append('\n').append(" PSK: ");
1511        if (this.preSharedKey != null) {
1512            sbuf.append('*');
1513        }
1514        sbuf.append("\nEnterprise config:\n");
1515        sbuf.append(enterpriseConfig);
1516
1517        sbuf.append("IP config:\n");
1518        sbuf.append(mIpConfiguration.toString());
1519
1520        if (mNetworkSelectionStatus.getNetworkSelectionBSSID() != null) {
1521            sbuf.append(" networkSelectionBSSID="
1522                    + mNetworkSelectionStatus.getNetworkSelectionBSSID());
1523        }
1524        long now_ms = System.currentTimeMillis();
1525        if (mNetworkSelectionStatus.getDisableTime() != NetworkSelectionStatus
1526                .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP) {
1527            sbuf.append('\n');
1528            long diff = now_ms - mNetworkSelectionStatus.getDisableTime();
1529            if (diff <= 0) {
1530                sbuf.append(" blackListed since <incorrect>");
1531            } else {
1532                sbuf.append(" blackListed: ").append(Long.toString(diff / 1000)).append("sec ");
1533            }
1534        }
1535        if (creatorUid != 0) sbuf.append(" cuid=" + creatorUid);
1536        if (creatorName != null) sbuf.append(" cname=" + creatorName);
1537        if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
1538        if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
1539        sbuf.append(" lcuid=" + lastConnectUid);
1540        sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
1541        sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
1542        sbuf.append(" ");
1543
1544        if (this.lastConnected != 0) {
1545            sbuf.append('\n');
1546            long diff = now_ms - this.lastConnected;
1547            if (diff <= 0) {
1548                sbuf.append("lastConnected since <incorrect>");
1549            } else {
1550                sbuf.append("lastConnected: ").append(Long.toString(diff / 1000)).append("sec ");
1551            }
1552        }
1553        if (this.lastConnectionFailure != 0) {
1554            sbuf.append('\n');
1555            long diff = now_ms - this.lastConnectionFailure;
1556            if (diff <= 0) {
1557                sbuf.append("lastConnectionFailure since <incorrect> ");
1558            } else {
1559                sbuf.append("lastConnectionFailure: ").append(Long.toString(diff / 1000));
1560                sbuf.append("sec ");
1561            }
1562        }
1563        if (this.lastRoamingFailure != 0) {
1564            sbuf.append('\n');
1565            long diff = now_ms - this.lastRoamingFailure;
1566            if (diff <= 0) {
1567                sbuf.append("lastRoamingFailure since <incorrect> ");
1568            } else {
1569                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff / 1000));
1570                sbuf.append("sec ");
1571            }
1572        }
1573        sbuf.append("roamingFailureBlackListTimeMilli: ").
1574                append(Long.toString(this.roamingFailureBlackListTimeMilli));
1575        sbuf.append('\n');
1576        if (this.linkedConfigurations != null) {
1577            for (String key : this.linkedConfigurations.keySet()) {
1578                sbuf.append(" linked: ").append(key);
1579                sbuf.append('\n');
1580            }
1581        }
1582        sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
1583        sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
1584        sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
1585        sbuf.append('\n');
1586        sbuf.append("ticksLow: ").append(this.numTicksAtLowRSSI);
1587        sbuf.append(" ticksBad: ").append(this.numTicksAtBadRSSI);
1588        sbuf.append(" ticksNotHigh: ").append(this.numTicksAtNotHighRSSI);
1589        sbuf.append('\n');
1590        sbuf.append("triggeredJoin: ").append(this.numUserTriggeredJoinAttempts);
1591        sbuf.append('\n');
1592
1593        return sbuf.toString();
1594    }
1595
1596    /** {@hide} */
1597    public String getPrintableSsid() {
1598        if (SSID == null) return "";
1599        final int length = SSID.length();
1600        if (length > 2 && (SSID.charAt(0) == '"') && SSID.charAt(length - 1) == '"') {
1601            return SSID.substring(1, length - 1);
1602        }
1603
1604        /** The ascii-encoded string format is P"<ascii-encoded-string>"
1605         * The decoding is implemented in the supplicant for a newly configured
1606         * network.
1607         */
1608        if (length > 3 && (SSID.charAt(0) == 'P') && (SSID.charAt(1) == '"') &&
1609                (SSID.charAt(length-1) == '"')) {
1610            WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(
1611                    SSID.substring(2, length - 1));
1612            return wifiSsid.toString();
1613        }
1614        return SSID;
1615    }
1616
1617    /** @hide **/
1618    public static String userApprovedAsString(int userApproved) {
1619        switch (userApproved) {
1620            case USER_APPROVED:
1621                return "USER_APPROVED";
1622            case USER_BANNED:
1623                return "USER_BANNED";
1624            case USER_UNSPECIFIED:
1625                return "USER_UNSPECIFIED";
1626            default:
1627                return "INVALID";
1628        }
1629    }
1630
1631    /**
1632     * Get an identifier for associating credentials with this config
1633     * @param current configuration contains values for additional fields
1634     *                that are not part of this configuration. Used
1635     *                when a config with some fields is passed by an application.
1636     * @throws IllegalStateException if config is invalid for key id generation
1637     * @hide
1638     */
1639    public String getKeyIdForCredentials(WifiConfiguration current) {
1640        String keyMgmt = null;
1641
1642        try {
1643            // Get current config details for fields that are not initialized
1644            if (TextUtils.isEmpty(SSID)) SSID = current.SSID;
1645            if (allowedKeyManagement.cardinality() == 0) {
1646                allowedKeyManagement = current.allowedKeyManagement;
1647            }
1648            if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
1649                keyMgmt = KeyMgmt.strings[KeyMgmt.WPA_EAP];
1650            }
1651            if (allowedKeyManagement.get(KeyMgmt.OSEN)) {
1652                keyMgmt = KeyMgmt.strings[KeyMgmt.OSEN];
1653            }
1654            if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1655                keyMgmt += KeyMgmt.strings[KeyMgmt.IEEE8021X];
1656            }
1657
1658            if (TextUtils.isEmpty(keyMgmt)) {
1659                throw new IllegalStateException("Not an EAP network");
1660            }
1661
1662            return trimStringForKeyId(SSID) + "_" + keyMgmt + "_" +
1663                    trimStringForKeyId(enterpriseConfig.getKeyId(current != null ?
1664                            current.enterpriseConfig : null));
1665        } catch (NullPointerException e) {
1666            throw new IllegalStateException("Invalid config details");
1667        }
1668    }
1669
1670    private String trimStringForKeyId(String string) {
1671        // Remove quotes and spaces
1672        return string.replace("\"", "").replace(" ", "");
1673    }
1674
1675    private static BitSet readBitSet(Parcel src) {
1676        int cardinality = src.readInt();
1677
1678        BitSet set = new BitSet();
1679        for (int i = 0; i < cardinality; i++) {
1680            set.set(src.readInt());
1681        }
1682
1683        return set;
1684    }
1685
1686    private static void writeBitSet(Parcel dest, BitSet set) {
1687        int nextSetBit = -1;
1688
1689        dest.writeInt(set.cardinality());
1690
1691        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1692            dest.writeInt(nextSetBit);
1693        }
1694    }
1695
1696    /** @hide */
1697    public int getAuthType() {
1698        if (allowedKeyManagement.cardinality() > 1) {
1699            throw new IllegalStateException("More than one auth type set");
1700        }
1701        if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1702            return KeyMgmt.WPA_PSK;
1703        } else if (allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
1704            return KeyMgmt.WPA2_PSK;
1705        } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
1706            return KeyMgmt.WPA_EAP;
1707        } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1708            return KeyMgmt.IEEE8021X;
1709        }
1710        return KeyMgmt.NONE;
1711    }
1712
1713    /* @hide
1714     * Cache the config key, this seems useful as a speed up since a lot of
1715     * lookups in the config store are done and based on this key.
1716     */
1717    String mCachedConfigKey;
1718
1719    /** @hide
1720     *  return the string used to calculate the hash in WifiConfigStore
1721     *  and uniquely identify this WifiConfiguration
1722     */
1723    public String configKey(boolean allowCached) {
1724        String key;
1725        if (allowCached && mCachedConfigKey != null) {
1726            key = mCachedConfigKey;
1727        } else if (providerFriendlyName != null) {
1728            key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1729            if (!shared) {
1730                key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
1731            }
1732        } else {
1733            if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1734                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
1735            } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1736                    allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1737                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1738            } else if (wepKeys[0] != null) {
1739                key = SSID + "WEP";
1740            } else {
1741                key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
1742            }
1743            if (!shared) {
1744                key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
1745            }
1746            mCachedConfigKey = key;
1747        }
1748        return key;
1749    }
1750
1751    /** @hide
1752     * get configKey, force calculating the config string
1753     */
1754    public String configKey() {
1755        return configKey(false);
1756    }
1757
1758    /** @hide */
1759    public IpConfiguration getIpConfiguration() {
1760        return mIpConfiguration;
1761    }
1762
1763    /** @hide */
1764    public void setIpConfiguration(IpConfiguration ipConfiguration) {
1765        mIpConfiguration = ipConfiguration;
1766    }
1767
1768    /** @hide */
1769    public StaticIpConfiguration getStaticIpConfiguration() {
1770        return mIpConfiguration.getStaticIpConfiguration();
1771    }
1772
1773    /** @hide */
1774    public void setStaticIpConfiguration(StaticIpConfiguration staticIpConfiguration) {
1775        mIpConfiguration.setStaticIpConfiguration(staticIpConfiguration);
1776    }
1777
1778    /** @hide */
1779    public IpConfiguration.IpAssignment getIpAssignment() {
1780        return mIpConfiguration.ipAssignment;
1781    }
1782
1783    /** @hide */
1784    public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
1785        mIpConfiguration.ipAssignment = ipAssignment;
1786    }
1787
1788    /** @hide */
1789    public IpConfiguration.ProxySettings getProxySettings() {
1790        return mIpConfiguration.proxySettings;
1791    }
1792
1793    /** @hide */
1794    public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
1795        mIpConfiguration.proxySettings = proxySettings;
1796    }
1797
1798    /** @hide */
1799    public ProxyInfo getHttpProxy() {
1800        return mIpConfiguration.httpProxy;
1801    }
1802
1803    /** @hide */
1804    public void setHttpProxy(ProxyInfo httpProxy) {
1805        mIpConfiguration.httpProxy = httpProxy;
1806    }
1807
1808    /** @hide */
1809    public void setProxy(ProxySettings settings, ProxyInfo proxy) {
1810        mIpConfiguration.proxySettings = settings;
1811        mIpConfiguration.httpProxy = proxy;
1812    }
1813
1814    /** Implement the Parcelable interface {@hide} */
1815    public int describeContents() {
1816        return 0;
1817    }
1818
1819    /** @hide */
1820    public void setPasspointManagementObjectTree(String passpointManagementObjectTree) {
1821        mPasspointManagementObjectTree = passpointManagementObjectTree;
1822    }
1823
1824    /** @hide */
1825    public String getMoTree() {
1826        return mPasspointManagementObjectTree;
1827    }
1828
1829    /** copy constructor {@hide} */
1830    public WifiConfiguration(WifiConfiguration source) {
1831        if (source != null) {
1832            networkId = source.networkId;
1833            status = source.status;
1834            SSID = source.SSID;
1835            BSSID = source.BSSID;
1836            FQDN = source.FQDN;
1837            roamingConsortiumIds = source.roamingConsortiumIds.clone();
1838            providerFriendlyName = source.providerFriendlyName;
1839            preSharedKey = source.preSharedKey;
1840
1841            mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus());
1842            apBand = source.apBand;
1843            apChannel = source.apChannel;
1844
1845            wepKeys = new String[4];
1846            for (int i = 0; i < wepKeys.length; i++) {
1847                wepKeys[i] = source.wepKeys[i];
1848            }
1849
1850            wepTxKeyIndex = source.wepTxKeyIndex;
1851            priority = source.priority;
1852            hiddenSSID = source.hiddenSSID;
1853            allowedKeyManagement   = (BitSet) source.allowedKeyManagement.clone();
1854            allowedProtocols       = (BitSet) source.allowedProtocols.clone();
1855            allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
1856            allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
1857            allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
1858            enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
1859
1860            defaultGwMacAddress = source.defaultGwMacAddress;
1861
1862            mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
1863
1864            if ((source.linkedConfigurations != null)
1865                    && (source.linkedConfigurations.size() > 0)) {
1866                linkedConfigurations = new HashMap<String, Integer>();
1867                linkedConfigurations.putAll(source.linkedConfigurations);
1868            }
1869            mCachedConfigKey = null; //force null configKey
1870            selfAdded = source.selfAdded;
1871            validatedInternetAccess = source.validatedInternetAccess;
1872            ephemeral = source.ephemeral;
1873            meteredHint = source.meteredHint;
1874            useExternalScores = source.useExternalScores;
1875            if (source.visibility != null) {
1876                visibility = new Visibility(source.visibility);
1877            }
1878
1879            lastFailure = source.lastFailure;
1880            didSelfAdd = source.didSelfAdd;
1881            lastConnectUid = source.lastConnectUid;
1882            lastUpdateUid = source.lastUpdateUid;
1883            creatorUid = source.creatorUid;
1884            creatorName = source.creatorName;
1885            lastUpdateName = source.lastUpdateName;
1886            peerWifiConfiguration = source.peerWifiConfiguration;
1887
1888            lastConnected = source.lastConnected;
1889            lastDisconnected = source.lastDisconnected;
1890            lastConnectionFailure = source.lastConnectionFailure;
1891            lastRoamingFailure = source.lastRoamingFailure;
1892            lastRoamingFailureReason = source.lastRoamingFailureReason;
1893            roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
1894            numScorerOverride = source.numScorerOverride;
1895            numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
1896            numAssociation = source.numAssociation;
1897            numUserTriggeredWifiDisableLowRSSI = source.numUserTriggeredWifiDisableLowRSSI;
1898            numUserTriggeredWifiDisableBadRSSI = source.numUserTriggeredWifiDisableBadRSSI;
1899            numUserTriggeredWifiDisableNotHighRSSI = source.numUserTriggeredWifiDisableNotHighRSSI;
1900            numTicksAtLowRSSI = source.numTicksAtLowRSSI;
1901            numTicksAtBadRSSI = source.numTicksAtBadRSSI;
1902            numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
1903            numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
1904            userApproved = source.userApproved;
1905            numNoInternetAccessReports = source.numNoInternetAccessReports;
1906            noInternetAccessExpected = source.noInternetAccessExpected;
1907            creationTime = source.creationTime;
1908            updateTime = source.updateTime;
1909            shared = source.shared;
1910        }
1911    }
1912
1913    /** Implement the Parcelable interface {@hide} */
1914    @Override
1915    public void writeToParcel(Parcel dest, int flags) {
1916        dest.writeInt(networkId);
1917        dest.writeInt(status);
1918        mNetworkSelectionStatus.writeToParcel(dest);
1919        dest.writeString(SSID);
1920        dest.writeString(BSSID);
1921        dest.writeInt(apBand);
1922        dest.writeInt(apChannel);
1923        dest.writeString(FQDN);
1924        dest.writeString(providerFriendlyName);
1925        dest.writeInt(roamingConsortiumIds.length);
1926        for (long roamingConsortiumId : roamingConsortiumIds) {
1927            dest.writeLong(roamingConsortiumId);
1928        }
1929        dest.writeString(preSharedKey);
1930        for (String wepKey : wepKeys) {
1931            dest.writeString(wepKey);
1932        }
1933        dest.writeInt(wepTxKeyIndex);
1934        dest.writeInt(priority);
1935        dest.writeInt(hiddenSSID ? 1 : 0);
1936        dest.writeInt(requirePMF ? 1 : 0);
1937        dest.writeString(updateIdentifier);
1938
1939        writeBitSet(dest, allowedKeyManagement);
1940        writeBitSet(dest, allowedProtocols);
1941        writeBitSet(dest, allowedAuthAlgorithms);
1942        writeBitSet(dest, allowedPairwiseCiphers);
1943        writeBitSet(dest, allowedGroupCiphers);
1944
1945        dest.writeParcelable(enterpriseConfig, flags);
1946
1947        dest.writeParcelable(mIpConfiguration, flags);
1948        dest.writeString(dhcpServer);
1949        dest.writeString(defaultGwMacAddress);
1950        dest.writeInt(selfAdded ? 1 : 0);
1951        dest.writeInt(didSelfAdd ? 1 : 0);
1952        dest.writeInt(validatedInternetAccess ? 1 : 0);
1953        dest.writeInt(ephemeral ? 1 : 0);
1954        dest.writeInt(meteredHint ? 1 : 0);
1955        dest.writeInt(useExternalScores ? 1 : 0);
1956        dest.writeInt(creatorUid);
1957        dest.writeInt(lastConnectUid);
1958        dest.writeInt(lastUpdateUid);
1959        dest.writeString(creatorName);
1960        dest.writeString(lastUpdateName);
1961        dest.writeLong(lastConnectionFailure);
1962        dest.writeLong(lastRoamingFailure);
1963        dest.writeInt(lastRoamingFailureReason);
1964        dest.writeLong(roamingFailureBlackListTimeMilli);
1965        dest.writeInt(numScorerOverride);
1966        dest.writeInt(numScorerOverrideAndSwitchedNetwork);
1967        dest.writeInt(numAssociation);
1968        dest.writeInt(numUserTriggeredWifiDisableLowRSSI);
1969        dest.writeInt(numUserTriggeredWifiDisableBadRSSI);
1970        dest.writeInt(numUserTriggeredWifiDisableNotHighRSSI);
1971        dest.writeInt(numTicksAtLowRSSI);
1972        dest.writeInt(numTicksAtBadRSSI);
1973        dest.writeInt(numTicksAtNotHighRSSI);
1974        dest.writeInt(numUserTriggeredJoinAttempts);
1975        dest.writeInt(userApproved);
1976        dest.writeInt(numNoInternetAccessReports);
1977        dest.writeInt(noInternetAccessExpected ? 1 : 0);
1978        dest.writeInt(shared ? 1 : 0);
1979        dest.writeString(mPasspointManagementObjectTree);
1980    }
1981
1982    /** Implement the Parcelable interface {@hide} */
1983    public static final Creator<WifiConfiguration> CREATOR =
1984        new Creator<WifiConfiguration>() {
1985            public WifiConfiguration createFromParcel(Parcel in) {
1986                WifiConfiguration config = new WifiConfiguration();
1987                config.networkId = in.readInt();
1988                config.status = in.readInt();
1989                config.mNetworkSelectionStatus.readFromParcel(in);
1990                config.SSID = in.readString();
1991                config.BSSID = in.readString();
1992                config.apBand = in.readInt();
1993                config.apChannel = in.readInt();
1994                config.FQDN = in.readString();
1995                config.providerFriendlyName = in.readString();
1996                int numRoamingConsortiumIds = in.readInt();
1997                config.roamingConsortiumIds = new long[numRoamingConsortiumIds];
1998                for (int i = 0; i < numRoamingConsortiumIds; i++) {
1999                    config.roamingConsortiumIds[i] = in.readLong();
2000                }
2001                config.preSharedKey = in.readString();
2002                for (int i = 0; i < config.wepKeys.length; i++) {
2003                    config.wepKeys[i] = in.readString();
2004                }
2005                config.wepTxKeyIndex = in.readInt();
2006                config.priority = in.readInt();
2007                config.hiddenSSID = in.readInt() != 0;
2008                config.requirePMF = in.readInt() != 0;
2009                config.updateIdentifier = in.readString();
2010
2011                config.allowedKeyManagement   = readBitSet(in);
2012                config.allowedProtocols       = readBitSet(in);
2013                config.allowedAuthAlgorithms  = readBitSet(in);
2014                config.allowedPairwiseCiphers = readBitSet(in);
2015                config.allowedGroupCiphers    = readBitSet(in);
2016
2017                config.enterpriseConfig = in.readParcelable(null);
2018                config.mIpConfiguration = in.readParcelable(null);
2019                config.dhcpServer = in.readString();
2020                config.defaultGwMacAddress = in.readString();
2021                config.selfAdded = in.readInt() != 0;
2022                config.didSelfAdd = in.readInt() != 0;
2023                config.validatedInternetAccess = in.readInt() != 0;
2024                config.ephemeral = in.readInt() != 0;
2025                config.meteredHint = in.readInt() != 0;
2026                config.useExternalScores = in.readInt() != 0;
2027                config.creatorUid = in.readInt();
2028                config.lastConnectUid = in.readInt();
2029                config.lastUpdateUid = in.readInt();
2030                config.creatorName = in.readString();
2031                config.lastUpdateName = in.readString();
2032                config.lastConnectionFailure = in.readLong();
2033                config.lastRoamingFailure = in.readLong();
2034                config.lastRoamingFailureReason = in.readInt();
2035                config.roamingFailureBlackListTimeMilli = in.readLong();
2036                config.numScorerOverride = in.readInt();
2037                config.numScorerOverrideAndSwitchedNetwork = in.readInt();
2038                config.numAssociation = in.readInt();
2039                config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
2040                config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
2041                config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
2042                config.numTicksAtLowRSSI = in.readInt();
2043                config.numTicksAtBadRSSI = in.readInt();
2044                config.numTicksAtNotHighRSSI = in.readInt();
2045                config.numUserTriggeredJoinAttempts = in.readInt();
2046                config.userApproved = in.readInt();
2047                config.numNoInternetAccessReports = in.readInt();
2048                config.noInternetAccessExpected = in.readInt() != 0;
2049                config.shared = in.readInt() != 0;
2050                config.mPasspointManagementObjectTree = in.readString();
2051                return config;
2052            }
2053
2054            public WifiConfiguration[] newArray(int size) {
2055                return new WifiConfiguration[size];
2056            }
2057        };
2058
2059    /**
2060     * Serializes the object for backup
2061     * @hide
2062     */
2063    public byte[] getBytesForBackup() throws IOException {
2064        ByteArrayOutputStream baos = new ByteArrayOutputStream();
2065        DataOutputStream out = new DataOutputStream(baos);
2066
2067        out.writeInt(BACKUP_VERSION);
2068        BackupUtils.writeString(out, SSID);
2069        out.writeInt(apBand);
2070        out.writeInt(apChannel);
2071        BackupUtils.writeString(out, preSharedKey);
2072        out.writeInt(getAuthType());
2073        return baos.toByteArray();
2074    }
2075
2076    /**
2077     * Deserializes a byte array into the WiFiConfiguration Object
2078     * @hide
2079     */
2080    public static WifiConfiguration getWifiConfigFromBackup(DataInputStream in) throws IOException,
2081            BackupUtils.BadVersionException {
2082        WifiConfiguration config = new WifiConfiguration();
2083        int version = in.readInt();
2084        if (version < 1 || version > BACKUP_VERSION) {
2085            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
2086        }
2087
2088        if (version == 1) return null; // Version 1 is a bad dataset.
2089
2090        config.SSID = BackupUtils.readString(in);
2091        config.apBand = in.readInt();
2092        config.apChannel = in.readInt();
2093        config.preSharedKey = BackupUtils.readString(in);
2094        config.allowedKeyManagement.set(in.readInt());
2095        return config;
2096    }
2097}
2098