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