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