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         * @deprecated it is not used any more.
821         * This network is disabled because higher layer (>2) network is bad
822         */
823        public static final int DISABLED_BAD_LINK = 1;
824        /**
825         * This network is disabled because multiple association rejects
826         */
827        public static final int DISABLED_ASSOCIATION_REJECTION = 2;
828        /**
829         * This network is disabled because multiple authentication failure
830         */
831        public static final int DISABLED_AUTHENTICATION_FAILURE = 3;
832        /**
833         * This network is disabled because multiple DHCP failure
834         */
835        public static final int DISABLED_DHCP_FAILURE = 4;
836        /**
837         * This network is disabled because of security network but no credentials
838         */
839        public static final int DISABLED_DNS_FAILURE = 5;
840        /**
841         * This network is disabled because EAP-TLS failure
842         */
843        public static final int DISABLED_TLS_VERSION_MISMATCH = 6;
844        /**
845         * This network is disabled due to WifiManager disable it explicitly
846         */
847        public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 7;
848        /**
849         * This network is disabled because no Internet connected and user do not want
850         */
851        public static final int DISABLED_NO_INTERNET = 8;
852        /**
853         * This network is disabled due to WifiManager disable it explicitly
854         */
855        public static final int DISABLED_BY_WIFI_MANAGER = 9;
856        /**
857         * This Maximum disable reason value
858         */
859        public static final int NETWORK_SELECTION_DISABLED_MAX = 10;
860
861        /**
862         * Quality network selection disable reason String (for debug purpose)
863         */
864        private static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
865                "NETWORK_SELECTION_ENABLE",
866                "NETWORK_SELECTION_DISABLED_BAD_LINK", // deprecated
867                "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ",
868                "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE",
869                "NETWORK_SELECTION_DISABLED_DHCP_FAILURE",
870                "NETWORK_SELECTION_DISABLED_DNS_FAILURE",
871                "NETWORK_SELECTION_DISABLED_TLS_VERSION",
872                "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS",
873                "NETWORK_SELECTION_DISABLED_NO_INTERNET",
874                "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER"};
875
876        /**
877         * Invalid time stamp for network selection disable
878         */
879        public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L;
880
881        /**
882         *  This constant indicates the current configuration has connect choice set
883         */
884        private static final int CONNECT_CHOICE_EXISTS = 1;
885
886        /**
887         *  This constant indicates the current configuration does not have connect choice set
888         */
889        private static final int CONNECT_CHOICE_NOT_EXISTS = -1;
890
891        // fields for QualityNetwork Selection
892        /**
893         * Network selection status, should be in one of three status: enable, temporaily disabled
894         * or permanently disabled
895         */
896        private int mStatus;
897
898        /**
899         * Reason for disable this network
900         */
901        private int mNetworkSelectionDisableReason;
902
903        /**
904         * Last time we temporarily disabled the configuration
905         */
906        private long mTemporarilyDisabledTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
907
908        /**
909         * counter for each Network selection disable reason
910         */
911        private int[] mNetworkSeclectionDisableCounter = new int[NETWORK_SELECTION_DISABLED_MAX];
912
913        /**
914         * Connect Choice over this configuration
915         *
916         * When current wifi configuration is visible to the user but user explicitly choose to
917         * connect to another network X, the another networks X's configure key will be stored here.
918         * We will consider user has a preference of X over this network. And in the future,
919         * network selection will always give X a higher preference over this configuration.
920         * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
921         */
922        private String mConnectChoice;
923
924        /**
925         * The system timestamp when we records the connectChoice. This value is obtained from
926         * System.currentTimeMillis
927         */
928        private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
929
930        /**
931         * Used to cache the temporary candidate during the network selection procedure. It will be
932         * kept updating once a new scan result has a higher score than current one
933         */
934        private ScanResult mCandidate;
935
936        /**
937         * Used to cache the score of the current temporary candidate during the network
938         * selection procedure.
939         */
940        private int mCandidateScore;
941
942        /**
943         * Indicate whether this network is visible in latest Qualified Network Selection. This
944         * means there is scan result found related to this Configuration and meet the minimum
945         * requirement. The saved network need not join latest Qualified Network Selection. For
946         * example, it is disabled. True means network is visible in latest Qualified Network
947         * Selection and false means network is invisible
948         */
949        private boolean mSeenInLastQualifiedNetworkSelection;
950
951        /**
952         * Boolean indicating if we have ever successfully connected to this network.
953         *
954         * This value will be set to true upon a successful connection.
955         * This value will be set to false if a previous value was not stored in the config or if
956         * the credentials are updated (ex. a password change).
957         */
958        private boolean mHasEverConnected;
959
960        /**
961         * set whether this network is visible in latest Qualified Network Selection
962         * @param seen value set to candidate
963         */
964        public void setSeenInLastQualifiedNetworkSelection(boolean seen) {
965            mSeenInLastQualifiedNetworkSelection =  seen;
966        }
967
968        /**
969         * get whether this network is visible in latest Qualified Network Selection
970         * @return returns true -- network is visible in latest Qualified Network Selection
971         *         false -- network is invisible in latest Qualified Network Selection
972         */
973        public boolean getSeenInLastQualifiedNetworkSelection() {
974            return mSeenInLastQualifiedNetworkSelection;
975        }
976        /**
977         * set the temporary candidate of current network selection procedure
978         * @param scanCandidate {@link ScanResult} the candidate set to mCandidate
979         */
980        public void setCandidate(ScanResult scanCandidate) {
981            mCandidate = scanCandidate;
982        }
983
984        /**
985         * get the temporary candidate of current network selection procedure
986         * @return  returns {@link ScanResult} temporary candidate of current network selection
987         * procedure
988         */
989        public ScanResult getCandidate() {
990            return mCandidate;
991        }
992
993        /**
994         * set the score of the temporary candidate of current network selection procedure
995         * @param score value set to mCandidateScore
996         */
997        public void setCandidateScore(int score) {
998            mCandidateScore = score;
999        }
1000
1001        /**
1002         * get the score of the temporary candidate of current network selection procedure
1003         * @return returns score of the temporary candidate of current network selection procedure
1004         */
1005        public int getCandidateScore() {
1006            return mCandidateScore;
1007        }
1008
1009        /**
1010         * get user preferred choice over this configuration
1011         *@return returns configKey of user preferred choice over this configuration
1012         */
1013        public String getConnectChoice() {
1014            return mConnectChoice;
1015        }
1016
1017        /**
1018         * set user preferred choice over this configuration
1019         * @param newConnectChoice, the configKey of user preferred choice over this configuration
1020         */
1021        public void setConnectChoice(String newConnectChoice) {
1022            mConnectChoice = newConnectChoice;
1023        }
1024
1025        /**
1026         * get the timeStamp when user select a choice over this configuration
1027         * @return returns when current connectChoice is set (time from System.currentTimeMillis)
1028         */
1029        public long getConnectChoiceTimestamp() {
1030            return mConnectChoiceTimestamp;
1031        }
1032
1033        /**
1034         * set the timeStamp when user select a choice over this configuration
1035         * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should
1036         *        be obtained from System.currentTimeMillis
1037         */
1038        public void setConnectChoiceTimestamp(long timeStamp) {
1039            mConnectChoiceTimestamp = timeStamp;
1040        }
1041
1042        /**
1043         * get current Quality network selection status
1044         * @return returns current Quality network selection status in String (for debug purpose)
1045         */
1046        public String getNetworkStatusString() {
1047            return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
1048        }
1049
1050        public void setHasEverConnected(boolean value) {
1051            mHasEverConnected = value;
1052        }
1053
1054        public boolean getHasEverConnected() {
1055            return mHasEverConnected;
1056        }
1057
1058        private NetworkSelectionStatus() {
1059            // previously stored configs will not have this parameter, so we default to false.
1060            mHasEverConnected = false;
1061        };
1062
1063        /**
1064         * @param reason specific error reason
1065         * @return  corresponding network disable reason String (for debug purpose)
1066         */
1067        public static String getNetworkDisableReasonString(int reason) {
1068            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1069                return QUALITY_NETWORK_SELECTION_DISABLE_REASON[reason];
1070            } else {
1071                return null;
1072            }
1073        }
1074        /**
1075         * get current network disable reason
1076         * @return current network disable reason in String (for debug purpose)
1077         */
1078        public String getNetworkDisableReasonString() {
1079            return QUALITY_NETWORK_SELECTION_DISABLE_REASON[mNetworkSelectionDisableReason];
1080        }
1081
1082        /**
1083         * get current network network selection status
1084         * @return return current network network selection status
1085         */
1086        public int getNetworkSelectionStatus() {
1087            return mStatus;
1088        }
1089        /**
1090         * @return whether current network is enabled to join network selection
1091         */
1092        public boolean isNetworkEnabled() {
1093            return mStatus == NETWORK_SELECTION_ENABLED;
1094        }
1095
1096        /**
1097         * @return whether current network is temporary disabled
1098         */
1099        public boolean isNetworkTemporaryDisabled() {
1100            return mStatus == NETWORK_SELECTION_TEMPORARY_DISABLED;
1101        }
1102
1103        /**
1104         * @return returns whether current network is permanently disabled
1105         */
1106        public boolean isNetworkPermanentlyDisabled() {
1107            return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED;
1108        }
1109
1110        /**
1111         * set current networ work selection status
1112         * @param status network selection status to set
1113         */
1114        public void setNetworkSelectionStatus(int status) {
1115            if (status >= 0 && status < NETWORK_SELECTION_STATUS_MAX) {
1116                mStatus = status;
1117            }
1118        }
1119
1120        /**
1121         * @return returns current network's disable reason
1122         */
1123        public int getNetworkSelectionDisableReason() {
1124            return mNetworkSelectionDisableReason;
1125        }
1126
1127        /**
1128         * set Network disable reason
1129         * @param  reason Network disable reason
1130         */
1131        public void setNetworkSelectionDisableReason(int reason) {
1132            if (reason >= 0 && reason < NETWORK_SELECTION_DISABLED_MAX) {
1133                mNetworkSelectionDisableReason = reason;
1134            } else {
1135                throw new IllegalArgumentException("Illegal reason value: " + reason);
1136            }
1137        }
1138
1139        /**
1140         * check whether network is disabled by this reason
1141         * @param reason a specific disable reason
1142         * @return true -- network is disabled for this reason
1143         *         false -- network is not disabled for this reason
1144         */
1145        public boolean isDisabledByReason(int reason) {
1146            return mNetworkSelectionDisableReason == reason;
1147        }
1148
1149        /**
1150         * @param timeStamp Set when current network is disabled in millisecond since January 1,
1151         * 1970 00:00:00.0 UTC
1152         */
1153        public void setDisableTime(long timeStamp) {
1154            mTemporarilyDisabledTimestamp = timeStamp;
1155        }
1156
1157        /**
1158         * @return returns when current network is disabled in millisecond since January 1,
1159         * 1970 00:00:00.0 UTC
1160         */
1161        public long getDisableTime() {
1162            return mTemporarilyDisabledTimestamp;
1163        }
1164
1165        /**
1166         * get the disable counter of a specific reason
1167         * @param  reason specific failure reason
1168         * @exception throw IllegalArgumentException for illegal input
1169         * @return counter number for specific error reason.
1170         */
1171        public int getDisableReasonCounter(int reason) {
1172            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1173                return mNetworkSeclectionDisableCounter[reason];
1174            } else {
1175                throw new IllegalArgumentException("Illegal reason value: " + reason);
1176            }
1177        }
1178
1179        /**
1180         * set the counter of a specific failure reason
1181         * @param reason reason for disable error
1182         * @param value the counter value for this specific reason
1183         * @exception throw IllegalArgumentException for illegal input
1184         */
1185        public void setDisableReasonCounter(int reason, int value) {
1186            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1187                mNetworkSeclectionDisableCounter[reason] = value;
1188            } else {
1189                throw new IllegalArgumentException("Illegal reason value: " + reason);
1190            }
1191        }
1192
1193        /**
1194         * increment the counter of a specific failure reason
1195         * @param reason a specific failure reason
1196         * @exception throw IllegalArgumentException for illegal input
1197         */
1198        public void incrementDisableReasonCounter(int reason) {
1199            if (reason >= NETWORK_SELECTION_ENABLE  && reason < NETWORK_SELECTION_DISABLED_MAX) {
1200                mNetworkSeclectionDisableCounter[reason]++;
1201            } else {
1202                throw new IllegalArgumentException("Illegal reason value: " + reason);
1203            }
1204        }
1205
1206        /**
1207         * clear the counter of a specific failure reason
1208         * @hide
1209         * @param reason a specific failure reason
1210         * @exception throw IllegalArgumentException for illegal input
1211         */
1212        public void clearDisableReasonCounter(int reason) {
1213            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
1214                mNetworkSeclectionDisableCounter[reason] = NETWORK_SELECTION_ENABLE;
1215            } else {
1216                throw new IllegalArgumentException("Illegal reason value: " + reason);
1217            }
1218        }
1219
1220        /**
1221         * clear all the failure reason counters
1222         */
1223        public void clearDisableReasonCounter() {
1224            Arrays.fill(mNetworkSeclectionDisableCounter, NETWORK_SELECTION_ENABLE);
1225        }
1226
1227        /**
1228         * BSSID for connection to this network (through network selection procedure)
1229         */
1230        private String mNetworkSelectionBSSID;
1231
1232        /**
1233         * get current network Selection BSSID
1234         * @return current network Selection BSSID
1235         */
1236        public String getNetworkSelectionBSSID() {
1237            return mNetworkSelectionBSSID;
1238        }
1239
1240        /**
1241         * set network Selection BSSID
1242         * @param bssid The target BSSID for assocaition
1243         */
1244        public void setNetworkSelectionBSSID(String bssid) {
1245            mNetworkSelectionBSSID = bssid;
1246        }
1247
1248        public void copy(NetworkSelectionStatus source) {
1249            mStatus = source.mStatus;
1250            mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason;
1251            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
1252                    index++) {
1253                mNetworkSeclectionDisableCounter[index] =
1254                        source.mNetworkSeclectionDisableCounter[index];
1255            }
1256            mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
1257            mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
1258            setConnectChoice(source.getConnectChoice());
1259            setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
1260            setHasEverConnected(source.getHasEverConnected());
1261        }
1262
1263        public void writeToParcel(Parcel dest) {
1264            dest.writeInt(getNetworkSelectionStatus());
1265            dest.writeInt(getNetworkSelectionDisableReason());
1266            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
1267                    index++) {
1268                dest.writeInt(getDisableReasonCounter(index));
1269            }
1270            dest.writeLong(getDisableTime());
1271            dest.writeString(getNetworkSelectionBSSID());
1272            if (getConnectChoice() != null) {
1273                dest.writeInt(CONNECT_CHOICE_EXISTS);
1274                dest.writeString(getConnectChoice());
1275                dest.writeLong(getConnectChoiceTimestamp());
1276            } else {
1277                dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
1278            }
1279            dest.writeInt(getHasEverConnected() ? 1 : 0);
1280        }
1281
1282        public void readFromParcel(Parcel in) {
1283            setNetworkSelectionStatus(in.readInt());
1284            setNetworkSelectionDisableReason(in.readInt());
1285            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
1286                    index++) {
1287                setDisableReasonCounter(index, in.readInt());
1288            }
1289            setDisableTime(in.readLong());
1290            setNetworkSelectionBSSID(in.readString());
1291            if (in.readInt() == CONNECT_CHOICE_EXISTS) {
1292                setConnectChoice(in.readString());
1293                setConnectChoiceTimestamp(in.readLong());
1294            } else {
1295                setConnectChoice(null);
1296                setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1297            }
1298            setHasEverConnected(in.readInt() != 0);
1299        }
1300    }
1301
1302    /**
1303     * @hide
1304     * network selection related member
1305     */
1306    private final NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
1307
1308    /**
1309     * @hide
1310     * @return network selection status
1311     */
1312    public NetworkSelectionStatus getNetworkSelectionStatus() {
1313        return mNetworkSelectionStatus;
1314    }
1315    /**
1316     * @hide
1317     * Linked Configurations: represent the set of Wificonfigurations that are equivalent
1318     * regarding roaming and auto-joining.
1319     * The linked configuration may or may not have same SSID, and may or may not have same
1320     * credentials.
1321     * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server.
1322     */
1323    public HashMap<String, Integer>  linkedConfigurations;
1324
1325    public WifiConfiguration() {
1326        networkId = INVALID_NETWORK_ID;
1327        SSID = null;
1328        BSSID = null;
1329        FQDN = null;
1330        roamingConsortiumIds = new long[0];
1331        priority = 0;
1332        hiddenSSID = false;
1333        allowedKeyManagement = new BitSet();
1334        allowedProtocols = new BitSet();
1335        allowedAuthAlgorithms = new BitSet();
1336        allowedPairwiseCiphers = new BitSet();
1337        allowedGroupCiphers = new BitSet();
1338        wepKeys = new String[4];
1339        for (int i = 0; i < wepKeys.length; i++) {
1340            wepKeys[i] = null;
1341        }
1342        enterpriseConfig = new WifiEnterpriseConfig();
1343        selfAdded = false;
1344        didSelfAdd = false;
1345        ephemeral = false;
1346        meteredHint = false;
1347        useExternalScores = false;
1348        validatedInternetAccess = false;
1349        mIpConfiguration = new IpConfiguration();
1350        lastUpdateUid = -1;
1351        creatorUid = -1;
1352        shared = true;
1353        dtimInterval = 0;
1354    }
1355
1356    /**
1357     * Identify if this configuration represents a passpoint network
1358     */
1359    public boolean isPasspoint() {
1360        return !TextUtils.isEmpty(FQDN)
1361                && !TextUtils.isEmpty(providerFriendlyName)
1362                && enterpriseConfig != null
1363                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
1364    }
1365
1366    /**
1367     * Helper function, identify if a configuration is linked
1368     * @hide
1369     */
1370    public boolean isLinked(WifiConfiguration config) {
1371        if (config != null) {
1372            if (config.linkedConfigurations != null && linkedConfigurations != null) {
1373                if (config.linkedConfigurations.get(configKey()) != null
1374                        && linkedConfigurations.get(config.configKey()) != null) {
1375                    return true;
1376                }
1377            }
1378        }
1379        return  false;
1380    }
1381
1382    /**
1383     * Helper function, idenfity if a configuration should be treated as an enterprise network
1384     * @hide
1385     */
1386    public boolean isEnterprise() {
1387        return allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1388            allowedKeyManagement.get(KeyMgmt.IEEE8021X);
1389    }
1390
1391    @Override
1392    public String toString() {
1393        StringBuilder sbuf = new StringBuilder();
1394        if (this.status == WifiConfiguration.Status.CURRENT) {
1395            sbuf.append("* ");
1396        } else if (this.status == WifiConfiguration.Status.DISABLED) {
1397            sbuf.append("- DSBLE ");
1398        }
1399        sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
1400                append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
1401                append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
1402                .append(" PRIO: ").append(this.priority)
1403                .append(" HIDDEN: ").append(this.hiddenSSID)
1404                .append('\n');
1405
1406
1407        sbuf.append(" NetworkSelectionStatus ")
1408                .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n");
1409        if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) {
1410            sbuf.append(" mNetworkSelectionDisableReason ")
1411                    .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
1412
1413            for (int index = mNetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
1414                    index < mNetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
1415                if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
1416                    sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
1417                            + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
1418                            + "\n");
1419                }
1420            }
1421        }
1422        if (mNetworkSelectionStatus.getConnectChoice() != null) {
1423            sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
1424            sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
1425                    .getConnectChoiceTimestamp());
1426        }
1427        sbuf.append(" hasEverConnected: ")
1428                .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
1429
1430        if (this.numAssociation > 0) {
1431            sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
1432        }
1433        if (this.numNoInternetAccessReports > 0) {
1434            sbuf.append(" numNoInternetAccessReports ");
1435            sbuf.append(this.numNoInternetAccessReports).append("\n");
1436        }
1437        if (this.updateTime != null) {
1438            sbuf.append("update ").append(this.updateTime).append("\n");
1439        }
1440        if (this.creationTime != null) {
1441            sbuf.append("creation").append(this.creationTime).append("\n");
1442        }
1443        if (this.didSelfAdd) sbuf.append(" didSelfAdd");
1444        if (this.selfAdded) sbuf.append(" selfAdded");
1445        if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
1446        if (this.ephemeral) sbuf.append(" ephemeral");
1447        if (this.meteredHint) sbuf.append(" meteredHint");
1448        if (this.useExternalScores) sbuf.append(" useExternalScores");
1449        if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
1450            || this.ephemeral || this.meteredHint || this.useExternalScores) {
1451            sbuf.append("\n");
1452        }
1453        sbuf.append(" KeyMgmt:");
1454        for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
1455            if (this.allowedKeyManagement.get(k)) {
1456                sbuf.append(" ");
1457                if (k < KeyMgmt.strings.length) {
1458                    sbuf.append(KeyMgmt.strings[k]);
1459                } else {
1460                    sbuf.append("??");
1461                }
1462            }
1463        }
1464        sbuf.append(" Protocols:");
1465        for (int p = 0; p < this.allowedProtocols.size(); p++) {
1466            if (this.allowedProtocols.get(p)) {
1467                sbuf.append(" ");
1468                if (p < Protocol.strings.length) {
1469                    sbuf.append(Protocol.strings[p]);
1470                } else {
1471                    sbuf.append("??");
1472                }
1473            }
1474        }
1475        sbuf.append('\n');
1476        sbuf.append(" AuthAlgorithms:");
1477        for (int a = 0; a < this.allowedAuthAlgorithms.size(); a++) {
1478            if (this.allowedAuthAlgorithms.get(a)) {
1479                sbuf.append(" ");
1480                if (a < AuthAlgorithm.strings.length) {
1481                    sbuf.append(AuthAlgorithm.strings[a]);
1482                } else {
1483                    sbuf.append("??");
1484                }
1485            }
1486        }
1487        sbuf.append('\n');
1488        sbuf.append(" PairwiseCiphers:");
1489        for (int pc = 0; pc < this.allowedPairwiseCiphers.size(); pc++) {
1490            if (this.allowedPairwiseCiphers.get(pc)) {
1491                sbuf.append(" ");
1492                if (pc < PairwiseCipher.strings.length) {
1493                    sbuf.append(PairwiseCipher.strings[pc]);
1494                } else {
1495                    sbuf.append("??");
1496                }
1497            }
1498        }
1499        sbuf.append('\n');
1500        sbuf.append(" GroupCiphers:");
1501        for (int gc = 0; gc < this.allowedGroupCiphers.size(); gc++) {
1502            if (this.allowedGroupCiphers.get(gc)) {
1503                sbuf.append(" ");
1504                if (gc < GroupCipher.strings.length) {
1505                    sbuf.append(GroupCipher.strings[gc]);
1506                } else {
1507                    sbuf.append("??");
1508                }
1509            }
1510        }
1511        sbuf.append('\n').append(" PSK: ");
1512        if (this.preSharedKey != null) {
1513            sbuf.append('*');
1514        }
1515        sbuf.append("\nEnterprise config:\n");
1516        sbuf.append(enterpriseConfig);
1517
1518        sbuf.append("IP config:\n");
1519        sbuf.append(mIpConfiguration.toString());
1520
1521        if (mNetworkSelectionStatus.getNetworkSelectionBSSID() != null) {
1522            sbuf.append(" networkSelectionBSSID="
1523                    + mNetworkSelectionStatus.getNetworkSelectionBSSID());
1524        }
1525        long now_ms = System.currentTimeMillis();
1526        if (mNetworkSelectionStatus.getDisableTime() != NetworkSelectionStatus
1527                .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP) {
1528            sbuf.append('\n');
1529            long diff = now_ms - mNetworkSelectionStatus.getDisableTime();
1530            if (diff <= 0) {
1531                sbuf.append(" blackListed since <incorrect>");
1532            } else {
1533                sbuf.append(" blackListed: ").append(Long.toString(diff / 1000)).append("sec ");
1534            }
1535        }
1536        if (creatorUid != 0) sbuf.append(" cuid=" + creatorUid);
1537        if (creatorName != null) sbuf.append(" cname=" + creatorName);
1538        if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
1539        if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
1540        sbuf.append(" lcuid=" + lastConnectUid);
1541        sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
1542        sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
1543        sbuf.append(" ");
1544
1545        if (this.lastConnected != 0) {
1546            sbuf.append('\n');
1547            long diff = now_ms - this.lastConnected;
1548            if (diff <= 0) {
1549                sbuf.append("lastConnected since <incorrect>");
1550            } else {
1551                sbuf.append("lastConnected: ").append(Long.toString(diff / 1000)).append("sec ");
1552            }
1553        }
1554        if (this.lastConnectionFailure != 0) {
1555            sbuf.append('\n');
1556            long diff = now_ms - this.lastConnectionFailure;
1557            if (diff <= 0) {
1558                sbuf.append("lastConnectionFailure since <incorrect> ");
1559            } else {
1560                sbuf.append("lastConnectionFailure: ").append(Long.toString(diff / 1000));
1561                sbuf.append("sec ");
1562            }
1563        }
1564        if (this.lastRoamingFailure != 0) {
1565            sbuf.append('\n');
1566            long diff = now_ms - this.lastRoamingFailure;
1567            if (diff <= 0) {
1568                sbuf.append("lastRoamingFailure since <incorrect> ");
1569            } else {
1570                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff / 1000));
1571                sbuf.append("sec ");
1572            }
1573        }
1574        sbuf.append("roamingFailureBlackListTimeMilli: ").
1575                append(Long.toString(this.roamingFailureBlackListTimeMilli));
1576        sbuf.append('\n');
1577        if (this.linkedConfigurations != null) {
1578            for (String key : this.linkedConfigurations.keySet()) {
1579                sbuf.append(" linked: ").append(key);
1580                sbuf.append('\n');
1581            }
1582        }
1583        sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
1584        sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
1585        sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
1586        sbuf.append('\n');
1587        sbuf.append("ticksLow: ").append(this.numTicksAtLowRSSI);
1588        sbuf.append(" ticksBad: ").append(this.numTicksAtBadRSSI);
1589        sbuf.append(" ticksNotHigh: ").append(this.numTicksAtNotHighRSSI);
1590        sbuf.append('\n');
1591        sbuf.append("triggeredJoin: ").append(this.numUserTriggeredJoinAttempts);
1592        sbuf.append('\n');
1593
1594        return sbuf.toString();
1595    }
1596
1597    /** {@hide} */
1598    public String getPrintableSsid() {
1599        if (SSID == null) return "";
1600        final int length = SSID.length();
1601        if (length > 2 && (SSID.charAt(0) == '"') && SSID.charAt(length - 1) == '"') {
1602            return SSID.substring(1, length - 1);
1603        }
1604
1605        /** The ascii-encoded string format is P"<ascii-encoded-string>"
1606         * The decoding is implemented in the supplicant for a newly configured
1607         * network.
1608         */
1609        if (length > 3 && (SSID.charAt(0) == 'P') && (SSID.charAt(1) == '"') &&
1610                (SSID.charAt(length-1) == '"')) {
1611            WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(
1612                    SSID.substring(2, length - 1));
1613            return wifiSsid.toString();
1614        }
1615        return SSID;
1616    }
1617
1618    /** @hide **/
1619    public static String userApprovedAsString(int userApproved) {
1620        switch (userApproved) {
1621            case USER_APPROVED:
1622                return "USER_APPROVED";
1623            case USER_BANNED:
1624                return "USER_BANNED";
1625            case USER_UNSPECIFIED:
1626                return "USER_UNSPECIFIED";
1627            default:
1628                return "INVALID";
1629        }
1630    }
1631
1632    /**
1633     * Get an identifier for associating credentials with this config
1634     * @param current configuration contains values for additional fields
1635     *                that are not part of this configuration. Used
1636     *                when a config with some fields is passed by an application.
1637     * @throws IllegalStateException if config is invalid for key id generation
1638     * @hide
1639     */
1640    public String getKeyIdForCredentials(WifiConfiguration current) {
1641        String keyMgmt = null;
1642
1643        try {
1644            // Get current config details for fields that are not initialized
1645            if (TextUtils.isEmpty(SSID)) SSID = current.SSID;
1646            if (allowedKeyManagement.cardinality() == 0) {
1647                allowedKeyManagement = current.allowedKeyManagement;
1648            }
1649            if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
1650                keyMgmt = KeyMgmt.strings[KeyMgmt.WPA_EAP];
1651            }
1652            if (allowedKeyManagement.get(KeyMgmt.OSEN)) {
1653                keyMgmt = KeyMgmt.strings[KeyMgmt.OSEN];
1654            }
1655            if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1656                keyMgmt += KeyMgmt.strings[KeyMgmt.IEEE8021X];
1657            }
1658
1659            if (TextUtils.isEmpty(keyMgmt)) {
1660                throw new IllegalStateException("Not an EAP network");
1661            }
1662
1663            return trimStringForKeyId(SSID) + "_" + keyMgmt + "_" +
1664                    trimStringForKeyId(enterpriseConfig.getKeyId(current != null ?
1665                            current.enterpriseConfig : null));
1666        } catch (NullPointerException e) {
1667            throw new IllegalStateException("Invalid config details");
1668        }
1669    }
1670
1671    private String trimStringForKeyId(String string) {
1672        // Remove quotes and spaces
1673        return string.replace("\"", "").replace(" ", "");
1674    }
1675
1676    private static BitSet readBitSet(Parcel src) {
1677        int cardinality = src.readInt();
1678
1679        BitSet set = new BitSet();
1680        for (int i = 0; i < cardinality; i++) {
1681            set.set(src.readInt());
1682        }
1683
1684        return set;
1685    }
1686
1687    private static void writeBitSet(Parcel dest, BitSet set) {
1688        int nextSetBit = -1;
1689
1690        dest.writeInt(set.cardinality());
1691
1692        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1693            dest.writeInt(nextSetBit);
1694        }
1695    }
1696
1697    /** @hide */
1698    public int getAuthType() {
1699        if (allowedKeyManagement.cardinality() > 1) {
1700            throw new IllegalStateException("More than one auth type set");
1701        }
1702        if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1703            return KeyMgmt.WPA_PSK;
1704        } else if (allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
1705            return KeyMgmt.WPA2_PSK;
1706        } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
1707            return KeyMgmt.WPA_EAP;
1708        } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1709            return KeyMgmt.IEEE8021X;
1710        }
1711        return KeyMgmt.NONE;
1712    }
1713
1714    /* @hide
1715     * Cache the config key, this seems useful as a speed up since a lot of
1716     * lookups in the config store are done and based on this key.
1717     */
1718    String mCachedConfigKey;
1719
1720    /** @hide
1721     *  return the string used to calculate the hash in WifiConfigStore
1722     *  and uniquely identify this WifiConfiguration
1723     */
1724    public String configKey(boolean allowCached) {
1725        String key;
1726        if (allowCached && mCachedConfigKey != null) {
1727            key = mCachedConfigKey;
1728        } else if (providerFriendlyName != null) {
1729            key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1730            if (!shared) {
1731                key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
1732            }
1733        } else {
1734            if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1735                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
1736            } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1737                    allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1738                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1739            } else if (wepKeys[0] != null) {
1740                key = SSID + "WEP";
1741            } else {
1742                key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
1743            }
1744            if (!shared) {
1745                key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
1746            }
1747            mCachedConfigKey = key;
1748        }
1749        return key;
1750    }
1751
1752    /** @hide
1753     * get configKey, force calculating the config string
1754     */
1755    public String configKey() {
1756        return configKey(false);
1757    }
1758
1759    /** @hide */
1760    public IpConfiguration getIpConfiguration() {
1761        return mIpConfiguration;
1762    }
1763
1764    /** @hide */
1765    public void setIpConfiguration(IpConfiguration ipConfiguration) {
1766        mIpConfiguration = ipConfiguration;
1767    }
1768
1769    /** @hide */
1770    public StaticIpConfiguration getStaticIpConfiguration() {
1771        return mIpConfiguration.getStaticIpConfiguration();
1772    }
1773
1774    /** @hide */
1775    public void setStaticIpConfiguration(StaticIpConfiguration staticIpConfiguration) {
1776        mIpConfiguration.setStaticIpConfiguration(staticIpConfiguration);
1777    }
1778
1779    /** @hide */
1780    public IpConfiguration.IpAssignment getIpAssignment() {
1781        return mIpConfiguration.ipAssignment;
1782    }
1783
1784    /** @hide */
1785    public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
1786        mIpConfiguration.ipAssignment = ipAssignment;
1787    }
1788
1789    /** @hide */
1790    public IpConfiguration.ProxySettings getProxySettings() {
1791        return mIpConfiguration.proxySettings;
1792    }
1793
1794    /** @hide */
1795    public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
1796        mIpConfiguration.proxySettings = proxySettings;
1797    }
1798
1799    /** @hide */
1800    public ProxyInfo getHttpProxy() {
1801        return mIpConfiguration.httpProxy;
1802    }
1803
1804    /** @hide */
1805    public void setHttpProxy(ProxyInfo httpProxy) {
1806        mIpConfiguration.httpProxy = httpProxy;
1807    }
1808
1809    /** @hide */
1810    public void setProxy(ProxySettings settings, ProxyInfo proxy) {
1811        mIpConfiguration.proxySettings = settings;
1812        mIpConfiguration.httpProxy = proxy;
1813    }
1814
1815    /** Implement the Parcelable interface {@hide} */
1816    public int describeContents() {
1817        return 0;
1818    }
1819
1820    /** @hide */
1821    public void setPasspointManagementObjectTree(String passpointManagementObjectTree) {
1822        mPasspointManagementObjectTree = passpointManagementObjectTree;
1823    }
1824
1825    /** @hide */
1826    public String getMoTree() {
1827        return mPasspointManagementObjectTree;
1828    }
1829
1830    /** copy constructor {@hide} */
1831    public WifiConfiguration(WifiConfiguration source) {
1832        if (source != null) {
1833            networkId = source.networkId;
1834            status = source.status;
1835            SSID = source.SSID;
1836            BSSID = source.BSSID;
1837            FQDN = source.FQDN;
1838            roamingConsortiumIds = source.roamingConsortiumIds.clone();
1839            providerFriendlyName = source.providerFriendlyName;
1840            preSharedKey = source.preSharedKey;
1841
1842            mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus());
1843            apBand = source.apBand;
1844            apChannel = source.apChannel;
1845
1846            wepKeys = new String[4];
1847            for (int i = 0; i < wepKeys.length; i++) {
1848                wepKeys[i] = source.wepKeys[i];
1849            }
1850
1851            wepTxKeyIndex = source.wepTxKeyIndex;
1852            priority = source.priority;
1853            hiddenSSID = source.hiddenSSID;
1854            allowedKeyManagement   = (BitSet) source.allowedKeyManagement.clone();
1855            allowedProtocols       = (BitSet) source.allowedProtocols.clone();
1856            allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
1857            allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
1858            allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
1859            enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
1860
1861            defaultGwMacAddress = source.defaultGwMacAddress;
1862
1863            mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
1864
1865            if ((source.linkedConfigurations != null)
1866                    && (source.linkedConfigurations.size() > 0)) {
1867                linkedConfigurations = new HashMap<String, Integer>();
1868                linkedConfigurations.putAll(source.linkedConfigurations);
1869            }
1870            mCachedConfigKey = null; //force null configKey
1871            selfAdded = source.selfAdded;
1872            validatedInternetAccess = source.validatedInternetAccess;
1873            ephemeral = source.ephemeral;
1874            meteredHint = source.meteredHint;
1875            useExternalScores = source.useExternalScores;
1876            if (source.visibility != null) {
1877                visibility = new Visibility(source.visibility);
1878            }
1879
1880            lastFailure = source.lastFailure;
1881            didSelfAdd = source.didSelfAdd;
1882            lastConnectUid = source.lastConnectUid;
1883            lastUpdateUid = source.lastUpdateUid;
1884            creatorUid = source.creatorUid;
1885            creatorName = source.creatorName;
1886            lastUpdateName = source.lastUpdateName;
1887            peerWifiConfiguration = source.peerWifiConfiguration;
1888
1889            lastConnected = source.lastConnected;
1890            lastDisconnected = source.lastDisconnected;
1891            lastConnectionFailure = source.lastConnectionFailure;
1892            lastRoamingFailure = source.lastRoamingFailure;
1893            lastRoamingFailureReason = source.lastRoamingFailureReason;
1894            roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
1895            numScorerOverride = source.numScorerOverride;
1896            numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
1897            numAssociation = source.numAssociation;
1898            numUserTriggeredWifiDisableLowRSSI = source.numUserTriggeredWifiDisableLowRSSI;
1899            numUserTriggeredWifiDisableBadRSSI = source.numUserTriggeredWifiDisableBadRSSI;
1900            numUserTriggeredWifiDisableNotHighRSSI = source.numUserTriggeredWifiDisableNotHighRSSI;
1901            numTicksAtLowRSSI = source.numTicksAtLowRSSI;
1902            numTicksAtBadRSSI = source.numTicksAtBadRSSI;
1903            numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
1904            numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
1905            userApproved = source.userApproved;
1906            numNoInternetAccessReports = source.numNoInternetAccessReports;
1907            noInternetAccessExpected = source.noInternetAccessExpected;
1908            creationTime = source.creationTime;
1909            updateTime = source.updateTime;
1910            shared = source.shared;
1911        }
1912    }
1913
1914    /** Implement the Parcelable interface {@hide} */
1915    @Override
1916    public void writeToParcel(Parcel dest, int flags) {
1917        dest.writeInt(networkId);
1918        dest.writeInt(status);
1919        mNetworkSelectionStatus.writeToParcel(dest);
1920        dest.writeString(SSID);
1921        dest.writeString(BSSID);
1922        dest.writeInt(apBand);
1923        dest.writeInt(apChannel);
1924        dest.writeString(FQDN);
1925        dest.writeString(providerFriendlyName);
1926        dest.writeInt(roamingConsortiumIds.length);
1927        for (long roamingConsortiumId : roamingConsortiumIds) {
1928            dest.writeLong(roamingConsortiumId);
1929        }
1930        dest.writeString(preSharedKey);
1931        for (String wepKey : wepKeys) {
1932            dest.writeString(wepKey);
1933        }
1934        dest.writeInt(wepTxKeyIndex);
1935        dest.writeInt(priority);
1936        dest.writeInt(hiddenSSID ? 1 : 0);
1937        dest.writeInt(requirePMF ? 1 : 0);
1938        dest.writeString(updateIdentifier);
1939
1940        writeBitSet(dest, allowedKeyManagement);
1941        writeBitSet(dest, allowedProtocols);
1942        writeBitSet(dest, allowedAuthAlgorithms);
1943        writeBitSet(dest, allowedPairwiseCiphers);
1944        writeBitSet(dest, allowedGroupCiphers);
1945
1946        dest.writeParcelable(enterpriseConfig, flags);
1947
1948        dest.writeParcelable(mIpConfiguration, flags);
1949        dest.writeString(dhcpServer);
1950        dest.writeString(defaultGwMacAddress);
1951        dest.writeInt(selfAdded ? 1 : 0);
1952        dest.writeInt(didSelfAdd ? 1 : 0);
1953        dest.writeInt(validatedInternetAccess ? 1 : 0);
1954        dest.writeInt(ephemeral ? 1 : 0);
1955        dest.writeInt(meteredHint ? 1 : 0);
1956        dest.writeInt(useExternalScores ? 1 : 0);
1957        dest.writeInt(creatorUid);
1958        dest.writeInt(lastConnectUid);
1959        dest.writeInt(lastUpdateUid);
1960        dest.writeString(creatorName);
1961        dest.writeString(lastUpdateName);
1962        dest.writeLong(lastConnectionFailure);
1963        dest.writeLong(lastRoamingFailure);
1964        dest.writeInt(lastRoamingFailureReason);
1965        dest.writeLong(roamingFailureBlackListTimeMilli);
1966        dest.writeInt(numScorerOverride);
1967        dest.writeInt(numScorerOverrideAndSwitchedNetwork);
1968        dest.writeInt(numAssociation);
1969        dest.writeInt(numUserTriggeredWifiDisableLowRSSI);
1970        dest.writeInt(numUserTriggeredWifiDisableBadRSSI);
1971        dest.writeInt(numUserTriggeredWifiDisableNotHighRSSI);
1972        dest.writeInt(numTicksAtLowRSSI);
1973        dest.writeInt(numTicksAtBadRSSI);
1974        dest.writeInt(numTicksAtNotHighRSSI);
1975        dest.writeInt(numUserTriggeredJoinAttempts);
1976        dest.writeInt(userApproved);
1977        dest.writeInt(numNoInternetAccessReports);
1978        dest.writeInt(noInternetAccessExpected ? 1 : 0);
1979        dest.writeInt(shared ? 1 : 0);
1980        dest.writeString(mPasspointManagementObjectTree);
1981    }
1982
1983    /** Implement the Parcelable interface {@hide} */
1984    public static final Creator<WifiConfiguration> CREATOR =
1985        new Creator<WifiConfiguration>() {
1986            public WifiConfiguration createFromParcel(Parcel in) {
1987                WifiConfiguration config = new WifiConfiguration();
1988                config.networkId = in.readInt();
1989                config.status = in.readInt();
1990                config.mNetworkSelectionStatus.readFromParcel(in);
1991                config.SSID = in.readString();
1992                config.BSSID = in.readString();
1993                config.apBand = in.readInt();
1994                config.apChannel = in.readInt();
1995                config.FQDN = in.readString();
1996                config.providerFriendlyName = in.readString();
1997                int numRoamingConsortiumIds = in.readInt();
1998                config.roamingConsortiumIds = new long[numRoamingConsortiumIds];
1999                for (int i = 0; i < numRoamingConsortiumIds; i++) {
2000                    config.roamingConsortiumIds[i] = in.readLong();
2001                }
2002                config.preSharedKey = in.readString();
2003                for (int i = 0; i < config.wepKeys.length; i++) {
2004                    config.wepKeys[i] = in.readString();
2005                }
2006                config.wepTxKeyIndex = in.readInt();
2007                config.priority = in.readInt();
2008                config.hiddenSSID = in.readInt() != 0;
2009                config.requirePMF = in.readInt() != 0;
2010                config.updateIdentifier = in.readString();
2011
2012                config.allowedKeyManagement   = readBitSet(in);
2013                config.allowedProtocols       = readBitSet(in);
2014                config.allowedAuthAlgorithms  = readBitSet(in);
2015                config.allowedPairwiseCiphers = readBitSet(in);
2016                config.allowedGroupCiphers    = readBitSet(in);
2017
2018                config.enterpriseConfig = in.readParcelable(null);
2019                config.mIpConfiguration = in.readParcelable(null);
2020                config.dhcpServer = in.readString();
2021                config.defaultGwMacAddress = in.readString();
2022                config.selfAdded = in.readInt() != 0;
2023                config.didSelfAdd = in.readInt() != 0;
2024                config.validatedInternetAccess = in.readInt() != 0;
2025                config.ephemeral = in.readInt() != 0;
2026                config.meteredHint = in.readInt() != 0;
2027                config.useExternalScores = in.readInt() != 0;
2028                config.creatorUid = in.readInt();
2029                config.lastConnectUid = in.readInt();
2030                config.lastUpdateUid = in.readInt();
2031                config.creatorName = in.readString();
2032                config.lastUpdateName = in.readString();
2033                config.lastConnectionFailure = in.readLong();
2034                config.lastRoamingFailure = in.readLong();
2035                config.lastRoamingFailureReason = in.readInt();
2036                config.roamingFailureBlackListTimeMilli = in.readLong();
2037                config.numScorerOverride = in.readInt();
2038                config.numScorerOverrideAndSwitchedNetwork = in.readInt();
2039                config.numAssociation = in.readInt();
2040                config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
2041                config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
2042                config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
2043                config.numTicksAtLowRSSI = in.readInt();
2044                config.numTicksAtBadRSSI = in.readInt();
2045                config.numTicksAtNotHighRSSI = in.readInt();
2046                config.numUserTriggeredJoinAttempts = in.readInt();
2047                config.userApproved = in.readInt();
2048                config.numNoInternetAccessReports = in.readInt();
2049                config.noInternetAccessExpected = in.readInt() != 0;
2050                config.shared = in.readInt() != 0;
2051                config.mPasspointManagementObjectTree = in.readString();
2052                return config;
2053            }
2054
2055            public WifiConfiguration[] newArray(int size) {
2056                return new WifiConfiguration[size];
2057            }
2058        };
2059
2060    /**
2061     * Serializes the object for backup
2062     * @hide
2063     */
2064    public byte[] getBytesForBackup() throws IOException {
2065        ByteArrayOutputStream baos = new ByteArrayOutputStream();
2066        DataOutputStream out = new DataOutputStream(baos);
2067
2068        out.writeInt(BACKUP_VERSION);
2069        BackupUtils.writeString(out, SSID);
2070        out.writeInt(apBand);
2071        out.writeInt(apChannel);
2072        BackupUtils.writeString(out, preSharedKey);
2073        out.writeInt(getAuthType());
2074        return baos.toByteArray();
2075    }
2076
2077    /**
2078     * Deserializes a byte array into the WiFiConfiguration Object
2079     * @hide
2080     */
2081    public static WifiConfiguration getWifiConfigFromBackup(DataInputStream in) throws IOException,
2082            BackupUtils.BadVersionException {
2083        WifiConfiguration config = new WifiConfiguration();
2084        int version = in.readInt();
2085        if (version < 1 || version > BACKUP_VERSION) {
2086            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
2087        }
2088
2089        if (version == 1) return null; // Version 1 is a bad dataset.
2090
2091        config.SSID = BackupUtils.readString(in);
2092        config.apBand = in.readInt();
2093        config.apChannel = in.readInt();
2094        config.preSharedKey = BackupUtils.readString(in);
2095        config.allowedKeyManagement.set(in.readInt());
2096        return config;
2097    }
2098}
2099