Home | History | Annotate | Download | only in wifi
      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 
     17 package android.net.wifi;
     18 
     19 import android.annotation.SystemApi;
     20 import android.net.IpConfiguration;
     21 import android.net.IpConfiguration.ProxySettings;
     22 import android.net.IpConfiguration.IpAssignment;
     23 import android.net.ProxyInfo;
     24 import android.net.StaticIpConfiguration;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.text.TextUtils;
     28 import android.annotation.SystemApi;
     29 
     30 import java.util.HashMap;
     31 import java.util.BitSet;
     32 import java.util.ArrayList;
     33 import java.util.Collections;
     34 import java.util.Comparator;
     35 
     36 /**
     37  * A class representing a configured Wi-Fi network, including the
     38  * security configuration.
     39  */
     40 public class WifiConfiguration implements Parcelable {
     41     private static final String TAG = "WifiConfiguration";
     42     /** {@hide} */
     43     public static final String ssidVarName = "ssid";
     44     /** {@hide} */
     45     public static final String bssidVarName = "bssid";
     46     /** {@hide} */
     47     public static final String pskVarName = "psk";
     48     /** {@hide} */
     49     public static final String[] wepKeyVarNames = { "wep_key0", "wep_key1", "wep_key2", "wep_key3" };
     50     /** {@hide} */
     51     public static final String wepTxKeyIdxVarName = "wep_tx_keyidx";
     52     /** {@hide} */
     53     public static final String priorityVarName = "priority";
     54     /** {@hide} */
     55     public static final String hiddenSSIDVarName = "scan_ssid";
     56     /** {@hide} */
     57     public static final String pmfVarName = "ieee80211w";
     58     /** {@hide} */
     59     public static final String updateIdentiferVarName = "update_identifier";
     60     /** {@hide} */
     61     public static final int INVALID_NETWORK_ID = -1;
     62     /**
     63      * Recognized key management schemes.
     64      */
     65     public static class KeyMgmt {
     66         private KeyMgmt() { }
     67 
     68         /** WPA is not used; plaintext or static WEP could be used. */
     69         public static final int NONE = 0;
     70         /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */
     71         public static final int WPA_PSK = 1;
     72         /** WPA using EAP authentication. Generally used with an external authentication server. */
     73         public static final int WPA_EAP = 2;
     74         /** IEEE 802.1X using EAP authentication and (optionally) dynamically
     75          * generated WEP keys. */
     76         public static final int IEEE8021X = 3;
     77 
     78         /** WPA2 pre-shared key for use with soft access point
     79           * (requires {@code preSharedKey} to be specified).
     80           * @hide
     81           */
     82         public static final int WPA2_PSK = 4;
     83 
     84         public static final String varName = "key_mgmt";
     85 
     86         public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X",
     87                 "WPA2_PSK" };
     88     }
     89 
     90     /**
     91      * Recognized security protocols.
     92      */
     93     public static class Protocol {
     94         private Protocol() { }
     95 
     96         /** WPA/IEEE 802.11i/D3.0 */
     97         public static final int WPA = 0;
     98         /** WPA2/IEEE 802.11i */
     99         public static final int RSN = 1;
    100 
    101         public static final String varName = "proto";
    102 
    103         public static final String[] strings = { "WPA", "RSN" };
    104     }
    105 
    106     /**
    107      * Recognized IEEE 802.11 authentication algorithms.
    108      */
    109     public static class AuthAlgorithm {
    110         private AuthAlgorithm() { }
    111 
    112         /** Open System authentication (required for WPA/WPA2) */
    113         public static final int OPEN = 0;
    114         /** Shared Key authentication (requires static WEP keys) */
    115         public static final int SHARED = 1;
    116         /** LEAP/Network EAP (only used with LEAP) */
    117         public static final int LEAP = 2;
    118 
    119         public static final String varName = "auth_alg";
    120 
    121         public static final String[] strings = { "OPEN", "SHARED", "LEAP" };
    122     }
    123 
    124     /**
    125      * Recognized pairwise ciphers for WPA.
    126      */
    127     public static class PairwiseCipher {
    128         private PairwiseCipher() { }
    129 
    130         /** Use only Group keys (deprecated) */
    131         public static final int NONE = 0;
    132         /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
    133         public static final int TKIP = 1;
    134         /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
    135         public static final int CCMP = 2;
    136 
    137         public static final String varName = "pairwise";
    138 
    139         public static final String[] strings = { "NONE", "TKIP", "CCMP" };
    140     }
    141 
    142     /**
    143      * Recognized group ciphers.
    144      * <pre>
    145      * CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
    146      * TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
    147      * WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
    148      * WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11)
    149      * </pre>
    150      */
    151     public static class GroupCipher {
    152         private GroupCipher() { }
    153 
    154         /** WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11) */
    155         public static final int WEP40 = 0;
    156         /** WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key */
    157         public static final int WEP104 = 1;
    158         /** Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] */
    159         public static final int TKIP = 2;
    160         /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
    161         public static final int CCMP = 3;
    162 
    163         public static final String varName = "group";
    164 
    165         public static final String[] strings = { "WEP40", "WEP104", "TKIP", "CCMP" };
    166     }
    167 
    168     /** Possible status of a network configuration. */
    169     public static class Status {
    170         private Status() { }
    171 
    172         /** this is the network we are currently connected to */
    173         public static final int CURRENT = 0;
    174         /** supplicant will not attempt to use this network */
    175         public static final int DISABLED = 1;
    176         /** supplicant will consider this network available for association */
    177         public static final int ENABLED = 2;
    178 
    179         public static final String[] strings = { "current", "disabled", "enabled" };
    180     }
    181 
    182     /** @hide */
    183     public static final int DISABLED_UNKNOWN_REASON                         = 0;
    184     /** @hide */
    185     public static final int DISABLED_DNS_FAILURE                            = 1;
    186     /** @hide */
    187     public static final int DISABLED_DHCP_FAILURE                           = 2;
    188     /** @hide */
    189     public static final int DISABLED_AUTH_FAILURE                           = 3;
    190     /** @hide */
    191     public static final int DISABLED_ASSOCIATION_REJECT                     = 4;
    192     /** @hide */
    193     public static final int DISABLED_BY_WIFI_MANAGER                        = 5;
    194 
    195     /**
    196      * The ID number that the supplicant uses to identify this
    197      * network configuration entry. This must be passed as an argument
    198      * to most calls into the supplicant.
    199      */
    200     public int networkId;
    201 
    202     /**
    203      * The current status of this network configuration entry.
    204      * @see Status
    205      */
    206     public int status;
    207 
    208     /**
    209      * The configuration needs to be written to networkHistory.txt
    210      * @hide
    211      */
    212     public boolean dirty;
    213 
    214     /**
    215      * The code referring to a reason for disabling the network
    216      * Valid when {@link #status} == Status.DISABLED
    217      * @hide
    218      */
    219     public int disableReason;
    220 
    221     /**
    222      * The network's SSID. Can either be an ASCII string,
    223      * which must be enclosed in double quotation marks
    224      * (e.g., {@code "MyNetwork"}, or a string of
    225      * hex digits,which are not enclosed in quotes
    226      * (e.g., {@code 01a243f405}).
    227      */
    228     public String SSID;
    229     /**
    230      * When set, this network configuration entry should only be used when
    231      * associating with the AP having the specified BSSID. The value is
    232      * a string in the format of an Ethernet MAC address, e.g.,
    233      * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
    234      */
    235     public String BSSID;
    236     /**
    237      * Fully qualified domain name (FQDN) of AAA server or RADIUS server
    238      * e.g. {@code "mail.example.com"}.
    239      */
    240     public String FQDN;
    241     /**
    242      * Network access identifier (NAI) realm, for Passpoint credential.
    243      * e.g. {@code "myhost.example.com"}.
    244      * @hide
    245      */
    246     public String naiRealm;
    247 
    248     /**
    249      * Pre-shared key for use with WPA-PSK.
    250      * <p/>
    251      * When the value of this key is read, the actual key is
    252      * not returned, just a "*" if the key has a value, or the null
    253      * string otherwise.
    254      */
    255     public String preSharedKey;
    256     /**
    257      * Up to four WEP keys. Either an ASCII string enclosed in double
    258      * quotation marks (e.g., {@code "abcdef"} or a string
    259      * of hex digits (e.g., {@code 0102030405}).
    260      * <p/>
    261      * When the value of one of these keys is read, the actual key is
    262      * not returned, just a "*" if the key has a value, or the null
    263      * string otherwise.
    264      */
    265     public String[] wepKeys;
    266 
    267     /** Default WEP key index, ranging from 0 to 3. */
    268     public int wepTxKeyIndex;
    269 
    270     /**
    271      * Priority determines the preference given to a network by {@code wpa_supplicant}
    272      * when choosing an access point with which to associate.
    273      */
    274     public int priority;
    275 
    276     /**
    277      * This is a network that does not broadcast its SSID, so an
    278      * SSID-specific probe request must be used for scans.
    279      */
    280     public boolean hiddenSSID;
    281 
    282     /**
    283      * This is a network that requries Protected Management Frames (PMF).
    284      * @hide
    285      */
    286     public boolean requirePMF;
    287 
    288     /**
    289      * Update identifier, for Passpoint network.
    290      * @hide
    291      */
    292     public String updateIdentifier;
    293 
    294     /**
    295      * The set of key management protocols supported by this configuration.
    296      * See {@link KeyMgmt} for descriptions of the values.
    297      * Defaults to WPA-PSK WPA-EAP.
    298      */
    299     public BitSet allowedKeyManagement;
    300     /**
    301      * The set of security protocols supported by this configuration.
    302      * See {@link Protocol} for descriptions of the values.
    303      * Defaults to WPA RSN.
    304      */
    305     public BitSet allowedProtocols;
    306     /**
    307      * The set of authentication protocols supported by this configuration.
    308      * See {@link AuthAlgorithm} for descriptions of the values.
    309      * Defaults to automatic selection.
    310      */
    311     public BitSet allowedAuthAlgorithms;
    312     /**
    313      * The set of pairwise ciphers for WPA supported by this configuration.
    314      * See {@link PairwiseCipher} for descriptions of the values.
    315      * Defaults to CCMP TKIP.
    316      */
    317     public BitSet allowedPairwiseCiphers;
    318     /**
    319      * The set of group ciphers supported by this configuration.
    320      * See {@link GroupCipher} for descriptions of the values.
    321      * Defaults to CCMP TKIP WEP104 WEP40.
    322      */
    323     public BitSet allowedGroupCiphers;
    324     /**
    325      * The enterprise configuration details specifying the EAP method,
    326      * certificates and other settings associated with the EAP.
    327      */
    328     public WifiEnterpriseConfig enterpriseConfig;
    329 
    330     /**
    331      * @hide
    332      */
    333     private IpConfiguration mIpConfiguration;
    334 
    335     /**
    336      * @hide
    337      * dhcp server MAC address if known
    338      */
    339     public String dhcpServer;
    340 
    341     /**
    342      * @hide
    343      * default Gateway MAC address if known
    344      */
    345     public String defaultGwMacAddress;
    346 
    347     /**
    348      * @hide
    349      * last failure
    350      */
    351     public String lastFailure;
    352 
    353     /**
    354      * @hide
    355      * last time we connected, this configuration had validated internet access
    356      */
    357     public boolean validatedInternetAccess;
    358 
    359     /**
    360      * @hide
    361      * Uid of app creating the configuration
    362      */
    363     @SystemApi
    364     public int creatorUid;
    365 
    366     /**
    367      * @hide
    368      * Uid of last app issuing a connection related command
    369      */
    370     public int lastConnectUid;
    371 
    372     /**
    373      * @hide
    374      * Uid of last app modifying the configuration
    375      */
    376     @SystemApi
    377     public int lastUpdateUid;
    378 
    379     /**
    380      * @hide
    381      * Uid used by autoJoin
    382      */
    383     public String autoJoinBSSID;
    384 
    385     /**
    386      * @hide
    387      * BSSID list on which this configuration was seen.
    388      * TODO: prevent this list to grow infinitely, age-out the results
    389      */
    390     public HashMap<String, ScanResult> scanResultCache;
    391 
    392     /** The Below RSSI thresholds are used to configure AutoJoin
    393      *  - GOOD/LOW/BAD thresholds are used so as to calculate link score
    394      *  - UNWANTED_SOFT are used by the blacklisting logic so as to handle
    395      *  the unwanted network message coming from CS
    396      *  - UNBLACKLIST thresholds are used so as to tweak the speed at which
    397      *  the network is unblacklisted (i.e. if
    398      *          it is seen with good RSSI, it is blacklisted faster)
    399      *  - INITIAL_AUTOJOIN_ATTEMPT, used to determine how close from
    400      *  the network we need to be before autojoin kicks in
    401      */
    402     /** @hide **/
    403     public static int INVALID_RSSI = -127;
    404 
    405     /** @hide **/
    406     public static int UNWANTED_BLACKLIST_SOFT_RSSI_24 = -80;
    407 
    408     /** @hide **/
    409     public static int UNWANTED_BLACKLIST_SOFT_RSSI_5 = -70;
    410 
    411     /** @hide **/
    412     public static int GOOD_RSSI_24 = -65;
    413 
    414     /** @hide **/
    415     public static int LOW_RSSI_24 = -77;
    416 
    417     /** @hide **/
    418     public static int BAD_RSSI_24 = -87;
    419 
    420     /** @hide **/
    421     public static int GOOD_RSSI_5 = -60;
    422 
    423     /** @hide **/
    424     public static int LOW_RSSI_5 = -72;
    425 
    426     /** @hide **/
    427     public static int BAD_RSSI_5 = -82;
    428 
    429     /** @hide **/
    430     public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
    431 
    432     /** @hide **/
    433     public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
    434 
    435     /** @hide **/
    436     public static int UNBLACKLIST_THRESHOLD_24_SOFT = -77;
    437 
    438     /** @hide **/
    439     public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
    440 
    441     /** @hide **/
    442     public static int UNBLACKLIST_THRESHOLD_5_SOFT = -63;
    443 
    444     /** @hide **/
    445     public static int UNBLACKLIST_THRESHOLD_5_HARD = -56;
    446 
    447     /** @hide **/
    448     public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_24 = -80;
    449 
    450     /** @hide **/
    451     public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70;
    452 
    453     /** @hide
    454      * 5GHz band is prefered low over 2.4 if the 5GHz RSSI is higher than this threshold */
    455     public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
    456 
    457     /** @hide
    458      * 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
    459     public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
    460 
    461     /** @hide
    462      * Boost given to RSSI on a home network for the purpose of calculating the score
    463      * This adds stickiness to home networks, as defined by:
    464      * - less than 4 known BSSIDs
    465      * - PSK only
    466      * - TODO: add a test to verify that all BSSIDs are behind same gateway
    467      ***/
    468     public static int HOME_NETWORK_RSSI_BOOST = 5;
    469 
    470     /** @hide
    471      * RSSI boost for configuration which use autoJoinUseAggressiveJoinAttemptThreshold
    472      * To be more aggressive when initially attempting to auto join
    473      */
    474     public static int MAX_INITIAL_AUTO_JOIN_RSSI_BOOST = 8;
    475 
    476     /**
    477      * @hide
    478      * A summary of the RSSI and Band status for that configuration
    479      * This is used as a temporary value by the auto-join controller
    480      */
    481     public final class Visibility {
    482         public int rssi5;   // strongest 5GHz RSSI
    483         public int rssi24;  // strongest 2.4GHz RSSI
    484         public int num5;    // number of BSSIDs on 5GHz
    485         public int num24;   // number of BSSIDs on 2.4GHz
    486         public long age5;   // timestamp of the strongest 5GHz BSSID (last time it was seen)
    487         public long age24;  // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
    488         public String BSSID24;
    489         public String BSSID5;
    490         public int score; // Debug only, indicate last score used for autojoin/cell-handover
    491         public int currentNetworkBoost; // Debug only, indicate boost applied to RSSI if current
    492         public int bandPreferenceBoost; // Debug only, indicate boost applied to RSSI if current
    493         public int lastChoiceBoost; // Debug only, indicate last choice applied to this configuration
    494         public String lastChoiceConfig; // Debug only, indicate last choice applied to this configuration
    495 
    496         public Visibility() {
    497             rssi5 = INVALID_RSSI;
    498             rssi24 = INVALID_RSSI;
    499         }
    500 
    501         public Visibility(Visibility source) {
    502             rssi5 = source.rssi5;
    503             rssi24 = source.rssi24;
    504             age24 = source.age24;
    505             age5 = source.age5;
    506             num24 = source.num24;
    507             num5 = source.num5;
    508             BSSID5 = source.BSSID5;
    509             BSSID24 = source.BSSID24;
    510         }
    511 
    512         @Override
    513         public String toString() {
    514             StringBuilder sbuf = new StringBuilder();
    515             sbuf.append("[");
    516             if (rssi24 > INVALID_RSSI) {
    517                 sbuf.append(Integer.toString(rssi24));
    518                 sbuf.append(",");
    519                 sbuf.append(Integer.toString(num24));
    520                 if (BSSID24 != null) sbuf.append(",").append(BSSID24);
    521             }
    522             sbuf.append("; ");
    523             if (rssi5 > INVALID_RSSI) {
    524                 sbuf.append(Integer.toString(rssi5));
    525                 sbuf.append(",");
    526                 sbuf.append(Integer.toString(num5));
    527                 if (BSSID5 != null) sbuf.append(",").append(BSSID5);
    528             }
    529             if (score != 0) {
    530                 sbuf.append("; ").append(score);
    531                 sbuf.append(", ").append(currentNetworkBoost);
    532                 sbuf.append(", ").append(bandPreferenceBoost);
    533                 if (lastChoiceConfig != null) {
    534                     sbuf.append(", ").append(lastChoiceBoost);
    535                     sbuf.append(", ").append(lastChoiceConfig);
    536                 }
    537             }
    538             sbuf.append("]");
    539             return sbuf.toString();
    540         }
    541     }
    542 
    543     /** @hide
    544      * Cache the visibility status of this configuration.
    545      * Visibility can change at any time depending on scan results availability.
    546      * Owner of the WifiConfiguration is responsible to set this field based on
    547      * recent scan results.
    548      ***/
    549     public Visibility visibility;
    550 
    551     /** @hide
    552      * calculate and set Visibility for that configuration.
    553      *
    554      * age in milliseconds: we will consider only ScanResults that are more recent,
    555      * i.e. younger.
    556      ***/
    557     public Visibility setVisibility(long age) {
    558         if (scanResultCache == null) {
    559             visibility = null;
    560             return null;
    561         }
    562 
    563         Visibility status = new Visibility();
    564 
    565         long now_ms = System.currentTimeMillis();
    566         for(ScanResult result : scanResultCache.values()) {
    567             if (result.seen == 0)
    568                 continue;
    569 
    570             if (result.is5GHz()) {
    571                 //strictly speaking: [4915, 5825]
    572                 //number of known BSSID on 5GHz band
    573                 status.num5 = status.num5 + 1;
    574             } else if (result.is24GHz()) {
    575                 //strictly speaking: [2412, 2482]
    576                 //number of known BSSID on 2.4Ghz band
    577                 status.num24 = status.num24 + 1;
    578             }
    579 
    580             if ((now_ms - result.seen) > age) continue;
    581 
    582             if (result.is5GHz()) {
    583                 if (result.level > status.rssi5) {
    584                     status.rssi5 = result.level;
    585                     status.age5 = result.seen;
    586                     status.BSSID5 = result.BSSID;
    587                 }
    588             } else if (result.is24GHz()) {
    589                 if (result.level > status.rssi24) {
    590                     status.rssi24 = result.level;
    591                     status.age24 = result.seen;
    592                     status.BSSID24 = result.BSSID;
    593                 }
    594             }
    595         }
    596         visibility = status;
    597         return status;
    598     }
    599 
    600     /** @hide */
    601     public static final int AUTO_JOIN_ENABLED                   = 0;
    602     /**
    603      * if this is set, the WifiConfiguration cannot use linkages so as to bump
    604      * it's relative priority.
    605      * - status between and 128 indicate various level of blacklisting depending
    606      * on the severity or frequency of the connection error
    607      * - deleted status indicates that the user is deleting the configuration, and so
    608      * although it may have been self added we will not re-self-add it, ignore it,
    609      * not return it to applications, and not connect to it
    610      * */
    611 
    612     /** @hide
    613      * network was temporary disabled due to bad connection, most likely due
    614      * to weak RSSI */
    615     public static final int AUTO_JOIN_TEMPORARY_DISABLED  = 1;
    616     /** @hide
    617      * network was temporary disabled due to bad connection, which cant be attributed
    618      * to weak RSSI */
    619     public static final int AUTO_JOIN_TEMPORARY_DISABLED_LINK_ERRORS  = 32;
    620     /** @hide */
    621     public static final int AUTO_JOIN_TEMPORARY_DISABLED_AT_SUPPLICANT  = 64;
    622     /** @hide */
    623     public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE  = 128;
    624     /** @hide */
    625     public static final int AUTO_JOIN_DISABLED_NO_CREDENTIALS = 160;
    626     /** @hide */
    627     public static final int AUTO_JOIN_DISABLED_USER_ACTION = 161;
    628 
    629     /** @hide */
    630     public static final int AUTO_JOIN_DELETED  = 200;
    631 
    632     /**
    633      * @hide
    634      */
    635     public int autoJoinStatus;
    636 
    637     /**
    638      * @hide
    639      * Number of connection failures
    640      */
    641     public int numConnectionFailures;
    642 
    643     /**
    644      * @hide
    645      * Number of IP config failures
    646      */
    647     public int numIpConfigFailures;
    648 
    649     /**
    650      * @hide
    651      * Number of Auth failures
    652      */
    653     public int numAuthFailures;
    654 
    655     /**
    656      * @hide
    657      * Number of reports indicating no Internet Access
    658      */
    659     public int numNoInternetAccessReports;
    660 
    661     /**
    662      * @hide
    663      * The WiFi configuration is considered to have no internet access for purpose of autojoining
    664      * if there has been a report of it having no internet access, and, it never have had
    665      * internet access in the past.
    666      */
    667     public boolean hasNoInternetAccess() {
    668         return numNoInternetAccessReports > 0 && !validatedInternetAccess;
    669     }
    670 
    671     /**
    672      * @hide
    673      * Last time we blacklisted the configuration
    674      */
    675     public long blackListTimestamp;
    676 
    677     /**
    678      * @hide
    679      * Last time the system was connected to this configuration.
    680      */
    681     public long lastConnected;
    682 
    683     /**
    684      * @hide
    685      * Last time the system tried to connect and failed.
    686      */
    687     public long lastConnectionFailure;
    688 
    689     /**
    690      * @hide
    691      * Last time the system tried to roam and failed because of authentication failure or DHCP
    692      * RENEW failure.
    693      */
    694     public long lastRoamingFailure;
    695 
    696     /** @hide */
    697     public static int ROAMING_FAILURE_IP_CONFIG = 1;
    698     /** @hide */
    699     public static int ROAMING_FAILURE_AUTH_FAILURE = 2;
    700 
    701     /**
    702      * @hide
    703      * Initial amount of time this Wifi configuration gets blacklisted for network switching
    704      * because of roaming failure
    705      */
    706     public long roamingFailureBlackListTimeMilli = 1000;
    707 
    708     /**
    709      * @hide
    710      * Last roaming failure reason code
    711      */
    712     public int lastRoamingFailureReason;
    713 
    714     /**
    715      * @hide
    716      * Last time the system was disconnected to this configuration.
    717      */
    718     public long lastDisconnected;
    719 
    720     /**
    721      * Set if the configuration was self added by the framework
    722      * This boolean is cleared if we get a connect/save/ update or
    723      * any wifiManager command that indicate the user interacted with the configuration
    724      * since we will now consider that the configuration belong to him.
    725      * @hide
    726      */
    727     public boolean selfAdded;
    728 
    729     /**
    730      * Set if the configuration was self added by the framework
    731      * This boolean is set once and never cleared. It is used
    732      * so as we never loose track of who created the
    733      * configuration in the first place.
    734      * @hide
    735      */
    736     public boolean didSelfAdd;
    737 
    738     /**
    739      * Peer WifiConfiguration this WifiConfiguration was added for
    740      * @hide
    741      */
    742     public String peerWifiConfiguration;
    743 
    744     /**
    745      * @hide
    746      * Indicate that a WifiConfiguration is temporary and should not be saved
    747      * nor considered by AutoJoin.
    748      */
    749     public boolean ephemeral;
    750 
    751     /**
    752      * @hide
    753      * Indicate that we didn't auto-join because rssi was too low
    754      */
    755     public boolean autoJoinBailedDueToLowRssi;
    756 
    757     /**
    758      * @hide
    759      * AutoJoin even though RSSI is 10dB below threshold
    760      */
    761     public int autoJoinUseAggressiveJoinAttemptThreshold;
    762 
    763     /**
    764      * @hide
    765      * Number of time the scorer overrode a the priority based choice, when comparing two
    766      * WifiConfigurations, note that since comparing WifiConfiguration happens very often
    767      * potentially at every scan, this number might become very large, even on an idle
    768      * system.
    769      */
    770     @SystemApi
    771     public int numScorerOverride;
    772 
    773     /**
    774      * @hide
    775      * Number of time the scorer overrode a the priority based choice, and the comparison
    776      * triggered a network switch
    777      */
    778     @SystemApi
    779     public int numScorerOverrideAndSwitchedNetwork;
    780 
    781     /**
    782      * @hide
    783      * Number of time we associated to this configuration.
    784      */
    785     @SystemApi
    786     public int numAssociation;
    787 
    788     /**
    789      * @hide
    790      * Number of time user disabled WiFi while associated to this configuration with Low RSSI.
    791      */
    792     public int numUserTriggeredWifiDisableLowRSSI;
    793 
    794     /**
    795      * @hide
    796      * Number of time user disabled WiFi while associated to this configuration with Bad RSSI.
    797      */
    798     public int numUserTriggeredWifiDisableBadRSSI;
    799 
    800     /**
    801      * @hide
    802      * Number of time user disabled WiFi while associated to this configuration
    803      * and RSSI was not HIGH.
    804      */
    805     public int numUserTriggeredWifiDisableNotHighRSSI;
    806 
    807     /**
    808      * @hide
    809      * Number of ticks associated to this configuration with Low RSSI.
    810      */
    811     public int numTicksAtLowRSSI;
    812 
    813     /**
    814      * @hide
    815      * Number of ticks associated to this configuration with Bad RSSI.
    816      */
    817     public int numTicksAtBadRSSI;
    818 
    819     /**
    820      * @hide
    821      * Number of ticks associated to this configuration
    822      * and RSSI was not HIGH.
    823      */
    824     public int numTicksAtNotHighRSSI;
    825     /**
    826      * @hide
    827      * Number of time user (WifiManager) triggered association to this configuration.
    828      * TODO: count this only for Wifi Settings uuid, so as to not count 3rd party apps
    829      */
    830     public int numUserTriggeredJoinAttempts;
    831 
    832     /**
    833      * @hide
    834      * Connect choices
    835      *
    836      * remember the keys identifying the known WifiConfiguration over which this configuration
    837      * was preferred by user or a "WiFi Network Management app", that is,
    838      * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration
    839      * was visible to the user:
    840      * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
    841      *
    842      * The integer represents the configuration's RSSI at that time (useful?)
    843      *
    844      * The overall auto-join algorithm make use of past connect choice so as to sort configuration,
    845      * the exact algorithm still fluctuating as of 5/7/2014
    846      *
    847      */
    848     public HashMap<String, Integer> connectChoices;
    849 
    850     /**
    851      * @hide
    852      * Linked Configurations: represent the set of Wificonfigurations that are equivalent
    853      * regarding roaming and auto-joining.
    854      * The linked configuration may or may not have same SSID, and may or may not have same
    855      * credentials.
    856      * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server.
    857      */
    858     public HashMap<String, Integer>  linkedConfigurations;
    859 
    860     public WifiConfiguration() {
    861         networkId = INVALID_NETWORK_ID;
    862         SSID = null;
    863         BSSID = null;
    864         FQDN = null;
    865         naiRealm = null;
    866         priority = 0;
    867         hiddenSSID = false;
    868         disableReason = DISABLED_UNKNOWN_REASON;
    869         allowedKeyManagement = new BitSet();
    870         allowedProtocols = new BitSet();
    871         allowedAuthAlgorithms = new BitSet();
    872         allowedPairwiseCiphers = new BitSet();
    873         allowedGroupCiphers = new BitSet();
    874         wepKeys = new String[4];
    875         for (int i = 0; i < wepKeys.length; i++) {
    876             wepKeys[i] = null;
    877         }
    878         enterpriseConfig = new WifiEnterpriseConfig();
    879         autoJoinStatus = AUTO_JOIN_ENABLED;
    880         selfAdded = false;
    881         didSelfAdd = false;
    882         ephemeral = false;
    883         validatedInternetAccess = false;
    884         mIpConfiguration = new IpConfiguration();
    885     }
    886 
    887     /**
    888      * indicates whether the configuration is valid
    889      * @return true if valid, false otherwise
    890      * @hide
    891      */
    892     public boolean isValid() {
    893 
    894         if (allowedKeyManagement == null)
    895             return false;
    896 
    897         if (allowedKeyManagement.cardinality() > 1) {
    898             if (allowedKeyManagement.cardinality() != 2) {
    899                 return false;
    900             }
    901             if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) == false) {
    902                 return false;
    903             }
    904             if ((allowedKeyManagement.get(KeyMgmt.IEEE8021X) == false)
    905                     && (allowedKeyManagement.get(KeyMgmt.WPA_PSK) == false)) {
    906                 return false;
    907             }
    908         }
    909 
    910         // TODO: Add more checks
    911         return true;
    912     }
    913 
    914     /**
    915      * Helper function, identify if a configuration is linked
    916      * @hide
    917      */
    918     public boolean isLinked(WifiConfiguration config) {
    919         if (config.linkedConfigurations != null && linkedConfigurations != null) {
    920             if (config.linkedConfigurations.get(configKey()) != null
    921                     && linkedConfigurations.get(config.configKey()) != null) {
    922                 return true;
    923             }
    924         }
    925         return  false;
    926     }
    927 
    928     /**
    929      * most recent time we have seen this configuration
    930      * @return most recent scanResult
    931      * @hide
    932      */
    933     public ScanResult lastSeen() {
    934         ScanResult mostRecent = null;
    935 
    936         if (scanResultCache == null) {
    937             return null;
    938         }
    939 
    940         for (ScanResult result : scanResultCache.values()) {
    941             if (mostRecent == null) {
    942                 if (result.seen != 0)
    943                    mostRecent = result;
    944             } else {
    945                 if (result.seen > mostRecent.seen) {
    946                    mostRecent = result;
    947                 }
    948             }
    949         }
    950         return mostRecent;
    951     }
    952 
    953     /** @hide **/
    954     public void setAutoJoinStatus(int status) {
    955         if (status < 0) status = 0;
    956         if (status == 0) {
    957             blackListTimestamp = 0;
    958         }  else if (status > autoJoinStatus) {
    959             blackListTimestamp = System.currentTimeMillis();
    960         }
    961         if (status != autoJoinStatus) {
    962             autoJoinStatus = status;
    963             dirty = true;
    964         }
    965     }
    966 
    967     /** @hide
    968      *  trim the scan Result Cache
    969      * @param: number of entries to keep in the cache
    970      */
    971     public void trimScanResultsCache(int num) {
    972         if (this.scanResultCache == null) {
    973             return;
    974         }
    975         int currenSize = this.scanResultCache.size();
    976         if (currenSize <= num) {
    977             return; // Nothing to trim
    978         }
    979         ArrayList<ScanResult> list = new ArrayList<ScanResult>(this.scanResultCache.values());
    980         if (list.size() != 0) {
    981             // Sort by descending timestamp
    982             Collections.sort(list, new Comparator() {
    983                 public int compare(Object o1, Object o2) {
    984                     ScanResult a = (ScanResult)o1;
    985                     ScanResult b = (ScanResult)o2;
    986                     if (a.seen > b.seen) {
    987                         return 1;
    988                     }
    989                     if (a.seen < b.seen) {
    990                         return -1;
    991                     }
    992                     return a.BSSID.compareTo(b.BSSID);
    993                 }
    994             });
    995         }
    996         for (int i = 0; i < currenSize - num ; i++) {
    997             // Remove oldest results from scan cache
    998             ScanResult result = list.get(i);
    999             this.scanResultCache.remove(result.BSSID);
   1000         }
   1001     }
   1002 
   1003     /* @hide */
   1004     private ArrayList<ScanResult> sortScanResults() {
   1005         ArrayList<ScanResult> list = new ArrayList<ScanResult>(this.scanResultCache.values());
   1006         if (list.size() != 0) {
   1007             Collections.sort(list, new Comparator() {
   1008                 public int compare(Object o1, Object o2) {
   1009                     ScanResult a = (ScanResult)o1;
   1010                     ScanResult b = (ScanResult)o2;
   1011                     if (a.numIpConfigFailures > b.numIpConfigFailures) {
   1012                         return 1;
   1013                     }
   1014                     if (a.numIpConfigFailures < b.numIpConfigFailures) {
   1015                         return -1;
   1016                     }
   1017                     if (a.seen > b.seen) {
   1018                         return -1;
   1019                     }
   1020                     if (a.seen < b.seen) {
   1021                         return 1;
   1022                     }
   1023                     if (a.level > b.level) {
   1024                         return -1;
   1025                     }
   1026                     if (a.level < b.level) {
   1027                         return 1;
   1028                     }
   1029                     return a.BSSID.compareTo(b.BSSID);
   1030                 }
   1031             });
   1032         }
   1033         return list;
   1034     }
   1035 
   1036     @Override
   1037     public String toString() {
   1038         StringBuilder sbuf = new StringBuilder();
   1039         if (this.status == WifiConfiguration.Status.CURRENT) {
   1040             sbuf.append("* ");
   1041         } else if (this.status == WifiConfiguration.Status.DISABLED) {
   1042             sbuf.append("- DSBLE ");
   1043         }
   1044         sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
   1045                 append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN).
   1046                 append(" REALM: ").append(this.naiRealm).append(" PRIO: ").append(this.priority).
   1047                 append('\n');
   1048         if (this.numConnectionFailures > 0) {
   1049             sbuf.append(" numConnectFailures ").append(this.numConnectionFailures).append("\n");
   1050         }
   1051         if (this.numIpConfigFailures > 0) {
   1052             sbuf.append(" numIpConfigFailures ").append(this.numIpConfigFailures).append("\n");
   1053         }
   1054         if (this.numAuthFailures > 0) {
   1055             sbuf.append(" numAuthFailures ").append(this.numAuthFailures).append("\n");
   1056         }
   1057         if (this.autoJoinStatus > 0) {
   1058             sbuf.append(" autoJoinStatus ").append(this.autoJoinStatus).append("\n");
   1059         }
   1060         if (this.disableReason > 0) {
   1061             sbuf.append(" disableReason ").append(this.disableReason).append("\n");
   1062         }
   1063         if (this.numAssociation > 0) {
   1064             sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
   1065         }
   1066         if (this.numNoInternetAccessReports > 0) {
   1067             sbuf.append(" numNoInternetAccessReports ");
   1068             sbuf.append(this.numNoInternetAccessReports).append("\n");
   1069         }
   1070         if (this.didSelfAdd) sbuf.append(" didSelfAdd");
   1071         if (this.selfAdded) sbuf.append(" selfAdded");
   1072         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
   1073         if (this.ephemeral) sbuf.append(" ephemeral");
   1074         if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess || this.ephemeral) {
   1075             sbuf.append("\n");
   1076         }
   1077         sbuf.append(" KeyMgmt:");
   1078         for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
   1079             if (this.allowedKeyManagement.get(k)) {
   1080                 sbuf.append(" ");
   1081                 if (k < KeyMgmt.strings.length) {
   1082                     sbuf.append(KeyMgmt.strings[k]);
   1083                 } else {
   1084                     sbuf.append("??");
   1085                 }
   1086             }
   1087         }
   1088         sbuf.append(" Protocols:");
   1089         for (int p = 0; p < this.allowedProtocols.size(); p++) {
   1090             if (this.allowedProtocols.get(p)) {
   1091                 sbuf.append(" ");
   1092                 if (p < Protocol.strings.length) {
   1093                     sbuf.append(Protocol.strings[p]);
   1094                 } else {
   1095                     sbuf.append("??");
   1096                 }
   1097             }
   1098         }
   1099         sbuf.append('\n');
   1100         sbuf.append(" AuthAlgorithms:");
   1101         for (int a = 0; a < this.allowedAuthAlgorithms.size(); a++) {
   1102             if (this.allowedAuthAlgorithms.get(a)) {
   1103                 sbuf.append(" ");
   1104                 if (a < AuthAlgorithm.strings.length) {
   1105                     sbuf.append(AuthAlgorithm.strings[a]);
   1106                 } else {
   1107                     sbuf.append("??");
   1108                 }
   1109             }
   1110         }
   1111         sbuf.append('\n');
   1112         sbuf.append(" PairwiseCiphers:");
   1113         for (int pc = 0; pc < this.allowedPairwiseCiphers.size(); pc++) {
   1114             if (this.allowedPairwiseCiphers.get(pc)) {
   1115                 sbuf.append(" ");
   1116                 if (pc < PairwiseCipher.strings.length) {
   1117                     sbuf.append(PairwiseCipher.strings[pc]);
   1118                 } else {
   1119                     sbuf.append("??");
   1120                 }
   1121             }
   1122         }
   1123         sbuf.append('\n');
   1124         sbuf.append(" GroupCiphers:");
   1125         for (int gc = 0; gc < this.allowedGroupCiphers.size(); gc++) {
   1126             if (this.allowedGroupCiphers.get(gc)) {
   1127                 sbuf.append(" ");
   1128                 if (gc < GroupCipher.strings.length) {
   1129                     sbuf.append(GroupCipher.strings[gc]);
   1130                 } else {
   1131                     sbuf.append("??");
   1132                 }
   1133             }
   1134         }
   1135         sbuf.append('\n').append(" PSK: ");
   1136         if (this.preSharedKey != null) {
   1137             sbuf.append('*');
   1138         }
   1139         sbuf.append("\nEnterprise config:\n");
   1140         sbuf.append(enterpriseConfig);
   1141 
   1142         sbuf.append("IP config:\n");
   1143         sbuf.append(mIpConfiguration.toString());
   1144 
   1145         if (this.creatorUid != 0)  sbuf.append(" uid=" + Integer.toString(creatorUid));
   1146         if (this.autoJoinBSSID != null) sbuf.append(" autoJoinBSSID=" + autoJoinBSSID);
   1147         long now_ms = System.currentTimeMillis();
   1148         if (this.blackListTimestamp != 0) {
   1149             sbuf.append('\n');
   1150             long diff = now_ms - this.blackListTimestamp;
   1151             if (diff <= 0) {
   1152                 sbuf.append(" blackListed since <incorrect>");
   1153             } else {
   1154                 sbuf.append(" blackListed: ").append(Long.toString(diff/1000)).append( "sec");
   1155             }
   1156         }
   1157         if (this.lastConnected != 0) {
   1158             sbuf.append('\n');
   1159             long diff = now_ms - this.lastConnected;
   1160             if (diff <= 0) {
   1161                 sbuf.append("lastConnected since <incorrect>");
   1162             } else {
   1163                 sbuf.append("lastConnected: ").append(Long.toString(diff/1000)).append( "sec");
   1164             }
   1165         }
   1166         if (this.lastConnectionFailure != 0) {
   1167             sbuf.append('\n');
   1168             long diff = now_ms - this.lastConnectionFailure;
   1169             if (diff <= 0) {
   1170                 sbuf.append("lastConnectionFailure since <incorrect>");
   1171             } else {
   1172                 sbuf.append("lastConnectionFailure: ").append(Long.toString(diff/1000));
   1173                 sbuf.append( "sec");
   1174             }
   1175         }
   1176         if (this.lastRoamingFailure != 0) {
   1177             sbuf.append('\n');
   1178             long diff = now_ms - this.lastRoamingFailure;
   1179             if (diff <= 0) {
   1180                 sbuf.append("lastRoamingFailure since <incorrect>");
   1181             } else {
   1182                 sbuf.append("lastRoamingFailure: ").append(Long.toString(diff/1000));
   1183                 sbuf.append( "sec");
   1184             }
   1185         }
   1186         sbuf.append("roamingFailureBlackListTimeMilli: ").
   1187                 append(Long.toString(this.roamingFailureBlackListTimeMilli));
   1188         sbuf.append('\n');
   1189         if (this.linkedConfigurations != null) {
   1190             for(String key : this.linkedConfigurations.keySet()) {
   1191                 sbuf.append(" linked: ").append(key);
   1192                 sbuf.append('\n');
   1193             }
   1194         }
   1195         if (this.connectChoices != null) {
   1196             for(String key : this.connectChoices.keySet()) {
   1197                 Integer choice = this.connectChoices.get(key);
   1198                 if (choice != null) {
   1199                     sbuf.append(" choice: ").append(key);
   1200                     sbuf.append(" = ").append(choice);
   1201                     sbuf.append('\n');
   1202                 }
   1203             }
   1204         }
   1205         if (this.scanResultCache != null) {
   1206             sbuf.append("Scan Cache:  ").append('\n');
   1207             ArrayList<ScanResult> list = sortScanResults();
   1208             if (list.size() > 0) {
   1209                 for (ScanResult result : list) {
   1210                     long milli = now_ms - result.seen;
   1211                     long ageSec = 0;
   1212                     long ageMin = 0;
   1213                     long ageHour = 0;
   1214                     long ageMilli = 0;
   1215                     long ageDay = 0;
   1216                     if (now_ms > result.seen && result.seen > 0) {
   1217                         ageMilli = milli % 1000;
   1218                         ageSec   = (milli / 1000) % 60;
   1219                         ageMin   = (milli / (60*1000)) % 60;
   1220                         ageHour  = (milli / (60*60*1000)) % 24;
   1221                         ageDay   = (milli / (24*60*60*1000));
   1222                     }
   1223                     sbuf.append("{").append(result.BSSID).append(",").append(result.frequency);
   1224                     sbuf.append(",").append(String.format("%3d", result.level));
   1225                     if (result.autoJoinStatus > 0) {
   1226                         sbuf.append(",st=").append(result.autoJoinStatus);
   1227                     }
   1228                     if (ageSec > 0 || ageMilli > 0) {
   1229                         sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay,
   1230                                 ageHour, ageMin, ageSec, ageMilli));
   1231                     }
   1232                     if (result.numIpConfigFailures > 0) {
   1233                         sbuf.append(",ipfail=");
   1234                         sbuf.append(result.numIpConfigFailures);
   1235                     }
   1236                     sbuf.append("} ");
   1237                 }
   1238                 sbuf.append('\n');
   1239             }
   1240         }
   1241         sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
   1242         sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
   1243         sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
   1244         sbuf.append('\n');
   1245         sbuf.append("ticksLow: ").append(this.numTicksAtLowRSSI);
   1246         sbuf.append(" ticksBad: ").append(this.numTicksAtBadRSSI);
   1247         sbuf.append(" ticksNotHigh: ").append(this.numTicksAtNotHighRSSI);
   1248         sbuf.append('\n');
   1249         sbuf.append("triggeredJoin: ").append(this.numUserTriggeredJoinAttempts);
   1250         sbuf.append('\n');
   1251         sbuf.append("autoJoinBailedDueToLowRssi: ").append(this.autoJoinBailedDueToLowRssi);
   1252         sbuf.append('\n');
   1253         sbuf.append("autoJoinUseAggressiveJoinAttemptThreshold: ");
   1254         sbuf.append(this.autoJoinUseAggressiveJoinAttemptThreshold);
   1255         sbuf.append('\n');
   1256 
   1257         return sbuf.toString();
   1258     }
   1259 
   1260     /**
   1261      * Construct a WifiConfiguration from a scanned network
   1262      * @param scannedAP the scan result used to construct the config entry
   1263      * TODO: figure out whether this is a useful way to construct a new entry.
   1264      *
   1265     public WifiConfiguration(ScanResult scannedAP) {
   1266         networkId = -1;
   1267         SSID = scannedAP.SSID;
   1268         BSSID = scannedAP.BSSID;
   1269     }
   1270     */
   1271 
   1272     /** {@hide} */
   1273     public String getPrintableSsid() {
   1274         if (SSID == null) return "";
   1275         final int length = SSID.length();
   1276         if (length > 2 && (SSID.charAt(0) == '"') && SSID.charAt(length - 1) == '"') {
   1277             return SSID.substring(1, length - 1);
   1278         }
   1279 
   1280         /** The ascii-encoded string format is P"<ascii-encoded-string>"
   1281          * The decoding is implemented in the supplicant for a newly configured
   1282          * network.
   1283          */
   1284         if (length > 3 && (SSID.charAt(0) == 'P') && (SSID.charAt(1) == '"') &&
   1285                 (SSID.charAt(length-1) == '"')) {
   1286             WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(
   1287                     SSID.substring(2, length - 1));
   1288             return wifiSsid.toString();
   1289         }
   1290         return SSID;
   1291     }
   1292 
   1293     /**
   1294      * Get an identifier for associating credentials with this config
   1295      * @param current configuration contains values for additional fields
   1296      *                that are not part of this configuration. Used
   1297      *                when a config with some fields is passed by an application.
   1298      * @throws IllegalStateException if config is invalid for key id generation
   1299      * @hide
   1300      */
   1301     public String getKeyIdForCredentials(WifiConfiguration current) {
   1302         String keyMgmt = null;
   1303 
   1304         try {
   1305             // Get current config details for fields that are not initialized
   1306             if (TextUtils.isEmpty(SSID)) SSID = current.SSID;
   1307             if (allowedKeyManagement.cardinality() == 0) {
   1308                 allowedKeyManagement = current.allowedKeyManagement;
   1309             }
   1310             if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
   1311                 keyMgmt = KeyMgmt.strings[KeyMgmt.WPA_EAP];
   1312             }
   1313             if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
   1314                 keyMgmt += KeyMgmt.strings[KeyMgmt.IEEE8021X];
   1315             }
   1316 
   1317             if (TextUtils.isEmpty(keyMgmt)) {
   1318                 throw new IllegalStateException("Not an EAP network");
   1319             }
   1320 
   1321             return trimStringForKeyId(SSID) + "_" + keyMgmt + "_" +
   1322                     trimStringForKeyId(enterpriseConfig.getKeyId(current != null ?
   1323                             current.enterpriseConfig : null));
   1324         } catch (NullPointerException e) {
   1325             throw new IllegalStateException("Invalid config details");
   1326         }
   1327     }
   1328 
   1329     private String trimStringForKeyId(String string) {
   1330         // Remove quotes and spaces
   1331         return string.replace("\"", "").replace(" ", "");
   1332     }
   1333 
   1334     private static BitSet readBitSet(Parcel src) {
   1335         int cardinality = src.readInt();
   1336 
   1337         BitSet set = new BitSet();
   1338         for (int i = 0; i < cardinality; i++) {
   1339             set.set(src.readInt());
   1340         }
   1341 
   1342         return set;
   1343     }
   1344 
   1345     private static void writeBitSet(Parcel dest, BitSet set) {
   1346         int nextSetBit = -1;
   1347 
   1348         dest.writeInt(set.cardinality());
   1349 
   1350         while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
   1351             dest.writeInt(nextSetBit);
   1352         }
   1353     }
   1354 
   1355     /** @hide */
   1356     public int getAuthType() {
   1357         if (isValid() == false) {
   1358             throw new IllegalStateException("Invalid configuration");
   1359         }
   1360         if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
   1361             return KeyMgmt.WPA_PSK;
   1362         } else if (allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
   1363             return KeyMgmt.WPA2_PSK;
   1364         } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
   1365             return KeyMgmt.WPA_EAP;
   1366         } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
   1367             return KeyMgmt.IEEE8021X;
   1368         }
   1369         return KeyMgmt.NONE;
   1370     }
   1371 
   1372     /* @hide
   1373      * Cache the config key, this seems useful as a speed up since a lot of
   1374      * lookups in the config store are done and based on this key.
   1375      */
   1376     String mCachedConfigKey;
   1377 
   1378     /** @hide
   1379      *  return the string used to calculate the hash in WifiConfigStore
   1380      *  and uniquely identify this WifiConfiguration
   1381      */
   1382     public String configKey(boolean allowCached) {
   1383         String key;
   1384         if (allowCached && mCachedConfigKey != null) {
   1385             key = mCachedConfigKey;
   1386         } else {
   1387             if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
   1388                 key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
   1389             } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
   1390                     allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
   1391                 key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
   1392             } else if (wepKeys[0] != null) {
   1393                 key = SSID + "WEP";
   1394             } else {
   1395                 key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
   1396             }
   1397             mCachedConfigKey = key;
   1398         }
   1399         return key;
   1400     }
   1401 
   1402     /** @hide
   1403      * get configKey, force calculating the config string
   1404      */
   1405     public String configKey() {
   1406         return configKey(false);
   1407     }
   1408 
   1409     /** @hide
   1410      * return the config key string based on a scan result
   1411      */
   1412     static public String configKey(ScanResult result) {
   1413         String key = "\"" + result.SSID + "\"";
   1414 
   1415         if (result.capabilities.contains("WEP")) {
   1416             key = key + "-WEP";
   1417         }
   1418 
   1419         if (result.capabilities.contains("PSK")) {
   1420             key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
   1421         }
   1422 
   1423         if (result.capabilities.contains("EAP")) {
   1424             key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
   1425         }
   1426 
   1427         return key;
   1428     }
   1429 
   1430     /** @hide */
   1431     public IpConfiguration getIpConfiguration() {
   1432         return mIpConfiguration;
   1433     }
   1434 
   1435     /** @hide */
   1436     public void setIpConfiguration(IpConfiguration ipConfiguration) {
   1437         mIpConfiguration = ipConfiguration;
   1438     }
   1439 
   1440     /** @hide */
   1441     public StaticIpConfiguration getStaticIpConfiguration() {
   1442         return mIpConfiguration.getStaticIpConfiguration();
   1443     }
   1444 
   1445     /** @hide */
   1446     public void setStaticIpConfiguration(StaticIpConfiguration staticIpConfiguration) {
   1447         mIpConfiguration.setStaticIpConfiguration(staticIpConfiguration);
   1448     }
   1449 
   1450     /** @hide */
   1451     public IpConfiguration.IpAssignment getIpAssignment() {
   1452         return mIpConfiguration.ipAssignment;
   1453     }
   1454 
   1455     /** @hide */
   1456     public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
   1457         mIpConfiguration.ipAssignment = ipAssignment;
   1458     }
   1459 
   1460     /** @hide */
   1461     public IpConfiguration.ProxySettings getProxySettings() {
   1462         return mIpConfiguration.proxySettings;
   1463     }
   1464 
   1465     /** @hide */
   1466     public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
   1467         mIpConfiguration.proxySettings = proxySettings;
   1468     }
   1469 
   1470     /** @hide */
   1471     public ProxyInfo getHttpProxy() {
   1472         return mIpConfiguration.httpProxy;
   1473     }
   1474 
   1475     /** @hide */
   1476     public void setHttpProxy(ProxyInfo httpProxy) {
   1477         mIpConfiguration.httpProxy = httpProxy;
   1478     }
   1479 
   1480     /** @hide */
   1481     public void setProxy(ProxySettings settings, ProxyInfo proxy) {
   1482         mIpConfiguration.proxySettings = settings;
   1483         mIpConfiguration.httpProxy = proxy;
   1484     }
   1485 
   1486     /** Implement the Parcelable interface {@hide} */
   1487     public int describeContents() {
   1488         return 0;
   1489     }
   1490 
   1491     /** copy constructor {@hide} */
   1492     public WifiConfiguration(WifiConfiguration source) {
   1493         if (source != null) {
   1494             networkId = source.networkId;
   1495             status = source.status;
   1496             disableReason = source.disableReason;
   1497             disableReason = source.disableReason;
   1498             SSID = source.SSID;
   1499             BSSID = source.BSSID;
   1500             FQDN = source.FQDN;
   1501             naiRealm = source.naiRealm;
   1502             preSharedKey = source.preSharedKey;
   1503 
   1504             wepKeys = new String[4];
   1505             for (int i = 0; i < wepKeys.length; i++) {
   1506                 wepKeys[i] = source.wepKeys[i];
   1507             }
   1508 
   1509             wepTxKeyIndex = source.wepTxKeyIndex;
   1510             priority = source.priority;
   1511             hiddenSSID = source.hiddenSSID;
   1512             allowedKeyManagement   = (BitSet) source.allowedKeyManagement.clone();
   1513             allowedProtocols       = (BitSet) source.allowedProtocols.clone();
   1514             allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
   1515             allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
   1516             allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
   1517 
   1518             enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
   1519 
   1520             defaultGwMacAddress = source.defaultGwMacAddress;
   1521 
   1522             mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
   1523 
   1524             if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) {
   1525                 scanResultCache = new HashMap<String, ScanResult>();
   1526                 scanResultCache.putAll(source.scanResultCache);
   1527             }
   1528 
   1529             if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
   1530                 connectChoices = new HashMap<String, Integer>();
   1531                 connectChoices.putAll(source.connectChoices);
   1532             }
   1533 
   1534             if ((source.linkedConfigurations != null)
   1535                     && (source.linkedConfigurations.size() > 0)) {
   1536                 linkedConfigurations = new HashMap<String, Integer>();
   1537                 linkedConfigurations.putAll(source.linkedConfigurations);
   1538             }
   1539             mCachedConfigKey = null; //force null configKey
   1540             autoJoinStatus = source.autoJoinStatus;
   1541             selfAdded = source.selfAdded;
   1542             validatedInternetAccess = source.validatedInternetAccess;
   1543             ephemeral = source.ephemeral;
   1544             if (source.visibility != null) {
   1545                 visibility = new Visibility(source.visibility);
   1546             }
   1547 
   1548             lastFailure = source.lastFailure;
   1549             didSelfAdd = source.didSelfAdd;
   1550             lastConnectUid = source.lastConnectUid;
   1551             lastUpdateUid = source.lastUpdateUid;
   1552             creatorUid = source.creatorUid;
   1553             peerWifiConfiguration = source.peerWifiConfiguration;
   1554             blackListTimestamp = source.blackListTimestamp;
   1555             lastConnected = source.lastConnected;
   1556             lastDisconnected = source.lastDisconnected;
   1557             lastConnectionFailure = source.lastConnectionFailure;
   1558             lastRoamingFailure = source.lastRoamingFailure;
   1559             lastRoamingFailureReason = source.lastRoamingFailureReason;
   1560             roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
   1561             numConnectionFailures = source.numConnectionFailures;
   1562             numIpConfigFailures = source.numIpConfigFailures;
   1563             numAuthFailures = source.numAuthFailures;
   1564             numScorerOverride = source.numScorerOverride;
   1565             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
   1566             numAssociation = source.numAssociation;
   1567             numUserTriggeredWifiDisableLowRSSI = source.numUserTriggeredWifiDisableLowRSSI;
   1568             numUserTriggeredWifiDisableBadRSSI = source.numUserTriggeredWifiDisableBadRSSI;
   1569             numUserTriggeredWifiDisableNotHighRSSI = source.numUserTriggeredWifiDisableNotHighRSSI;
   1570             numTicksAtLowRSSI = source.numTicksAtLowRSSI;
   1571             numTicksAtBadRSSI = source.numTicksAtBadRSSI;
   1572             numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
   1573             numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
   1574             autoJoinBSSID = source.autoJoinBSSID;
   1575             autoJoinUseAggressiveJoinAttemptThreshold
   1576                     = source.autoJoinUseAggressiveJoinAttemptThreshold;
   1577             autoJoinBailedDueToLowRssi = source.autoJoinBailedDueToLowRssi;
   1578             dirty = source.dirty;
   1579             numNoInternetAccessReports = source.numNoInternetAccessReports;
   1580         }
   1581     }
   1582 
   1583     /** {@hide} */
   1584     //public static final int NOTHING_TAG = 0;
   1585     /** {@hide} */
   1586     //public static final int SCAN_CACHE_TAG = 1;
   1587 
   1588     /** Implement the Parcelable interface {@hide} */
   1589     @Override
   1590     public void writeToParcel(Parcel dest, int flags) {
   1591         dest.writeInt(networkId);
   1592         dest.writeInt(status);
   1593         dest.writeInt(disableReason);
   1594         dest.writeString(SSID);
   1595         dest.writeString(BSSID);
   1596         dest.writeString(autoJoinBSSID);
   1597         dest.writeString(FQDN);
   1598         dest.writeString(naiRealm);
   1599         dest.writeString(preSharedKey);
   1600         for (String wepKey : wepKeys) {
   1601             dest.writeString(wepKey);
   1602         }
   1603         dest.writeInt(wepTxKeyIndex);
   1604         dest.writeInt(priority);
   1605         dest.writeInt(hiddenSSID ? 1 : 0);
   1606         dest.writeInt(requirePMF ? 1 : 0);
   1607         dest.writeString(updateIdentifier);
   1608 
   1609         writeBitSet(dest, allowedKeyManagement);
   1610         writeBitSet(dest, allowedProtocols);
   1611         writeBitSet(dest, allowedAuthAlgorithms);
   1612         writeBitSet(dest, allowedPairwiseCiphers);
   1613         writeBitSet(dest, allowedGroupCiphers);
   1614 
   1615         dest.writeParcelable(enterpriseConfig, flags);
   1616 
   1617         dest.writeParcelable(mIpConfiguration, flags);
   1618         dest.writeString(dhcpServer);
   1619         dest.writeString(defaultGwMacAddress);
   1620         dest.writeInt(autoJoinStatus);
   1621         dest.writeInt(selfAdded ? 1 : 0);
   1622         dest.writeInt(didSelfAdd ? 1 : 0);
   1623         dest.writeInt(validatedInternetAccess ? 1 : 0);
   1624         dest.writeInt(ephemeral ? 1 : 0);
   1625         dest.writeInt(creatorUid);
   1626         dest.writeInt(lastConnectUid);
   1627         dest.writeInt(lastUpdateUid);
   1628         dest.writeLong(blackListTimestamp);
   1629         dest.writeLong(lastConnectionFailure);
   1630         dest.writeLong(lastRoamingFailure);
   1631         dest.writeInt(lastRoamingFailureReason);
   1632         dest.writeLong(roamingFailureBlackListTimeMilli);
   1633         dest.writeInt(numConnectionFailures);
   1634         dest.writeInt(numIpConfigFailures);
   1635         dest.writeInt(numAuthFailures);
   1636         dest.writeInt(numScorerOverride);
   1637         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
   1638         dest.writeInt(numAssociation);
   1639         dest.writeInt(numUserTriggeredWifiDisableLowRSSI);
   1640         dest.writeInt(numUserTriggeredWifiDisableBadRSSI);
   1641         dest.writeInt(numUserTriggeredWifiDisableNotHighRSSI);
   1642         dest.writeInt(numTicksAtLowRSSI);
   1643         dest.writeInt(numTicksAtBadRSSI);
   1644         dest.writeInt(numTicksAtNotHighRSSI);
   1645         dest.writeInt(numUserTriggeredJoinAttempts);
   1646         dest.writeInt(autoJoinUseAggressiveJoinAttemptThreshold);
   1647         dest.writeInt(autoJoinBailedDueToLowRssi ? 1 : 0);
   1648         dest.writeInt(numNoInternetAccessReports);
   1649     }
   1650 
   1651     /** Implement the Parcelable interface {@hide} */
   1652     public static final Creator<WifiConfiguration> CREATOR =
   1653         new Creator<WifiConfiguration>() {
   1654             public WifiConfiguration createFromParcel(Parcel in) {
   1655                 WifiConfiguration config = new WifiConfiguration();
   1656                 config.networkId = in.readInt();
   1657                 config.status = in.readInt();
   1658                 config.disableReason = in.readInt();
   1659                 config.SSID = in.readString();
   1660                 config.BSSID = in.readString();
   1661                 config.autoJoinBSSID = in.readString();
   1662                 config.FQDN = in.readString();
   1663                 config.naiRealm = in.readString();
   1664                 config.preSharedKey = in.readString();
   1665                 for (int i = 0; i < config.wepKeys.length; i++) {
   1666                     config.wepKeys[i] = in.readString();
   1667                 }
   1668                 config.wepTxKeyIndex = in.readInt();
   1669                 config.priority = in.readInt();
   1670                 config.hiddenSSID = in.readInt() != 0;
   1671                 config.requirePMF = in.readInt() != 0;
   1672                 config.updateIdentifier = in.readString();
   1673 
   1674                 config.allowedKeyManagement   = readBitSet(in);
   1675                 config.allowedProtocols       = readBitSet(in);
   1676                 config.allowedAuthAlgorithms  = readBitSet(in);
   1677                 config.allowedPairwiseCiphers = readBitSet(in);
   1678                 config.allowedGroupCiphers    = readBitSet(in);
   1679 
   1680                 config.enterpriseConfig = in.readParcelable(null);
   1681 
   1682                 config.mIpConfiguration = in.readParcelable(null);
   1683                 config.dhcpServer = in.readString();
   1684                 config.defaultGwMacAddress = in.readString();
   1685                 config.autoJoinStatus = in.readInt();
   1686                 config.selfAdded = in.readInt() != 0;
   1687                 config.didSelfAdd = in.readInt() != 0;
   1688                 config.validatedInternetAccess = in.readInt() != 0;
   1689                 config.ephemeral = in.readInt() != 0;
   1690                 config.creatorUid = in.readInt();
   1691                 config.lastConnectUid = in.readInt();
   1692                 config.lastUpdateUid = in.readInt();
   1693                 config.blackListTimestamp = in.readLong();
   1694                 config.lastConnectionFailure = in.readLong();
   1695                 config.lastRoamingFailure = in.readLong();
   1696                 config.lastRoamingFailureReason = in.readInt();
   1697                 config.roamingFailureBlackListTimeMilli = in.readLong();
   1698                 config.numConnectionFailures = in.readInt();
   1699                 config.numIpConfigFailures = in.readInt();
   1700                 config.numAuthFailures = in.readInt();
   1701                 config.numScorerOverride = in.readInt();
   1702                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
   1703                 config.numAssociation = in.readInt();
   1704                 config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
   1705                 config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
   1706                 config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
   1707                 config.numTicksAtLowRSSI = in.readInt();
   1708                 config.numTicksAtBadRSSI = in.readInt();
   1709                 config.numTicksAtNotHighRSSI = in.readInt();
   1710                 config.numUserTriggeredJoinAttempts = in.readInt();
   1711                 config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
   1712                 config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
   1713                 config.numNoInternetAccessReports = in.readInt();
   1714                 return config;
   1715             }
   1716 
   1717             public WifiConfiguration[] newArray(int size) {
   1718                 return new WifiConfiguration[size];
   1719             }
   1720         };
   1721 }
   1722