WifiConfigStore.java revision 3b26801d62a06475b722bbf29cba7f48f376654e
1/*
2 * Copyright (C) 2010 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 com.android.server.wifi;
18
19import android.content.Context;
20import android.content.Intent;
21import android.net.IpConfiguration;
22import android.net.IpConfiguration.IpAssignment;
23import android.net.IpConfiguration.ProxySettings;
24import android.net.LinkAddress;
25import android.net.NetworkInfo.DetailedState;
26import android.net.ProxyInfo;
27import android.net.RouteInfo;
28import android.net.StaticIpConfiguration;
29import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiConfiguration.KeyMgmt;
31import android.net.wifi.WifiConfiguration.Status;
32import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
33
34import android.net.wifi.WifiEnterpriseConfig;
35import android.net.wifi.WifiManager;
36import android.net.wifi.WifiSsid;
37import android.net.wifi.WpsInfo;
38import android.net.wifi.WpsResult;
39import android.net.wifi.ScanResult;
40import android.net.wifi.WifiInfo;
41
42import android.os.Environment;
43import android.os.FileObserver;
44import android.os.Process;
45import android.os.SystemClock;
46import android.os.UserHandle;
47import android.provider.Settings;
48import android.security.Credentials;
49import android.security.KeyChain;
50import android.security.KeyStore;
51import android.text.TextUtils;
52import android.util.LocalLog;
53import android.util.Log;
54import android.util.SparseArray;
55
56import com.android.server.net.DelayedDiskWrite;
57import com.android.server.net.IpConfigStore;
58
59import java.io.BufferedReader;
60import java.io.BufferedInputStream;
61import java.io.DataInputStream;
62import java.io.DataOutputStream;
63import java.io.EOFException;
64import java.io.File;
65import java.io.FileDescriptor;
66import java.io.FileInputStream;
67import java.io.FileNotFoundException;
68import java.io.FileReader;
69import java.io.IOException;
70import java.io.PrintWriter;
71import java.math.BigInteger;
72import java.net.InetAddress;
73import java.nio.charset.Charset;
74import java.security.PrivateKey;
75import java.security.cert.Certificate;
76import java.security.cert.CertificateException;
77import java.text.SimpleDateFormat;
78import java.text.DateFormat;
79import java.util.regex.Matcher;
80import java.util.regex.Pattern;
81import java.util.*;
82
83/**
84 * This class provides the API to manage configured
85 * wifi networks. The API is not thread safe is being
86 * used only from WifiStateMachine.
87 *
88 * It deals with the following
89 * - Add/update/remove a WifiConfiguration
90 *   The configuration contains two types of information.
91 *     = IP and proxy configuration that is handled by WifiConfigStore and
92 *       is saved to disk on any change.
93 *
94 *       The format of configuration file is as follows:
95 *       <version>
96 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
97 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
98 *       ..
99 *
100 *       (key, value) pairs for a given network are grouped together and can
101 *       be in any order. A EOS at the end of a set of (key, value) pairs
102 *       indicates that the next set of (key, value) pairs are for a new
103 *       network. A network is identified by a unique ID_KEY. If there is no
104 *       ID_KEY in the (key, value) pairs, the data is discarded.
105 *
106 *       An invalid version on read would result in discarding the contents of
107 *       the file. On the next write, the latest version is written to file.
108 *
109 *       Any failures during read or write to the configuration file are ignored
110 *       without reporting to the user since the likelihood of these errors are
111 *       low and the impact on connectivity is low.
112 *
113 *     = SSID & security details that is pushed to the supplicant.
114 *       supplicant saves these details to the disk on calling
115 *       saveConfigCommand().
116 *
117 *       We have two kinds of APIs exposed:
118 *        > public API calls that provide fine grained control
119 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
120 *          removeNetwork(). For these calls, the config is not persisted
121 *          to the disk. (TODO: deprecate these calls in WifiManager)
122 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
123 *          These calls persist the supplicant config to disk.
124 *
125 * - Maintain a list of configured networks for quick access
126 *
127 */
128public class WifiConfigStore extends IpConfigStore {
129
130    private Context mContext;
131    private static final String TAG = "WifiConfigStore";
132    private static final boolean DBG = true;
133    private static boolean VDBG = false;
134
135    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
136
137    /* configured networks with network id as the key */
138    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
139            new HashMap<Integer, WifiConfiguration>();
140
141    /* A network id is a unique identifier for a network configured in the
142     * supplicant. Network ids are generated when the supplicant reads
143     * the configuration file at start and can thus change for networks.
144     * We store the IP configuration for networks along with a unique id
145     * that is generated from SSID and security type of the network. A mapping
146     * from the generated unique id to network id of the network is needed to
147     * map supplicant config to IP configuration. */
148    private HashMap<Integer, Integer> mNetworkIds =
149            new HashMap<Integer, Integer>();
150
151    /* Tracks the highest priority of configured networks */
152    private int mLastPriority = -1;
153
154    private static final String ipConfigFile = Environment.getDataDirectory() +
155            "/misc/wifi/ipconfig.txt";
156
157    private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
158            "/misc/wifi/networkHistory.txt";
159
160    private static final String autoJoinConfigFile = Environment.getDataDirectory() +
161            "/misc/wifi/autojoinconfig.txt";
162
163    /* Network History Keys */
164    private static final String SSID_KEY = "SSID:  ";
165    private static final String CONFIG_KEY = "CONFIG:  ";
166    private static final String CHOICE_KEY = "CHOICE:  ";
167    private static final String LINK_KEY = "LINK:  ";
168    private static final String BSSID_KEY = "BSSID:  ";
169    private static final String BSSID_KEY_END = "/BSSID:  ";
170    private static final String RSSI_KEY = "RSSI:  ";
171    private static final String FREQ_KEY = "FREQ:  ";
172    private static final String DATE_KEY = "DATE:  ";
173    private static final String MILLI_KEY = "MILLI:  ";
174    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI:  ";
175    private static final String NETWORK_ID_KEY = "ID:  ";
176    private static final String PRIORITY_KEY = "PRIORITY:  ";
177    private static final String DEFAULT_GW_KEY = "DEFAULT_GW:  ";
178    private static final String AUTH_KEY = "AUTH:  ";
179    private static final String SEPARATOR_KEY = "\n";
180    private static final String STATUS_KEY = "AUTO_JOIN_STATUS:  ";
181    private static final String BSSID_STATUS_KEY = "BSSID_STATUS:  ";
182    private static final String SELF_ADDED_KEY = "SELF_ADDED:  ";
183    private static final String FAILURE_KEY = "FAILURE:  ";
184    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD:  ";
185    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION:  ";
186    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY:  ";
187    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY:  ";
188    private static final String UPDATE_UID_KEY = "UPDATE_UID:  ";
189    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS:  ";
190    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON:  ";
191    private static final String FQDN_KEY = "FQDN:  ";
192    private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES:  ";
193    private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE:  ";
194    private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH:  ";
195    private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION:  ";
196    private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
197            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G:  ";
198    private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
199            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G:  ";
200    private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
201            = "THRESHOLD_UNBLACKLIST_HARD_5G:  ";
202    private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
203            = "THRESHOLD_UNBLACKLIST_SOFT_5G:  ";
204    private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
205            = "THRESHOLD_UNBLACKLIST_HARD_24G:  ";
206    private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
207            = "THRESHOLD_UNBLACKLIST_SOFT_24G:  ";
208    private static final String THRESHOLD_GOOD_RSSI_5_KEY
209            = "THRESHOLD_GOOD_RSSI_5:  ";
210    private static final String THRESHOLD_LOW_RSSI_5_KEY
211            = "THRESHOLD_LOW_RSSI_5:  ";
212    private static final String THRESHOLD_BAD_RSSI_5_KEY
213            = "THRESHOLD_BAD_RSSI_5:  ";
214    private static final String THRESHOLD_GOOD_RSSI_24_KEY
215            = "THRESHOLD_GOOD_RSSI_24:  ";
216    private static final String THRESHOLD_LOW_RSSI_24_KEY
217            = "THRESHOLD_LOW_RSSI_24:  ";
218    private static final String THRESHOLD_BAD_RSSI_24_KEY
219            = "THRESHOLD_BAD_RSSI_24:  ";
220    private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
221            = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING:   ";
222    private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
223            = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING:   ";
224
225    private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
226            "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW:   ";
227    private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
228            "A_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
229    private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
230            "G_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
231
232    private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
233            = "ENABLE_AUTOJOIN_WHILE_ASSOCIATED:   ";
234
235    public boolean enableAutoJoinWhileAssociated = true;
236
237    public int maxTxPacketForNetworkSwitching = 40;
238    public int maxRxPacketForNetworkSwitching = 80;
239
240    public int thresholdInitialAutoJoinAttemptMin5RSSI
241            = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5;
242    public int thresholdInitialAutoJoinAttemptMin24RSSI
243            = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24;
244
245    public int thresholdBadRssi5 = WifiConfiguration.BAD_RSSI_5;
246    public int thresholdLowRssi5 = WifiConfiguration.LOW_RSSI_5;
247    public int thresholdGoodRssi5 = WifiConfiguration.GOOD_RSSI_5;
248    public int thresholdBadRssi24 = WifiConfiguration.BAD_RSSI_24;
249    public int thresholdLowRssi24 = WifiConfiguration.LOW_RSSI_24;
250    public int thresholdGoodRssi24 = WifiConfiguration.GOOD_RSSI_24;
251
252    public int thresholdBandPreferenceRssi24
253            = WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD;
254    public int thresholdBandPreferenceRssi5
255            = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD;
256    public int thresholdBandPreferenceLowRssi5
257            = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW;
258
259    public int thresholdUnblacklistThreshold5Hard
260            = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD;
261    public int thresholdUnblacklistThreshold5Soft
262            = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT;
263    public int thresholdUnblacklistThreshold24Hard
264            = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD;
265    public int thresholdUnblacklistThreshold24Soft
266            = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT;
267
268    /**
269     * Regex pattern for extracting a connect choice.
270     * Matches a strings like the following:
271     * <configKey>=([0:9]+)
272     */
273    private static Pattern mConnectChoice =
274            Pattern.compile("(.*)=([0-9]+)");
275
276
277    /* Enterprise configuration keys */
278    /**
279     * In old configurations, the "private_key" field was used. However, newer
280     * configurations use the key_id field with the engine_id set to "keystore".
281     * If this field is found in the configuration, the migration code is
282     * triggered.
283     */
284    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
285
286    /**
287     * This represents an empty value of an enterprise field.
288     * NULL is used at wpa_supplicant to indicate an empty value
289     */
290    static final String EMPTY_VALUE = "NULL";
291
292    // Internal use only
293    private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
294            WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
295            WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
296            WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
297            WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
298            WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
299            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
300
301
302    /**
303     * The maximum number of times we will retry a connection to an access point
304     * for which we have failed in acquiring an IP address from DHCP. A value of
305     * N means that we will make N+1 connection attempts in all.
306     * <p>
307     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
308     * value if a Settings value is not present.
309     */
310    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
311
312
313    private final LocalLog mLocalLog;
314    private final WpaConfigFileObserver mFileObserver;
315
316    private WifiNative mWifiNative;
317    private final KeyStore mKeyStore = KeyStore.getInstance();
318
319    /**
320     * The lastSelectedConfiguration is used to remember which network
321     * was selected last by the user.
322     * The connection to this network may not be successful, as well
323     * the selection (i.e. network priority) might not be persisted.
324     * WiFi state machine is the only object that sets this variable.
325     */
326    private String lastSelectedConfiguration = null;
327
328    WifiConfigStore(Context c, WifiNative wn) {
329        mContext = c;
330        mWifiNative = wn;
331
332        if (VDBG) {
333            mLocalLog = mWifiNative.getLocalLog();
334            mFileObserver = new WpaConfigFileObserver();
335            mFileObserver.startWatching();
336        } else {
337            mLocalLog = null;
338            mFileObserver = null;
339        }
340    }
341
342    void enableVerboseLogging(int verbose) {
343        if (verbose > 0) {
344            VDBG = true;
345        } else {
346            VDBG = false;
347        }
348    }
349
350    class WpaConfigFileObserver extends FileObserver {
351
352        public WpaConfigFileObserver() {
353            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
354        }
355
356        @Override
357        public void onEvent(int event, String path) {
358            if (event == CLOSE_WRITE) {
359                File file = new File(SUPPLICANT_CONFIG_FILE);
360                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
361            }
362        }
363    }
364
365
366    /**
367     * Fetch the list of configured networks
368     * and enable all stored networks in supplicant.
369     */
370    void loadAndEnableAllNetworks() {
371        if (DBG) log("Loading config and enabling all networks ");
372        loadConfiguredNetworks();
373        enableAllNetworks();
374    }
375
376    int getConfiguredNetworksSize() {
377        return mConfiguredNetworks.size();
378    }
379
380    private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) {
381        List<WifiConfiguration> networks = new ArrayList<>();
382        for(WifiConfiguration config : mConfiguredNetworks.values()) {
383            WifiConfiguration newConfig = new WifiConfiguration(config);
384            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
385                //do not enumerate and return this configuration to any one,
386                //for instance WiFi Picker.
387                //instead treat it as unknown. the configuration can still be retrieved
388                //directly by the key or networkId
389                continue;
390            }
391            if (pskMap != null && config.allowedKeyManagement != null
392                    && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
393                    && pskMap.containsKey(config.SSID)) {
394                newConfig.preSharedKey = pskMap.get(config.SSID);
395            }
396            networks.add(newConfig);
397        }
398        return networks;
399    }
400
401    /**
402     * Fetch the list of currently configured networks
403     * @return List of networks
404     */
405    List<WifiConfiguration> getConfiguredNetworks() {
406        return getConfiguredNetworks(null);
407    }
408
409    /**
410     * Fetch the list of currently configured networks, filled with real preSharedKeys
411     * @return List of networks
412     */
413    List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
414        Map<String, String> pskMap = getCredentialsBySsidMap();
415        return getConfiguredNetworks(pskMap);
416    }
417
418    /**
419     * Fetch the preSharedKeys for all networks.
420     * @return a map from Ssid to preSharedKey.
421     */
422    private Map<String, String> getCredentialsBySsidMap() {
423        return readNetworkVariablesFromSupplicantFile("psk");
424    }
425
426    /**
427     * Fetch the list of currently configured networks that were recently seen
428     *
429     * @return List of networks
430     */
431    List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
432        List<WifiConfiguration> networks = null;
433
434        for (WifiConfiguration config : mConfiguredNetworks.values()) {
435            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
436                // Do not enumerate and return this configuration to any one,
437                // instead treat it as unknown. the configuration can still be retrieved
438                // directly by the key or networkId
439                continue;
440            }
441
442            // Calculate the RSSI for scan results that are more recent than milli
443            config.setVisibility(milli);
444
445            if (config.visibility == null) {
446                continue;
447            }
448            if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
449                    config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
450                continue;
451            }
452            if (networks == null)
453                networks = new ArrayList<WifiConfiguration>();
454            if (copy) {
455                networks.add(new WifiConfiguration(config));
456            } else {
457                networks.add(config);
458            }
459        }
460        return networks;
461    }
462
463    /**
464     *  Update the configuration and BSSID with latest RSSI value.
465     */
466    void updateConfiguration(WifiInfo info) {
467        WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
468        if (config != null && config.scanResultCache != null) {
469            ScanResult result = config.scanResultCache.get(info.getBSSID());
470            if (result != null) {
471                long previousSeen = result.seen;
472                int previousRssi = result.level;
473
474                // Update the scan result
475                result.seen = System.currentTimeMillis();
476                result.level = info.getRssi();
477
478                // Average the RSSI value
479                result.averageRssi(previousRssi, previousSeen,
480                        WifiAutoJoinController.mScanResultMaximumAge);
481                if (VDBG) {
482                    loge("updateConfiguration freq=" + result.frequency
483                        + " BSSID=" + result.BSSID
484                        + " RSSI=" + result.level
485                        + " " + config.configKey());
486                }
487            }
488        }
489    }
490
491    /**
492     * get the Wificonfiguration for this netId
493     *
494     * @return Wificonfiguration
495     */
496    WifiConfiguration getWifiConfiguration(int netId) {
497        if (mConfiguredNetworks == null)
498            return null;
499        return mConfiguredNetworks.get(netId);
500    }
501
502    /**
503     * Get the Wificonfiguration for this key
504     * @return Wificonfiguration
505     */
506    WifiConfiguration getWifiConfiguration(String key) {
507        if (key == null)
508            return null;
509        int hash = key.hashCode();
510        if (mNetworkIds == null)
511            return null;
512        Integer n = mNetworkIds.get(hash);
513        if (n == null)
514            return null;
515        int netId = n.intValue();
516        return getWifiConfiguration(netId);
517    }
518
519    /**
520     * Enable all networks and save config. This will be a no-op if the list
521     * of configured networks indicates all networks as being enabled
522     */
523    void enableAllNetworks() {
524        boolean networkEnabledStateChanged = false;
525        for(WifiConfiguration config : mConfiguredNetworks.values()) {
526            if(config != null && config.status == Status.DISABLED
527                    && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) {
528                if(mWifiNative.enableNetwork(config.networkId, false)) {
529                    networkEnabledStateChanged = true;
530                    config.status = Status.ENABLED;
531                } else {
532                    loge("Enable network failed on " + config.networkId);
533                }
534            }
535        }
536
537        if (networkEnabledStateChanged) {
538            mWifiNative.saveConfig();
539            sendConfiguredNetworksChangedBroadcast();
540        }
541    }
542
543
544    /**
545     * Selects the specified network for connection. This involves
546     * updating the priority of all the networks and enabling the given
547     * network while disabling others.
548     *
549     * Selecting a network will leave the other networks disabled and
550     * a call to enableAllNetworks() needs to be issued upon a connection
551     * or a failure event from supplicant
552     *
553     * @param netId network to select for connection
554     * @return false if the network id is invalid
555     */
556    boolean selectNetwork(int netId) {
557        if (VDBG) localLog("selectNetwork", netId);
558        if (netId == INVALID_NETWORK_ID) return false;
559
560        // Reset the priority of each network at start or if it goes too high.
561        if (mLastPriority == -1 || mLastPriority > 1000000) {
562            for(WifiConfiguration config : mConfiguredNetworks.values()) {
563                if (config.networkId != INVALID_NETWORK_ID) {
564                    config.priority = 0;
565                    addOrUpdateNetworkNative(config);
566                }
567            }
568            mLastPriority = 0;
569        }
570
571        // Set to the highest priority and save the configuration.
572        WifiConfiguration config = new WifiConfiguration();
573        config.networkId = netId;
574        config.priority = ++mLastPriority;
575
576        addOrUpdateNetworkNative(config);
577        mWifiNative.saveConfig();
578
579        /* Enable the given network while disabling all other networks */
580        enableNetworkWithoutBroadcast(netId, true);
581
582       /* Avoid saving the config & sending a broadcast to prevent settings
583        * from displaying a disabled list of networks */
584        return true;
585    }
586
587    /**
588     * Add/update the specified configuration and save config
589     *
590     * @param config WifiConfiguration to be saved
591     * @return network update result
592     */
593    NetworkUpdateResult saveNetwork(WifiConfiguration config) {
594        WifiConfiguration conf;
595
596        // A new network cannot have null SSID
597        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
598                config.SSID == null)) {
599            return new NetworkUpdateResult(INVALID_NETWORK_ID);
600        }
601        if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
602        if (VDBG) {
603            loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
604                    + " SSID=" + config.SSID
605                    + " Uid=" + Integer.toString(config.creatorUid)
606                    + "/" + Integer.toString(config.lastUpdateUid));
607        }
608        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
609        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
610        int netId = result.getNetworkId();
611
612        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
613
614        /* enable a new network */
615        if (newNetwork && netId != INVALID_NETWORK_ID) {
616            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
617
618            mWifiNative.enableNetwork(netId, false);
619            conf = mConfiguredNetworks.get(netId);
620            if (conf != null)
621                conf.status = Status.ENABLED;
622        }
623
624        conf = mConfiguredNetworks.get(netId);
625        if (conf != null) {
626            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
627                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
628
629                // reenable autojoin, since new information has been provided
630                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
631                enableNetworkWithoutBroadcast(conf.networkId, false);
632            }
633            if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId="
634                    + Integer.toString(netId)
635                    + " uid=" + Integer.toString(config.creatorUid));
636        }
637
638        mWifiNative.saveConfig();
639        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
640                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
641        return result;
642    }
643
644    void updateStatus(int netId, DetailedState state) {
645        if (netId != INVALID_NETWORK_ID) {
646            WifiConfiguration config = mConfiguredNetworks.get(netId);
647            if (config == null) return;
648            switch (state) {
649                case CONNECTED:
650                    config.status = Status.CURRENT;
651                    //we successfully connected, hence remove the blacklist
652                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
653                    break;
654                case DISCONNECTED:
655                    //If network is already disabled, keep the status
656                    if (config.status == Status.CURRENT) {
657                        config.status = Status.ENABLED;
658                    }
659                    break;
660                default:
661                    //do nothing, retain the existing state
662                    break;
663            }
664        }
665    }
666
667    /**
668     * Forget the specified network and save config
669     *
670     * @param netId network to forget
671     * @return {@code true} if it succeeds, {@code false} otherwise
672     */
673    boolean forgetNetwork(int netId) {
674        if (VDBG) localLog("forgetNetwork", netId);
675
676        boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
677        if (!remove) {
678            //success but we dont want to remove the network from supplicant conf file
679            return true;
680        }
681        if (mWifiNative.removeNetwork(netId)) {
682            mWifiNative.saveConfig();
683            return true;
684        } else {
685            loge("Failed to remove network " + netId);
686            return false;
687        }
688    }
689
690    /**
691     * Add/update a network. Note that there is no saveConfig operation.
692     * This function is retained for compatibility with the public
693     * API. The more powerful saveNetwork() is used by the
694     * state machine
695     *
696     * @param config wifi configuration to add/update
697     * @return network Id
698     */
699    int addOrUpdateNetwork(WifiConfiguration config) {
700        if (VDBG) localLog("addOrUpdateNetwork id=", config.networkId);
701        //adding unconditional message to chase b/15111865
702        Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
703                + " uid=" + Integer.toString(config.creatorUid)
704                + "/" + Integer.toString(config.lastUpdateUid));
705        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
706        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
707            WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
708            if (conf != null) {
709                sendConfiguredNetworksChangedBroadcast(conf,
710                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
711                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
712            }
713        }
714        return result.getNetworkId();
715    }
716
717    /**
718     * Remove a network. Note that there is no saveConfig operation.
719     * This function is retained for compatibility with the public
720     * API. The more powerful forgetNetwork() is used by the
721     * state machine for network removal
722     *
723     * @param netId network to be removed
724     * @return {@code true} if it succeeds, {@code false} otherwise
725     */
726    boolean removeNetwork(int netId) {
727        if (VDBG) localLog("removeNetwork", netId);
728        boolean ret = mWifiNative.removeNetwork(netId);
729        if (ret) {
730            removeConfigAndSendBroadcastIfNeeded(netId);
731        }
732        return ret;
733    }
734
735    private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
736        boolean remove = true;
737        WifiConfiguration config = mConfiguredNetworks.get(netId);
738        if (config != null) {
739            if (VDBG) {
740                loge("removeNetwork " + Integer.toString(netId) + " key=" +
741                        config.configKey() + " config.id=" + Integer.toString(config.networkId));
742            }
743
744            // cancel the last user choice
745            if (config.configKey().equals(lastSelectedConfiguration)) {
746                lastSelectedConfiguration = null;
747            }
748
749            // Remove any associated keys
750            if (config.enterpriseConfig != null) {
751                removeKeys(config.enterpriseConfig);
752            }
753
754            if (config.didSelfAdd) {
755                if (config.peerWifiConfiguration != null) {
756                    for (WifiConfiguration peer : mConfiguredNetworks.values()) {
757                        if (config.peerWifiConfiguration.equals(peer.configKey())) {
758                            /* the configuration that trigger the add is still there */
759                            remove = false;
760                        }
761                    }
762                }
763            }
764
765            if (remove) {
766                mConfiguredNetworks.remove(netId);
767                mNetworkIds.remove(configKey(config));
768            } else {
769                /* we can't directly remove the configuration since we added it ourselves, because
770                 * that could cause the system to re-add it right away.
771                 * Instead black list it. It will be unblacklisted only thru a new add.
772                 */
773                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DELETED);
774                mWifiNative.disableNetwork(config.networkId);
775                remove = false;
776            }
777
778            writeIpAndProxyConfigurations();
779            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
780            writeKnownNetworkHistory();
781        }
782        return remove;
783    }
784
785    /**
786     * Enable a network. Note that there is no saveConfig operation.
787     * This function is retained for compatibility with the public
788     * API. The more powerful selectNetwork()/saveNetwork() is used by the
789     * state machine for connecting to a network
790     *
791     * @param netId network to be enabled
792     * @return {@code true} if it succeeds, {@code false} otherwise
793     */
794    boolean enableNetwork(int netId, boolean disableOthers) {
795        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
796        if (disableOthers) {
797            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
798            sendConfiguredNetworksChangedBroadcast();
799        } else {
800            if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
801            WifiConfiguration enabledNetwork = null;
802            synchronized(mConfiguredNetworks) {
803                enabledNetwork = mConfiguredNetworks.get(netId);
804            }
805            // check just in case the network was removed by someone else.
806            if (enabledNetwork != null) {
807                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
808                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
809            }
810        }
811        return ret;
812    }
813
814    boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
815        boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
816
817        WifiConfiguration config = mConfiguredNetworks.get(netId);
818        if (config != null) config.status = Status.ENABLED;
819
820        if (disableOthers) {
821            markAllNetworksDisabledExcept(netId);
822        }
823        return ret;
824    }
825
826    void disableAllNetworks() {
827        if (VDBG) localLog("disableAllNetworks");
828        boolean networkDisabled = false;
829        for(WifiConfiguration config : mConfiguredNetworks.values()) {
830            if(config != null && config.status != Status.DISABLED) {
831                if(mWifiNative.disableNetwork(config.networkId)) {
832                    networkDisabled = true;
833                    config.status = Status.DISABLED;
834                } else {
835                    loge("Disable network failed on " + config.networkId);
836                }
837            }
838        }
839
840        if (networkDisabled) {
841            sendConfiguredNetworksChangedBroadcast();
842        }
843    }
844    /**
845     * Disable a network. Note that there is no saveConfig operation.
846     * @param netId network to be disabled
847     * @return {@code true} if it succeeds, {@code false} otherwise
848     */
849    boolean disableNetwork(int netId) {
850        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
851    }
852
853    /**
854     * Disable a network. Note that there is no saveConfig operation.
855     * @param netId network to be disabled
856     * @param reason reason code network was disabled
857     * @return {@code true} if it succeeds, {@code false} otherwise
858     */
859    boolean disableNetwork(int netId, int reason) {
860        if (VDBG) localLog("disableNetwork", netId);
861        boolean ret = mWifiNative.disableNetwork(netId);
862        WifiConfiguration network = null;
863        WifiConfiguration config = mConfiguredNetworks.get(netId);
864
865        if (VDBG) {
866            if (config != null) {
867                loge("disableNetwork netId=" + Integer.toString(netId)
868                        + " SSID=" + config.SSID
869                        + " disabled=" + (config.status == Status.DISABLED)
870                        + " reason=" + Integer.toString(config.disableReason));
871            }
872        }
873        /* Only change the reason if the network was not previously disabled */
874        if (config != null && config.status != Status.DISABLED) {
875            config.status = Status.DISABLED;
876            config.disableReason = reason;
877            network = config;
878        }
879        if (network != null) {
880            sendConfiguredNetworksChangedBroadcast(network,
881                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
882        }
883        return ret;
884    }
885
886    /**
887     * Save the configured networks in supplicant to disk
888     * @return {@code true} if it succeeds, {@code false} otherwise
889     */
890    boolean saveConfig() {
891        return mWifiNative.saveConfig();
892    }
893
894    /**
895     * Start WPS pin method configuration with pin obtained
896     * from the access point
897     * @param config WPS configuration
898     * @return Wps result containing status and pin
899     */
900    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
901        WpsResult result = new WpsResult();
902        if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
903            /* WPS leaves all networks disabled */
904            markAllNetworksDisabled();
905            result.status = WpsResult.Status.SUCCESS;
906        } else {
907            loge("Failed to start WPS pin method configuration");
908            result.status = WpsResult.Status.FAILURE;
909        }
910        return result;
911    }
912
913    /**
914     * Start WPS pin method configuration with pin obtained
915     * from the device
916     * @return WpsResult indicating status and pin
917     */
918    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
919        WpsResult result = new WpsResult();
920        result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
921        /* WPS leaves all networks disabled */
922        if (!TextUtils.isEmpty(result.pin)) {
923            markAllNetworksDisabled();
924            result.status = WpsResult.Status.SUCCESS;
925        } else {
926            loge("Failed to start WPS pin method configuration");
927            result.status = WpsResult.Status.FAILURE;
928        }
929        return result;
930    }
931
932    /**
933     * Start WPS push button configuration
934     * @param config WPS configuration
935     * @return WpsResult indicating status and pin
936     */
937    WpsResult startWpsPbc(WpsInfo config) {
938        WpsResult result = new WpsResult();
939        if (mWifiNative.startWpsPbc(config.BSSID)) {
940            /* WPS leaves all networks disabled */
941            markAllNetworksDisabled();
942            result.status = WpsResult.Status.SUCCESS;
943        } else {
944            loge("Failed to start WPS push button configuration");
945            result.status = WpsResult.Status.FAILURE;
946        }
947        return result;
948    }
949
950    /**
951     * Fetch the static IP configuration for a given network id
952     */
953    StaticIpConfiguration getStaticIpConfiguration(int netId) {
954        WifiConfiguration config = mConfiguredNetworks.get(netId);
955        if (config != null) {
956            return config.getStaticIpConfiguration();
957        }
958        return null;
959    }
960
961    /**
962     * Set the static IP configuration for a given network id
963     */
964    void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
965        WifiConfiguration config = mConfiguredNetworks.get(netId);
966        if (config != null) {
967            config.setStaticIpConfiguration(staticIpConfiguration);
968        }
969    }
970
971    /**
972     * set default GW MAC address
973     */
974    void setDefaultGwMacAddress(int netId, String macAddress) {
975        WifiConfiguration config = mConfiguredNetworks.get(netId);
976        if (config != null) {
977            //update defaultGwMacAddress
978            config.defaultGwMacAddress = macAddress;
979        }
980    }
981
982
983    /**
984     * Fetch the proxy properties for a given network id
985     * @param network id
986     * @return ProxyInfo for the network id
987     */
988    ProxyInfo getProxyProperties(int netId) {
989        WifiConfiguration config = mConfiguredNetworks.get(netId);
990        if (config != null) {
991            return config.getHttpProxy();
992        }
993        return null;
994    }
995
996    /**
997     * Return if the specified network is using static IP
998     * @param network id
999     * @return {@code true} if using static ip for netId
1000     */
1001    boolean isUsingStaticIp(int netId) {
1002        WifiConfiguration config = mConfiguredNetworks.get(netId);
1003        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
1004            return true;
1005        }
1006        return false;
1007    }
1008
1009    /**
1010     * Should be called when a single network configuration is made.
1011     * @param network The network configuration that changed.
1012     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
1013     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
1014     */
1015    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
1016            int reason) {
1017        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1018        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1019        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
1020        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
1021        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
1022        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1023    }
1024
1025    /**
1026     * Should be called when multiple network configuration changes are made.
1027     */
1028    private void sendConfiguredNetworksChangedBroadcast() {
1029        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1030        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1031        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
1032        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1033    }
1034
1035    void loadConfiguredNetworks() {
1036        String listStr = mWifiNative.listNetworks();
1037        mLastPriority = 0;
1038
1039        mConfiguredNetworks.clear();
1040        mNetworkIds.clear();
1041
1042        if (listStr == null)
1043            return;
1044
1045        String[] lines = listStr.split("\n");
1046
1047        if (VDBG) {
1048            loge("loadConfiguredNetworks: found " + Integer.toString(lines.length)
1049                    + " networks", true);
1050        }
1051
1052        // Skip the first line, which is a header
1053        for (int i = 1; i < lines.length; i++) {
1054            String[] result = lines[i].split("\t");
1055            // network-id | ssid | bssid | flags
1056            WifiConfiguration config = new WifiConfiguration();
1057            try {
1058                config.networkId = Integer.parseInt(result[0]);
1059            } catch(NumberFormatException e) {
1060                loge("Failed to read network-id '" + result[0] + "'");
1061                continue;
1062            }
1063            if (result.length > 3) {
1064                if (result[3].indexOf("[CURRENT]") != -1)
1065                    config.status = WifiConfiguration.Status.CURRENT;
1066                else if (result[3].indexOf("[DISABLED]") != -1)
1067                    config.status = WifiConfiguration.Status.DISABLED;
1068                else
1069                    config.status = WifiConfiguration.Status.ENABLED;
1070            } else {
1071                config.status = WifiConfiguration.Status.ENABLED;
1072            }
1073            readNetworkVariables(config);
1074            if (config.priority > mLastPriority) {
1075                mLastPriority = config.priority;
1076            }
1077
1078            config.setIpAssignment(IpAssignment.DHCP);
1079            config.setProxySettings(ProxySettings.NONE);
1080
1081            if (mNetworkIds.containsKey(configKey(config))) {
1082                // That SSID is already known, just ignore this duplicate entry
1083                if (VDBG) localLog("discarded duplicate network ", config.networkId);
1084            } else if(config.isValid()){
1085                mConfiguredNetworks.put(config.networkId, config);
1086                mNetworkIds.put(configKey(config), config.networkId);
1087                if (VDBG) localLog("loaded configured network", config.networkId);
1088            } else {
1089                if (DBG) log("Ignoring loaded configured for network " + config.networkId
1090                    + " because config are not valid");
1091            }
1092        }
1093
1094        readIpAndProxyConfigurations();
1095        readNetworkHistory();
1096        readAutoJoinConfig();
1097
1098        sendConfiguredNetworksChangedBroadcast();
1099
1100        if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
1101
1102        if (mNetworkIds.size() == 0) {
1103            // no networks? Lets log if the wpa_supplicant.conf file contents
1104            BufferedReader reader = null;
1105            try {
1106                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1107                if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---");
1108                for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1109                    if (VDBG) localLog(line);
1110                }
1111                if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---");
1112            } catch (FileNotFoundException e) {
1113                if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1114            } catch (IOException e) {
1115                if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1116            } finally {
1117                try {
1118                    if (reader != null) {
1119                        reader.close();
1120                    }
1121                } catch (IOException e) {
1122                    // Just ignore the fact that we couldn't close
1123                }
1124            }
1125        }
1126    }
1127
1128    private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
1129        Map<String, String> result = new HashMap<>();
1130        BufferedReader reader = null;
1131        if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key);
1132
1133        try {
1134            reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1135            boolean found = false;
1136            String networkSsid = null;
1137            String value = null;
1138
1139            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1140                if (VDBG) loge(line);
1141
1142                if (line.matches("[ \\t]*network=\\{")) {
1143                    found = true;
1144                    networkSsid = null;
1145                    value = null;
1146                } else if (line.matches("[ \\t]*\\{")) {
1147                    found = false;
1148                    networkSsid = null;
1149                    value = null;
1150                }
1151
1152                if (found) {
1153                    int index;
1154                    if ((index = line.indexOf("ssid=")) >= 0) {
1155                        networkSsid = line.substring(index + 5);
1156                    } else if ((index = line.indexOf(key + "=")) >= 0) {
1157                        value = line.substring(index + key.length() + 1);
1158                    }
1159
1160                    if (networkSsid != null && value != null) {
1161                        result.put(networkSsid, value);
1162                    }
1163                }
1164            }
1165        } catch (FileNotFoundException e) {
1166            if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1167        } catch (IOException e) {
1168            if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1169        } finally {
1170            try {
1171                if (reader != null) {
1172                    reader.close();
1173                }
1174            } catch (IOException e) {
1175                // Just ignore the fact that we couldn't close
1176            }
1177        }
1178
1179        return result;
1180    }
1181
1182    private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
1183        Map<String, String> data = readNetworkVariablesFromSupplicantFile(key);
1184        if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key);
1185        return data.get(ssid);
1186    }
1187
1188    /* Mark all networks except specified netId as disabled */
1189    private void markAllNetworksDisabledExcept(int netId) {
1190        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1191            if(config != null && config.networkId != netId) {
1192                if (config.status != Status.DISABLED) {
1193                    config.status = Status.DISABLED;
1194                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
1195                }
1196            }
1197        }
1198    }
1199
1200    private void markAllNetworksDisabled() {
1201        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
1202    }
1203
1204    boolean needsUnlockedKeyStore() {
1205
1206        // Any network using certificates to authenticate access requires
1207        // unlocked key store; unless the certificates can be stored with
1208        // hardware encryption
1209
1210        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1211
1212            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
1213                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1214
1215                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
1216                    return true;
1217                }
1218            }
1219        }
1220
1221        return false;
1222    }
1223
1224    public void writeKnownNetworkHistory() {
1225        if (VDBG) {
1226            loge(" writeKnownNetworkHistory() num networks:" +
1227                    Integer.toString(mConfiguredNetworks.size()), true);
1228        }
1229
1230        /* Make a copy */
1231        final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
1232        for (WifiConfiguration config : mConfiguredNetworks.values()) {
1233            networks.add(new WifiConfiguration(config));
1234        }
1235
1236        mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() {
1237            public void onWriteCalled(DataOutputStream out) throws IOException {
1238                for (WifiConfiguration config : networks) {
1239                    //loge("onWriteCalled write SSID: " + config.SSID);
1240                   /* if (config.getLinkProperties() != null)
1241                        loge(" lp " + config.getLinkProperties().toString());
1242                    else
1243                        loge("attempt config w/o lp");
1244                    */
1245
1246                    if (VDBG) {
1247                        int num = 0;
1248                        if (config.connectChoices != null) {
1249                            num = config.connectChoices.size();
1250                        }
1251                        loge("saving network history: " + config.configKey()  + " gw: " +
1252                                config.defaultGwMacAddress + " autojoin status: " +
1253                                config.autoJoinStatus + " ephemeral=" + config.ephemeral
1254                                + " choices:" + Integer.toString(num));
1255                    }
1256                    if (config.ephemeral == true)
1257                        continue;
1258
1259                    if (config.isValid() == false)
1260                        continue;
1261
1262                    if (config.SSID == null) {
1263                        if (VDBG) {
1264                            loge("writeKnownNetworkHistory trying to write config with null SSID");
1265                        }
1266                        continue;
1267                    }
1268
1269                    out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
1270
1271                    out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
1272                    out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY);
1273
1274                    out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
1275                    out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
1276                            + SEPARATOR_KEY);
1277                    out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
1278                            + SEPARATOR_KEY);
1279                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
1280                            + Integer.toString(config.disableReason)
1281                            + SEPARATOR_KEY);
1282                    out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
1283                            + SEPARATOR_KEY);
1284                    out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
1285                            + SEPARATOR_KEY);
1286                    out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
1287                            + SEPARATOR_KEY);
1288                    if (config.peerWifiConfiguration != null) {
1289                        out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
1290                                + SEPARATOR_KEY);
1291                    }
1292                    out.writeUTF(NUM_CONNECTION_FAILURES_KEY
1293                            + Integer.toString(config.numConnectionFailures)
1294                            + SEPARATOR_KEY);
1295                    out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride)
1296                            + SEPARATOR_KEY);
1297                    out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY
1298                            + Integer.toString(config.numScorerOverrideAndSwitchedNetwork)
1299                            + SEPARATOR_KEY);
1300                    out.writeUTF(NUM_ASSOCIATION_KEY
1301                            + Integer.toString(config.numAssociation)
1302                            + SEPARATOR_KEY);
1303                    out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
1304                            + SEPARATOR_KEY);
1305                    out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
1306                            + SEPARATOR_KEY);
1307                    out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
1308                            + SEPARATOR_KEY);
1309                    out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
1310                            + SEPARATOR_KEY);
1311                    String allowedKeyManagementString =
1312                            makeString(config.allowedKeyManagement,
1313                                    WifiConfiguration.KeyMgmt.strings);
1314                    out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
1315
1316                    if (config.connectChoices != null) {
1317                        for (String key : config.connectChoices.keySet()) {
1318                            Integer choice = config.connectChoices.get(key);
1319                            out.writeUTF(CHOICE_KEY + key + "="
1320                                    + choice.toString() + SEPARATOR_KEY);
1321                        }
1322                    }
1323                    if (config.linkedConfigurations != null) {
1324                        for (String key : config.linkedConfigurations.keySet()) {
1325                            out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
1326                        }
1327                    }
1328
1329                    String macAddress = config.defaultGwMacAddress;
1330                    if (macAddress != null) {
1331                        out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
1332                    }
1333
1334                    if (config.scanResultCache != null) {
1335                        for (ScanResult result : config.scanResultCache.values()) {
1336                            out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
1337
1338                            out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
1339                                    + SEPARATOR_KEY);
1340
1341                            out.writeUTF(RSSI_KEY + Integer.toString(result.level)
1342                                    + SEPARATOR_KEY);
1343
1344                            out.writeUTF(BSSID_STATUS_KEY + Integer.toString(result.status)
1345                                    + SEPARATOR_KEY);
1346
1347                            if (result.seen != 0) {
1348                                out.writeUTF(MILLI_KEY + Long.toString(result.seen)
1349                                        + SEPARATOR_KEY);
1350                            }
1351                            out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
1352                        }
1353                    }
1354                    if (config.lastFailure != null) {
1355                        out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
1356                    }
1357                    out.writeUTF(SEPARATOR_KEY);
1358                }
1359            }
1360
1361        });
1362    }
1363
1364    public void setLastSelectedConfiguration(int netId) {
1365        if (DBG) {
1366            loge("setLastSelectedConfiguration " + Integer.toString(netId));
1367        }
1368        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
1369            lastSelectedConfiguration = null;
1370        } else {
1371            WifiConfiguration selected = getWifiConfiguration(netId);
1372            if (selected == null) {
1373                lastSelectedConfiguration = null;
1374            } else {
1375                lastSelectedConfiguration = selected.configKey();
1376                if (VDBG) {
1377                    loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
1378                }
1379            }
1380        }
1381    }
1382
1383    public String getLastSelectedConfiguration() {
1384        return lastSelectedConfiguration;
1385    }
1386
1387    private void readNetworkHistory() {
1388        if (VDBG) {
1389            loge("will readNetworkHistory path:" + networkHistoryConfigFile, true);
1390        }
1391        DataInputStream in = null;
1392        try {
1393            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
1394                    networkHistoryConfigFile)));
1395            WifiConfiguration config = null;
1396            while (true) {
1397                int id = -1;
1398                String key = in.readUTF();
1399                String bssid = null;
1400                String ssid = null;
1401
1402                int freq = 0;
1403                int status = 0;
1404                long seen = 0;
1405                int rssi = WifiConfiguration.INVALID_RSSI;
1406                String caps = null;
1407                if (key.startsWith(CONFIG_KEY)) {
1408
1409                    if (config != null) {
1410                        config = null;
1411                    }
1412                    String configKey = key.replace(CONFIG_KEY, "");
1413                    configKey = configKey.replace(SEPARATOR_KEY, "");
1414                    // get the networkId for that config Key
1415                    Integer n = mNetworkIds.get(configKey.hashCode());
1416                    // skip reading that configuration data
1417                    // since we don't have a corresponding network ID
1418                    if (n == null) {
1419                        loge("readNetworkHistory didnt find netid for hash="
1420                                + Integer.toString(configKey.hashCode())
1421                                + " key: " + configKey);
1422                        continue;
1423                    }
1424                    config = mConfiguredNetworks.get(n);
1425                    if (config == null) {
1426                        loge("readNetworkHistory didnt find config for netid="
1427                                + n.toString()
1428                                + " key: " + configKey);
1429                    }
1430                    status = 0;
1431                    ssid = null;
1432                    bssid = null;
1433                    freq = 0;
1434                    seen = 0;
1435                    rssi = WifiConfiguration.INVALID_RSSI;
1436                    caps = null;
1437
1438                } else if (config != null) {
1439                    if (key.startsWith(SSID_KEY)) {
1440                        ssid = key.replace(SSID_KEY, "");
1441                        ssid = ssid.replace(SEPARATOR_KEY, "");
1442                        if (config.SSID != null && !config.SSID.equals(ssid)) {
1443                            loge("Error parsing network history file, mismatched SSIDs");
1444                            config = null; //error
1445                            ssid = null;
1446                        } else {
1447                            config.SSID = ssid;
1448                        }
1449                    }
1450
1451                    if (key.startsWith(FQDN_KEY)) {
1452                        String fqdn = key.replace(FQDN_KEY, "");
1453                        fqdn = fqdn.replace(SEPARATOR_KEY, "");
1454                        config.FQDN = fqdn;
1455                    }
1456
1457                    if (key.startsWith(DEFAULT_GW_KEY)) {
1458                        String gateway = key.replace(DEFAULT_GW_KEY, "");
1459                        gateway = gateway.replace(SEPARATOR_KEY, "");
1460                        config.defaultGwMacAddress = gateway;
1461                    }
1462
1463                    if (key.startsWith(STATUS_KEY)) {
1464                        String st = key.replace(STATUS_KEY, "");
1465                        st = st.replace(SEPARATOR_KEY, "");
1466                        config.autoJoinStatus = Integer.parseInt(st);
1467                    }
1468
1469                    /*
1470                    if (key.startsWith(SUPPLICANT_STATUS_KEY)) {
1471                        String status = key.replace(SUPPLICANT_STATUS_KEY, "");
1472                        status = status.replace(SEPARATOR_KEY, "");
1473                        config.status = Integer.parseInt(status);
1474                    }
1475
1476                    if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
1477                        String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
1478                        reason = reason.replace(SEPARATOR_KEY, "");
1479                        config.disableReason = Integer.parseInt(reason);
1480                    }*/
1481
1482                    if (key.startsWith(SELF_ADDED_KEY)) {
1483                        String selfAdded = key.replace(SELF_ADDED_KEY, "");
1484                        selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
1485                        config.selfAdded = Boolean.parseBoolean(selfAdded);
1486                    }
1487
1488                    if (key.startsWith(DID_SELF_ADD_KEY)) {
1489                        String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
1490                        didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
1491                        config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
1492                    }
1493
1494                    if (key.startsWith(CREATOR_UID_KEY)) {
1495                        String uid = key.replace(CREATOR_UID_KEY, "");
1496                        uid = uid.replace(SEPARATOR_KEY, "");
1497                        config.creatorUid = Integer.parseInt(uid);
1498                    }
1499
1500                    if (key.startsWith(BLACKLIST_MILLI_KEY)) {
1501                        String milli = key.replace(BLACKLIST_MILLI_KEY, "");
1502                        milli = milli.replace(SEPARATOR_KEY, "");
1503                        config.blackListTimestamp = Long.parseLong(milli);
1504                    }
1505
1506                    if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) {
1507                        String num = key.replace(NUM_CONNECTION_FAILURES_KEY, "");
1508                        num = num.replace(SEPARATOR_KEY, "");
1509                        config.numConnectionFailures = Integer.parseInt(num);
1510                    }
1511
1512                    if (key.startsWith(SCORER_OVERRIDE_KEY)) {
1513                        String num = key.replace(SCORER_OVERRIDE_KEY, "");
1514                        num = num.replace(SEPARATOR_KEY, "");
1515                        config.numScorerOverride = Integer.parseInt(num);
1516                    }
1517
1518                    if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) {
1519                        String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, "");
1520                        num = num.replace(SEPARATOR_KEY, "");
1521                        config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num);
1522                    }
1523
1524                    if (key.startsWith(NUM_ASSOCIATION_KEY)) {
1525                        String num = key.replace(NUM_ASSOCIATION_KEY, "");
1526                        num = num.replace(SEPARATOR_KEY, "");
1527                        config.numAssociation = Integer.parseInt(num);
1528                    }
1529
1530                    if (key.startsWith(CONNECT_UID_KEY)) {
1531                        String uid = key.replace(CONNECT_UID_KEY, "");
1532                        uid = uid.replace(SEPARATOR_KEY, "");
1533                        config.lastConnectUid = Integer.parseInt(uid);
1534                    }
1535
1536                    if (key.startsWith(UPDATE_UID_KEY)) {
1537                        String uid = key.replace(UPDATE_UID_KEY, "");
1538                        uid = uid.replace(SEPARATOR_KEY, "");
1539                        config.lastUpdateUid = Integer.parseInt(uid);
1540                    }
1541
1542                    if (key.startsWith(FAILURE_KEY)) {
1543                        config.lastFailure = key.replace(FAILURE_KEY, "");
1544                        config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
1545                    }
1546
1547                    if (key.startsWith(PEER_CONFIGURATION_KEY)) {
1548                        config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
1549                        config.peerWifiConfiguration =
1550                                config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
1551                    }
1552
1553                    if (key.startsWith(CHOICE_KEY)) {
1554                        String choiceStr = key.replace(CHOICE_KEY, "");
1555                        choiceStr = choiceStr.replace(SEPARATOR_KEY, "");
1556                        String configKey = "";
1557                        int choice = 0;
1558                        Matcher match = mConnectChoice.matcher(choiceStr);
1559                        if (!match.find()) {
1560                            if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
1561                                    " Couldnt match pattern : " + choiceStr);
1562                        } else {
1563                            configKey = match.group(1);
1564                            try {
1565                                choice = Integer.parseInt(match.group(2));
1566                            } catch (NumberFormatException e) {
1567                                choice = 0;
1568                            }
1569                            if (choice > 0) {
1570                                if (config.connectChoices == null) {
1571                                    config.connectChoices = new HashMap<String, Integer>();
1572                                }
1573                                config.connectChoices.put(configKey, choice);
1574                            }
1575                        }
1576                    }
1577
1578                    if (key.startsWith(LINK_KEY)) {
1579                        String configKey = key.replace(LINK_KEY, "");
1580                        configKey = configKey.replace(SEPARATOR_KEY, "");
1581                        if (config.linkedConfigurations == null) {
1582                            config.linkedConfigurations = new HashMap<String, Integer>();
1583                        }
1584                        if (config.linkedConfigurations != null) {
1585                            config.linkedConfigurations.put(configKey, -1);
1586                        }
1587                    }
1588
1589                    if (key.startsWith(BSSID_KEY)) {
1590                        if (key.startsWith(BSSID_KEY)) {
1591                            bssid = key.replace(BSSID_KEY, "");
1592                            bssid = bssid.replace(SEPARATOR_KEY, "");
1593                            freq = 0;
1594                            seen = 0;
1595                            rssi = WifiConfiguration.INVALID_RSSI;
1596                            caps = "";
1597                            status = 0;
1598                        }
1599
1600                        if (key.startsWith(RSSI_KEY)) {
1601                            String lvl = key.replace(RSSI_KEY, "");
1602                            lvl = lvl.replace(SEPARATOR_KEY, "");
1603                            rssi = Integer.parseInt(lvl);
1604                        }
1605
1606                        if (key.startsWith(BSSID_STATUS_KEY)) {
1607                            String st = key.replace(BSSID_STATUS_KEY, "");
1608                            st = st.replace(SEPARATOR_KEY, "");
1609                            status = Integer.parseInt(st);
1610                        }
1611
1612                        if (key.startsWith(FREQ_KEY)) {
1613                            String channel = key.replace(FREQ_KEY, "");
1614                            channel = channel.replace(SEPARATOR_KEY, "");
1615                            freq = Integer.parseInt(channel);
1616                        }
1617
1618                        if (key.startsWith(DATE_KEY)) {
1619                        /*
1620                         * when reading the configuration from file we don't update the date
1621                         * so as to avoid reading back stale or non-sensical data that would
1622                         * depend on network time.
1623                         * The date of a WifiConfiguration should only come from actual scan result.
1624                         *
1625                        String s = key.replace(FREQ_KEY, "");
1626                        seen = Integer.getInteger(s);
1627                        */
1628                        }
1629
1630                        if (key.startsWith(BSSID_KEY_END)) {
1631
1632                            if ((bssid != null) && (ssid != null)) {
1633
1634                                if (config.scanResultCache == null) {
1635                                    config.scanResultCache = new HashMap<String, ScanResult>();
1636                                }
1637                                WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
1638                                ScanResult result = new ScanResult(wssid, bssid,
1639                                        caps, rssi, freq, (long) 0);
1640                                result.seen = seen;
1641                                config.scanResultCache.put(bssid, result);
1642                                result.status = status;
1643                            }
1644                        }
1645                    }
1646                }
1647            }
1648        } catch (EOFException ignore) {
1649            if (in != null) {
1650                try {
1651                    in.close();
1652                } catch (Exception e) {
1653                    loge("readNetworkHistory: Error reading file" + e);
1654                }
1655            }
1656        } catch (IOException e) {
1657            loge("readNetworkHistory: No config file, revert to default" + e);
1658        }
1659
1660        if(in!=null) {
1661            try {
1662                in.close();
1663            } catch (Exception e) {
1664                loge("readNetworkHistory: Error closing file" + e);
1665            }
1666        }
1667    }
1668
1669    private void readAutoJoinConfig() {
1670        BufferedReader reader = null;
1671        try {
1672
1673            reader = new BufferedReader(new FileReader(autoJoinConfigFile));
1674
1675            for (String key = reader.readLine(); key != null; key = reader.readLine()) {
1676                if (key != null) {
1677                    Log.d(TAG, "readAutoJoinConfig line: " + key);
1678                }
1679                if (key.startsWith(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY)) {
1680                    String st = key.replace(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY, "");
1681                    st = st.replace(SEPARATOR_KEY, "");
1682                    try {
1683                        enableAutoJoinWhileAssociated = Integer.parseInt(st) != 0;
1684                        Log.d(TAG,"readAutoJoinConfig: enabled = " + enableAutoJoinWhileAssociated);
1685                    } catch (NumberFormatException e) {
1686                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1687                    }
1688                }
1689
1690                if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY)) {
1691                    String st =
1692                            key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, "");
1693                    st = st.replace(SEPARATOR_KEY, "");
1694                    try {
1695                        thresholdInitialAutoJoinAttemptMin5RSSI = Integer.parseInt(st);
1696                        Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin5RSSI = "
1697                                + Integer.toString(thresholdInitialAutoJoinAttemptMin5RSSI));
1698                    } catch (NumberFormatException e) {
1699                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1700                    }
1701                }
1702
1703                if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY)) {
1704                    String st =
1705                            key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, "");
1706                    st = st.replace(SEPARATOR_KEY, "");
1707                    try {
1708                        thresholdInitialAutoJoinAttemptMin24RSSI = Integer.parseInt(st);
1709                        Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin24RSSI = "
1710                                + Integer.toString(thresholdInitialAutoJoinAttemptMin24RSSI));
1711                    } catch (NumberFormatException e) {
1712                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1713                    }
1714                }
1715
1716                if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_5G_KEY)) {
1717                    String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, "");
1718                    st = st.replace(SEPARATOR_KEY, "");
1719                    try {
1720                        thresholdUnblacklistThreshold5Hard = Integer.parseInt(st);
1721                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Hard = "
1722                            + Integer.toString(thresholdUnblacklistThreshold5Hard));
1723                    } catch (NumberFormatException e) {
1724                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1725                    }
1726                }
1727                if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY)) {
1728                    String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, "");
1729                    st = st.replace(SEPARATOR_KEY, "");
1730                    try {
1731                        thresholdUnblacklistThreshold5Soft = Integer.parseInt(st);
1732                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Soft = "
1733                            + Integer.toString(thresholdUnblacklistThreshold5Soft));
1734                    } catch (NumberFormatException e) {
1735                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1736                    }
1737                }
1738                if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_24G_KEY)) {
1739                    String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, "");
1740                    st = st.replace(SEPARATOR_KEY, "");
1741                    try {
1742                        thresholdUnblacklistThreshold24Hard = Integer.parseInt(st);
1743                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Hard = "
1744                            + Integer.toString(thresholdUnblacklistThreshold24Hard));
1745                    } catch (NumberFormatException e) {
1746                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1747                    }
1748                }
1749                if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY)) {
1750                    String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, "");
1751                    st = st.replace(SEPARATOR_KEY, "");
1752                    try {
1753                        thresholdUnblacklistThreshold24Soft = Integer.parseInt(st);
1754                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Soft = "
1755                            + Integer.toString(thresholdUnblacklistThreshold24Soft));
1756                    } catch (NumberFormatException e) {
1757                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1758                    }
1759                }
1760
1761                if (key.startsWith(THRESHOLD_GOOD_RSSI_5_KEY)) {
1762                    String st = key.replace(THRESHOLD_GOOD_RSSI_5_KEY, "");
1763                    st = st.replace(SEPARATOR_KEY, "");
1764                    try {
1765                        thresholdGoodRssi5 = Integer.parseInt(st);
1766                        Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi5 = "
1767                            + Integer.toString(thresholdGoodRssi5));
1768                    } catch (NumberFormatException e) {
1769                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1770                    }
1771                }
1772                if (key.startsWith(THRESHOLD_LOW_RSSI_5_KEY)) {
1773                    String st = key.replace(THRESHOLD_LOW_RSSI_5_KEY, "");
1774                    st = st.replace(SEPARATOR_KEY, "");
1775                    try {
1776                        thresholdLowRssi5 = Integer.parseInt(st);
1777                        Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi5 = "
1778                            + Integer.toString(thresholdLowRssi5));
1779                    } catch (NumberFormatException e) {
1780                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1781                    }
1782                }
1783                if (key.startsWith(THRESHOLD_BAD_RSSI_5_KEY)) {
1784                    String st = key.replace(THRESHOLD_BAD_RSSI_5_KEY, "");
1785                    st = st.replace(SEPARATOR_KEY, "");
1786                    try {
1787                        thresholdBadRssi5 = Integer.parseInt(st);
1788                        Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi5 = "
1789                            + Integer.toString(thresholdBadRssi5));
1790                    } catch (NumberFormatException e) {
1791                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1792                    }
1793                }
1794
1795                if (key.startsWith(THRESHOLD_GOOD_RSSI_24_KEY)) {
1796                    String st = key.replace(THRESHOLD_GOOD_RSSI_24_KEY, "");
1797                    st = st.replace(SEPARATOR_KEY, "");
1798                    try {
1799                        thresholdGoodRssi24 = Integer.parseInt(st);
1800                        Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi24 = "
1801                            + Integer.toString(thresholdGoodRssi24));
1802                    } catch (NumberFormatException e) {
1803                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1804                    }
1805                }
1806                if (key.startsWith(THRESHOLD_LOW_RSSI_24_KEY)) {
1807                    String st = key.replace(THRESHOLD_LOW_RSSI_24_KEY, "");
1808                    st = st.replace(SEPARATOR_KEY, "");
1809                    try {
1810                        thresholdLowRssi24 = Integer.parseInt(st);
1811                        Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi24 = "
1812                            + Integer.toString(thresholdLowRssi24));
1813                    } catch (NumberFormatException e) {
1814                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1815                    }
1816                }
1817                if (key.startsWith(THRESHOLD_BAD_RSSI_24_KEY)) {
1818                    String st = key.replace(THRESHOLD_BAD_RSSI_24_KEY, "");
1819                    st = st.replace(SEPARATOR_KEY, "");
1820                    try {
1821                        thresholdBadRssi24 = Integer.parseInt(st);
1822                        Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi24 = "
1823                            + Integer.toString(thresholdBadRssi24));
1824                    } catch (NumberFormatException e) {
1825                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1826                    }
1827                }
1828
1829                if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
1830                    String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
1831                    st = st.replace(SEPARATOR_KEY, "");
1832                    try {
1833                        maxTxPacketForNetworkSwitching = Integer.parseInt(st);
1834                        Log.d(TAG,"readAutoJoinConfig: maxTxPacketForNetworkSwitching = "
1835                            + Integer.toString(maxTxPacketForNetworkSwitching));
1836                    } catch (NumberFormatException e) {
1837                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1838                    }
1839                }
1840                if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
1841                    String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
1842                    st = st.replace(SEPARATOR_KEY, "");
1843                    try {
1844                        maxRxPacketForNetworkSwitching = Integer.parseInt(st);
1845                        Log.d(TAG,"readAutoJoinConfig: maxRxPacketForNetworkSwitching = "
1846                            + Integer.toString(maxRxPacketForNetworkSwitching));
1847                    } catch (NumberFormatException e) {
1848                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1849                    }
1850                }
1851
1852                if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY)) {
1853                    String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY, "");
1854                    st = st.replace(SEPARATOR_KEY, "");
1855                    try {
1856                        thresholdBandPreferenceLowRssi5 = Integer.parseInt(st);
1857                        Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceLowRssi5 = "
1858                            + Integer.toString(thresholdBandPreferenceLowRssi5));
1859                    } catch (NumberFormatException e) {
1860                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1861                    }
1862                }
1863                if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
1864                    String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
1865                    st = st.replace(SEPARATOR_KEY, "");
1866                    try {
1867                        thresholdBandPreferenceRssi5 = Integer.parseInt(st);
1868                        Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi5 = "
1869                            + Integer.toString(thresholdBandPreferenceRssi5));
1870                    } catch (NumberFormatException e) {
1871                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1872                    }
1873                }
1874                if (key.startsWith(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
1875                    String st = key.replace(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
1876                    st = st.replace(SEPARATOR_KEY, "");
1877                    try {
1878                        thresholdBandPreferenceRssi24 = Integer.parseInt(st);
1879                        Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi24 = "
1880                            + Integer.toString(thresholdBandPreferenceRssi24));
1881                    } catch (NumberFormatException e) {
1882                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1883                    }
1884                }
1885            }
1886        } catch (EOFException ignore) {
1887            if (reader != null) {
1888                try {
1889                    reader.close();
1890                    reader = null;
1891                } catch (Exception e) {
1892                    loge("readAutoJoinStatus: Error closing file" + e);
1893                }
1894            }
1895        } catch (IOException e) {
1896            loge("readAutoJoinStatus: Error parsing configuration" + e);
1897        }
1898
1899        if (reader!=null) {
1900           try {
1901               reader.close();
1902           } catch (Exception e) {
1903               loge("readAutoJoinStatus: Error closing file" + e);
1904           }
1905        }
1906    }
1907
1908
1909    private void writeIpAndProxyConfigurations() {
1910        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
1911        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1912            if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
1913                networks.put(configKey(config), config.getIpConfiguration());
1914            }
1915        }
1916
1917        super.writeIpAndProxyConfigurations(ipConfigFile, networks);
1918    }
1919
1920    private void readIpAndProxyConfigurations() {
1921        SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
1922
1923        if (networks.size() == 0) {
1924            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
1925            return;
1926        }
1927
1928        for (int i = 0; i < networks.size(); i++) {
1929            int id = networks.keyAt(i);
1930            WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
1931
1932
1933            if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1934                loge("configuration found for missing network, nid=" + id
1935                        +", ignored, networks.size=" + Integer.toString(networks.size()));
1936            } else {
1937                config.setIpConfiguration(networks.valueAt(i));
1938            }
1939        }
1940    }
1941
1942    /*
1943     * Convert string to Hexadecimal before passing to wifi native layer
1944     * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
1945     * conversion to hex is required because SSIDs can have space characters in them;
1946     * and that can confuses the supplicant because it uses space charaters as delimiters
1947     */
1948
1949    private String encodeSSID(String str){
1950        String tmp = removeDoubleQuotes(str);
1951        return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
1952    }
1953
1954    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
1955        /*
1956         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1957         * network configuration. Otherwise, the networkId should
1958         * refer to an existing configuration.
1959         */
1960
1961        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1962
1963        int netId = config.networkId;
1964        boolean newNetwork = false;
1965        // networkId of INVALID_NETWORK_ID means we want to create a new network
1966        if (netId == INVALID_NETWORK_ID) {
1967            Integer savedNetId = mNetworkIds.get(configKey(config));
1968            //paranoia: check if either we have a network Id or a WifiConfiguration
1969            //matching the one we are trying to add.
1970            if (savedNetId == null) {
1971                for (WifiConfiguration test : mConfiguredNetworks.values()) {
1972                    if (test.configKey().equals(config.configKey())) {
1973                        savedNetId = test.networkId;
1974                        loge("addOrUpdateNetworkNative " + config.configKey()
1975                                + " was found, but no network Id");
1976                        break;
1977                    }
1978                }
1979            }
1980            if (savedNetId != null) {
1981                netId = savedNetId;
1982            } else {
1983                newNetwork = true;
1984                netId = mWifiNative.addNetwork();
1985                if (netId < 0) {
1986                    loge("Failed to add a network!");
1987                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
1988                } else {
1989                    loge("addOrUpdateNetworkNative created netId=" + netId);
1990                }
1991            }
1992        }
1993
1994        boolean updateFailed = true;
1995
1996        setVariables: {
1997
1998            if (config.SSID != null &&
1999                    !mWifiNative.setNetworkVariable(
2000                        netId,
2001                        WifiConfiguration.ssidVarName,
2002                        encodeSSID(config.SSID))) {
2003                loge("failed to set SSID: "+config.SSID);
2004                break setVariables;
2005            }
2006
2007            if (config.BSSID != null) {
2008                loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
2009                if (!mWifiNative.setNetworkVariable(
2010                        netId,
2011                        WifiConfiguration.bssidVarName,
2012                        config.BSSID)) {
2013                    loge("failed to set BSSID: " + config.BSSID);
2014                    break setVariables;
2015                }
2016            }
2017
2018            String allowedKeyManagementString =
2019                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
2020            if (config.allowedKeyManagement.cardinality() != 0 &&
2021                    !mWifiNative.setNetworkVariable(
2022                        netId,
2023                        WifiConfiguration.KeyMgmt.varName,
2024                        allowedKeyManagementString)) {
2025                loge("failed to set key_mgmt: "+
2026                        allowedKeyManagementString);
2027                break setVariables;
2028            }
2029
2030            String allowedProtocolsString =
2031                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
2032            if (config.allowedProtocols.cardinality() != 0 &&
2033                    !mWifiNative.setNetworkVariable(
2034                        netId,
2035                        WifiConfiguration.Protocol.varName,
2036                        allowedProtocolsString)) {
2037                loge("failed to set proto: "+
2038                        allowedProtocolsString);
2039                break setVariables;
2040            }
2041
2042            String allowedAuthAlgorithmsString =
2043                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
2044            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
2045                    !mWifiNative.setNetworkVariable(
2046                        netId,
2047                        WifiConfiguration.AuthAlgorithm.varName,
2048                        allowedAuthAlgorithmsString)) {
2049                loge("failed to set auth_alg: "+
2050                        allowedAuthAlgorithmsString);
2051                break setVariables;
2052            }
2053
2054            String allowedPairwiseCiphersString =
2055                    makeString(config.allowedPairwiseCiphers,
2056                    WifiConfiguration.PairwiseCipher.strings);
2057            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
2058                    !mWifiNative.setNetworkVariable(
2059                        netId,
2060                        WifiConfiguration.PairwiseCipher.varName,
2061                        allowedPairwiseCiphersString)) {
2062                loge("failed to set pairwise: "+
2063                        allowedPairwiseCiphersString);
2064                break setVariables;
2065            }
2066
2067            String allowedGroupCiphersString =
2068                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
2069            if (config.allowedGroupCiphers.cardinality() != 0 &&
2070                    !mWifiNative.setNetworkVariable(
2071                        netId,
2072                        WifiConfiguration.GroupCipher.varName,
2073                        allowedGroupCiphersString)) {
2074                loge("failed to set group: "+
2075                        allowedGroupCiphersString);
2076                break setVariables;
2077            }
2078
2079            // Prevent client screw-up by passing in a WifiConfiguration we gave it
2080            // by preventing "*" as a key.
2081            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
2082                    !mWifiNative.setNetworkVariable(
2083                        netId,
2084                        WifiConfiguration.pskVarName,
2085                        config.preSharedKey)) {
2086                loge("failed to set psk");
2087                break setVariables;
2088            }
2089
2090            boolean hasSetKey = false;
2091            if (config.wepKeys != null) {
2092                for (int i = 0; i < config.wepKeys.length; i++) {
2093                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
2094                    // by preventing "*" as a key.
2095                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
2096                        if (!mWifiNative.setNetworkVariable(
2097                                    netId,
2098                                    WifiConfiguration.wepKeyVarNames[i],
2099                                    config.wepKeys[i])) {
2100                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
2101                            break setVariables;
2102                        }
2103                        hasSetKey = true;
2104                    }
2105                }
2106            }
2107
2108            if (hasSetKey) {
2109                if (!mWifiNative.setNetworkVariable(
2110                            netId,
2111                            WifiConfiguration.wepTxKeyIdxVarName,
2112                            Integer.toString(config.wepTxKeyIndex))) {
2113                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
2114                    break setVariables;
2115                }
2116            }
2117
2118            if (!mWifiNative.setNetworkVariable(
2119                        netId,
2120                        WifiConfiguration.priorityVarName,
2121                        Integer.toString(config.priority))) {
2122                loge(config.SSID + ": failed to set priority: "
2123                        +config.priority);
2124                break setVariables;
2125            }
2126
2127            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
2128                        netId,
2129                        WifiConfiguration.hiddenSSIDVarName,
2130                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
2131                loge(config.SSID + ": failed to set hiddenSSID: "+
2132                        config.hiddenSSID);
2133                break setVariables;
2134            }
2135
2136            if (config.requirePMF && !mWifiNative.setNetworkVariable(
2137                        netId,
2138                        WifiConfiguration.pmfVarName,
2139                        "2")) {
2140                loge(config.SSID + ": failed to set requirePMF: "+
2141                        config.requirePMF);
2142                break setVariables;
2143            }
2144
2145            if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
2146                    netId,
2147                    WifiConfiguration.updateIdentiferVarName,
2148                    config.updateIdentifier)) {
2149                loge(config.SSID + ": failed to set updateIdentifier: "+
2150                        config.updateIdentifier);
2151                break setVariables;
2152            }
2153
2154            if (config.enterpriseConfig != null &&
2155                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
2156
2157                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
2158
2159                if (needsKeyStore(enterpriseConfig)) {
2160                    /**
2161                     * Keyguard settings may eventually be controlled by device policy.
2162                     * We check here if keystore is unlocked before installing
2163                     * credentials.
2164                     * TODO: Do we need a dialog here ?
2165                     */
2166                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
2167                        loge(config.SSID + ": key store is locked");
2168                        break setVariables;
2169                    }
2170
2171                    try {
2172                        /* config passed may include only fields being updated.
2173                         * In order to generate the key id, fetch uninitialized
2174                         * fields from the currently tracked configuration
2175                         */
2176                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
2177                        String keyId = config.getKeyIdForCredentials(currentConfig);
2178
2179                        if (!installKeys(enterpriseConfig, keyId)) {
2180                            loge(config.SSID + ": failed to install keys");
2181                            break setVariables;
2182                        }
2183                    } catch (IllegalStateException e) {
2184                        loge(config.SSID + " invalid config for key installation");
2185                        break setVariables;
2186                    }
2187                }
2188
2189                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
2190                for (String key : enterpriseFields.keySet()) {
2191                        String value = enterpriseFields.get(key);
2192                        if (key.equals("password") && value != null && value.equals("*")) {
2193                            //no need to try to set an obfuscated password, which will fail
2194                            continue;
2195                        }
2196                        if (!mWifiNative.setNetworkVariable(
2197                                    netId,
2198                                    key,
2199                                    value)) {
2200                            removeKeys(enterpriseConfig);
2201                            loge(config.SSID + ": failed to set " + key +
2202                                    ": " + value);
2203                            break setVariables;
2204                        }
2205                }
2206            }
2207            updateFailed = false;
2208        } //end of setVariables
2209
2210        if (updateFailed) {
2211            if (newNetwork) {
2212                mWifiNative.removeNetwork(netId);
2213                loge("Failed to set a network variable, removed network: " + netId);
2214            }
2215            return new NetworkUpdateResult(INVALID_NETWORK_ID);
2216        }
2217
2218        /* An update of the network variables requires reading them
2219         * back from the supplicant to update mConfiguredNetworks.
2220         * This is because some of the variables (SSID, wep keys &
2221         * passphrases) reflect different values when read back than
2222         * when written. For example, wep key is stored as * irrespective
2223         * of the value sent to the supplicant
2224         */
2225        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
2226        if (currentConfig == null) {
2227            currentConfig = new WifiConfiguration();
2228            currentConfig.setIpAssignment(IpAssignment.DHCP);
2229            currentConfig.setProxySettings(ProxySettings.NONE);
2230            currentConfig.networkId = netId;
2231            if (config != null) {
2232                //carry over the creation parameters
2233                currentConfig.selfAdded = config.selfAdded;
2234                currentConfig.didSelfAdd = config.didSelfAdd;
2235                currentConfig.lastConnectUid = config.lastConnectUid;
2236                currentConfig.lastUpdateUid = config.lastUpdateUid;
2237                currentConfig.creatorUid = config.creatorUid;
2238                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
2239            }
2240            if (DBG) {
2241                loge("created new config netId=" + Integer.toString(netId)
2242                        + " uid=" + Integer.toString(currentConfig.creatorUid));
2243            }
2244        }
2245
2246        if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
2247            //make sure the configuration is not deleted anymore since we just
2248            //added or modified it.
2249            currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
2250            currentConfig.selfAdded = false;
2251            currentConfig.didSelfAdd = false;
2252        }
2253
2254        if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
2255
2256        readNetworkVariables(currentConfig);
2257
2258        mConfiguredNetworks.put(netId, currentConfig);
2259        mNetworkIds.put(configKey(currentConfig), netId);
2260
2261        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
2262        result.setIsNewNetwork(newNetwork);
2263        result.setNetworkId(netId);
2264        return result;
2265    }
2266
2267
2268    public void linkConfiguration(WifiConfiguration config) {
2269        if (config.scanResultCache != null && config.scanResultCache.size() > 6) {
2270            // Ignore configurations with large number of BSSIDs
2271            return;
2272        }
2273        if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
2274            // Only link WPA_PSK config
2275            return;
2276        }
2277        for (WifiConfiguration link : mConfiguredNetworks.values()) {
2278            boolean doLink = false;
2279
2280            if (link.configKey().equals(config.configKey())) {
2281                continue;
2282            }
2283
2284            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
2285                continue;
2286            }
2287
2288            // Autojoin will be allowed to dynamically jump from a linked configuration
2289            // to another, hence only link configurations that have equivalent level of security
2290            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
2291                continue;
2292            }
2293
2294            if (link.scanResultCache != null && link.scanResultCache.size() > 6) {
2295                // Ignore configurations with large number of BSSIDs
2296                continue;
2297            }
2298
2299            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
2300                // If both default GW are known, compare based on RSSI only if the GW is equal
2301                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
2302                    if (VDBG) {
2303                        loge("linkConfiguration link due to same gw" + link.SSID +
2304                                " and " + config.SSID + " GW " + config.defaultGwMacAddress);
2305                    }
2306                    doLink = true;
2307                }
2308            } else {
2309                // We do not know BOTH default gateways hence we will try to link
2310                // hoping that WifiConfigurations are indeed behind the same gateway.
2311                // once both WifiConfiguration have been tried and thus once both efault gateways
2312                // are known we will revisit the choice of linking them
2313                if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
2314                        && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
2315                    for (String abssid : config.scanResultCache.keySet()) {
2316                        for (String bbssid : link.scanResultCache.keySet()) {
2317                            if (VDBG) {
2318                                loge("linkConfiguration try to link due to DBDC BSSID match "
2319                                        + link.SSID +
2320                                        " and " + config.SSID + " bssida " + abssid
2321                                        + " bssidb " + bbssid);
2322                            }
2323                            if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
2324                                // If first 16 ascii characters of BSSID matches,
2325                                // we assume this is a DBDC
2326                                doLink = true;
2327                            }
2328                        }
2329                    }
2330                }
2331            }
2332
2333            if (doLink) {
2334                if (VDBG) {
2335                    loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID);
2336                }
2337                if (link.linkedConfigurations == null) {
2338                    link.linkedConfigurations = new HashMap<String, Integer>();
2339                }
2340                if (config.linkedConfigurations == null) {
2341                    config.linkedConfigurations = new HashMap<String, Integer>();
2342                }
2343                link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
2344                config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
2345            } else {
2346                //todo if they are linked, break the link
2347            }
2348        }
2349    }
2350
2351    /*
2352     * We try to link a scan result with a WifiConfiguration for which SSID and
2353     * key management dont match,
2354     * for instance, we try identify the 5GHz SSID of a DBDC AP,
2355     * even though we know only of the 2.4GHz
2356     *
2357     * Obviously, this function is not optimal since it is used to compare every scan
2358     * result with every Saved WifiConfiguration, with a string.equals operation.
2359     * As a speed up, might be better to implement the mConfiguredNetworks store as a
2360     * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
2361     * so as to speed this up. Also to prevent the tiny probability of hash collision.
2362     *
2363     */
2364    public WifiConfiguration associateWithConfiguration(ScanResult result) {
2365        String configKey = WifiConfiguration.configKey(result);
2366        if (configKey == null) {
2367            if (DBG) loge("associateWithConfiguration(): no config key " );
2368            return null;
2369        }
2370
2371        //need to compare with quoted string
2372        String SSID = "\"" + result.SSID + "\"";
2373
2374        WifiConfiguration config = null;
2375        for (WifiConfiguration link : mConfiguredNetworks.values()) {
2376            boolean doLink = false;
2377
2378            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) {
2379                //make sure we dont associate the scan result to a deleted config
2380                continue;
2381            }
2382
2383            if (configKey.equals(link.configKey())) {
2384                if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
2385                return link; //found it exactly
2386            }
2387
2388            if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) {
2389                String bssid = "";
2390                for (String key : link.scanResultCache.keySet()) {
2391                    bssid = key;
2392                }
2393
2394                if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
2395                        && SSID.regionMatches(false, 0, link.SSID, 0, 3)) {
2396                    // if first 16 ascii characters of BSSID matches, and first 3
2397                    // characters of SSID match, we assume this is a home setup
2398                    // and thus we will try to transfer the password from the known
2399                    // BSSID/SSID to the recently found BSSID/SSID
2400
2401                    //if (VDBG)
2402                    //    loge("associateWithConfiguration OK " );
2403                    doLink = true;
2404                }
2405            }
2406
2407            if (doLink) {
2408                //try to make a non verified WifiConfiguration, but only if the original
2409                //configuration was not self already added
2410                if (VDBG) {
2411                    loge("associateWithConfiguration: will create " +
2412                            result.SSID + " and associate it with: " + link.SSID);
2413                }
2414                config = wifiConfigurationFromScanResult(result);
2415                if (config != null) {
2416                    config.selfAdded = true;
2417                    config.didSelfAdd = true;
2418                    config.peerWifiConfiguration = link.configKey();
2419                    if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
2420                            config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
2421                        //transfer the credentials from the configuration we are linking from
2422                        String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
2423                        if (psk != null) {
2424                            config.preSharedKey = psk;
2425                            if (VDBG) {
2426                                if (config.preSharedKey != null)
2427                                    loge(" transfer PSK : " + config.preSharedKey);
2428                            }
2429
2430                            //link configurations
2431                            if (link.linkedConfigurations == null) {
2432                                link.linkedConfigurations = new HashMap<String, Integer>();
2433                            }
2434                            if (config.linkedConfigurations == null) {
2435                                config.linkedConfigurations = new HashMap<String, Integer>();
2436                            }
2437                            link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
2438                            config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
2439                        } else {
2440                            config = null;
2441                        }
2442                    } else {
2443                        config = null;
2444                    }
2445                }
2446            } else {
2447                //todo if they are linked, break the link
2448            }
2449        }
2450        return config;
2451    }
2452
2453    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
2454        if (config == null)
2455            return null;
2456        long now_ms = System.currentTimeMillis();
2457
2458        HashSet<Integer> channels = new HashSet<Integer>();
2459
2460        //get channels for this configuration, if there are at least 2 BSSIDs
2461        if (config.scanResultCache == null && config.linkedConfigurations == null) {
2462            return null;
2463        }
2464
2465        if (VDBG) {
2466            StringBuilder dbg = new StringBuilder();
2467            dbg.append("makeChannelList age=" + Integer.toString(age)
2468                    + " for " + config.configKey());
2469            if (config.scanResultCache != null) {
2470                dbg.append(" bssids=" + config.scanResultCache.size());
2471            }
2472            if (config.linkedConfigurations != null) {
2473                dbg.append(" linked=" + config.linkedConfigurations.size());
2474            }
2475            loge(dbg.toString());
2476        }
2477
2478        if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
2479            for (ScanResult result : config.scanResultCache.values()) {
2480                if (VDBG) {
2481                    boolean test = (now_ms - result.seen) < age;
2482                    loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
2483                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
2484                }
2485                if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2486                    channels.add(result.frequency);
2487                }
2488            }
2489        }
2490
2491        //get channels for linked configurations
2492        if (config.linkedConfigurations != null) {
2493            for (String key : config.linkedConfigurations.keySet()) {
2494                WifiConfiguration linked = getWifiConfiguration(key);
2495                if (linked == null)
2496                    continue;
2497                if (linked.scanResultCache == null) {
2498                    continue;
2499                }
2500                for (ScanResult result : linked.scanResultCache.values()) {
2501                    if (VDBG) {
2502                        loge("has link: " + result.BSSID
2503                                + " freq=" + Integer.toString(result.frequency)
2504                                + " age=" + Long.toString(now_ms - result.seen));
2505                    }
2506                    if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2507                        channels.add(result.frequency);
2508                    }
2509                }
2510            }
2511        }
2512        return channels;
2513    }
2514
2515    public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) {
2516        WifiConfiguration found = null;
2517        if (scanResult == null)
2518            return found;
2519
2520        //first step, look for this scan Result by SSID + Key Management
2521        String key = WifiConfiguration.configKey(scanResult);
2522        int hash = key.hashCode();
2523
2524        Integer netId = mNetworkIds.get(hash);
2525        if (netId == null) return null;
2526        WifiConfiguration config = mConfiguredNetworks.get(netId);
2527        if (config != null) {
2528           if (config.scanResultCache == null) {
2529                config.scanResultCache = new HashMap<String, ScanResult>();
2530           }
2531           if (config.scanResultCache == null) {
2532                return null;
2533           }
2534           //add the scan result to this WifiConfiguration
2535           config.scanResultCache.put(scanResult.BSSID, scanResult);
2536           mConfiguredNetworks.put(netId, config);
2537           linkConfiguration(config);
2538           found = config;
2539        }
2540
2541        if (VDBG) {
2542            config = mConfiguredNetworks.get(netId);
2543            if (config != null) {
2544                if (config.scanResultCache != null) {
2545                    String status = "";
2546                    if (scanResult.status > 0) {
2547                        status = " status=" + Integer.toString(scanResult.status);
2548                    }
2549                    loge("                    got known scan result " +
2550                            scanResult.BSSID + " key : " + key + " num: " +
2551                            Integer.toString(config.scanResultCache.size())
2552                            + " rssi=" + Integer.toString(scanResult.level)
2553                            + " freq=" + Integer.toString(scanResult.frequency)
2554                            + status);
2555                } else {
2556                    loge("                    got known scan result and no cache" +
2557                            scanResult.BSSID + " key : " + key);
2558                }
2559            }
2560        }
2561        return found;
2562
2563    }
2564
2565    /* Compare current and new configuration and write to file on change */
2566    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
2567            WifiConfiguration currentConfig,
2568            WifiConfiguration newConfig) {
2569        boolean ipChanged = false;
2570        boolean proxyChanged = false;
2571
2572        if (VDBG) {
2573            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
2574                    newConfig.SSID + " path: " + ipConfigFile);
2575        }
2576
2577
2578        switch (newConfig.getIpAssignment()) {
2579            case STATIC:
2580                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2581                    ipChanged = true;
2582                } else {
2583                    ipChanged = !Objects.equals(
2584                            currentConfig.getStaticIpConfiguration(),
2585                            newConfig.getStaticIpConfiguration());
2586                }
2587                break;
2588            case DHCP:
2589                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2590                    ipChanged = true;
2591                }
2592                break;
2593            case UNASSIGNED:
2594                /* Ignore */
2595                break;
2596            default:
2597                loge("Ignore invalid ip assignment during write");
2598                break;
2599        }
2600
2601        switch (newConfig.getProxySettings()) {
2602            case STATIC:
2603            case PAC:
2604                ProxyInfo newHttpProxy = newConfig.getHttpProxy();
2605                ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
2606
2607                if (newHttpProxy != null) {
2608                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
2609                } else {
2610                    proxyChanged = (currentHttpProxy != null);
2611                }
2612                break;
2613            case NONE:
2614                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
2615                    proxyChanged = true;
2616                }
2617                break;
2618            case UNASSIGNED:
2619                /* Ignore */
2620                break;
2621            default:
2622                loge("Ignore invalid proxy configuration during write");
2623                break;
2624        }
2625
2626        if (ipChanged) {
2627            currentConfig.setIpAssignment(newConfig.getIpAssignment());
2628            currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
2629            log("IP config changed SSID = " + currentConfig.SSID +
2630                " static configuration: " + currentConfig.getStaticIpConfiguration().toString());
2631        }
2632
2633        if (proxyChanged) {
2634            currentConfig.setProxySettings(newConfig.getProxySettings());
2635            currentConfig.setHttpProxy(newConfig.getHttpProxy());
2636            log("proxy changed SSID = " + currentConfig.SSID);
2637            if (currentConfig.getHttpProxy() != null) {
2638                log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
2639            }
2640        }
2641
2642        if (ipChanged || proxyChanged) {
2643            writeIpAndProxyConfigurations();
2644            sendConfiguredNetworksChangedBroadcast(currentConfig,
2645                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
2646        }
2647        return new NetworkUpdateResult(ipChanged, proxyChanged);
2648    }
2649
2650    /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */
2651    private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
2652        switch (key) {
2653            case WifiEnterpriseConfig.EAP_KEY:
2654            case WifiEnterpriseConfig.ENGINE_KEY:
2655                return false;
2656            default:
2657                return true;
2658        }
2659    }
2660
2661    /**
2662     * Read the variables from the supplicant daemon that are needed to
2663     * fill in the WifiConfiguration object.
2664     *
2665     * @param config the {@link WifiConfiguration} object to be filled in.
2666     */
2667    private void readNetworkVariables(WifiConfiguration config) {
2668
2669        int netId = config.networkId;
2670        if (netId < 0)
2671            return;
2672
2673        /*
2674         * TODO: maybe should have a native method that takes an array of
2675         * variable names and returns an array of values. But we'd still
2676         * be doing a round trip to the supplicant daemon for each variable.
2677         */
2678        String value;
2679
2680        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
2681        if (!TextUtils.isEmpty(value)) {
2682            if (value.charAt(0) != '"') {
2683                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
2684                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
2685                //supplicant string
2686            } else {
2687                config.SSID = value;
2688            }
2689        } else {
2690            config.SSID = null;
2691        }
2692
2693        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
2694        if (!TextUtils.isEmpty(value)) {
2695            config.BSSID = value;
2696        } else {
2697            config.BSSID = null;
2698        }
2699
2700        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
2701        config.priority = -1;
2702        if (!TextUtils.isEmpty(value)) {
2703            try {
2704                config.priority = Integer.parseInt(value);
2705            } catch (NumberFormatException ignore) {
2706            }
2707        }
2708
2709        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
2710        config.hiddenSSID = false;
2711        if (!TextUtils.isEmpty(value)) {
2712            try {
2713                config.hiddenSSID = Integer.parseInt(value) != 0;
2714            } catch (NumberFormatException ignore) {
2715            }
2716        }
2717
2718        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
2719        config.wepTxKeyIndex = -1;
2720        if (!TextUtils.isEmpty(value)) {
2721            try {
2722                config.wepTxKeyIndex = Integer.parseInt(value);
2723            } catch (NumberFormatException ignore) {
2724            }
2725        }
2726
2727        for (int i = 0; i < 4; i++) {
2728            value = mWifiNative.getNetworkVariable(netId,
2729                    WifiConfiguration.wepKeyVarNames[i]);
2730            if (!TextUtils.isEmpty(value)) {
2731                config.wepKeys[i] = value;
2732            } else {
2733                config.wepKeys[i] = null;
2734            }
2735        }
2736
2737        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
2738        if (!TextUtils.isEmpty(value)) {
2739            config.preSharedKey = value;
2740        } else {
2741            config.preSharedKey = null;
2742        }
2743
2744        value = mWifiNative.getNetworkVariable(config.networkId,
2745                WifiConfiguration.Protocol.varName);
2746        if (!TextUtils.isEmpty(value)) {
2747            String vals[] = value.split(" ");
2748            for (String val : vals) {
2749                int index =
2750                    lookupString(val, WifiConfiguration.Protocol.strings);
2751                if (0 <= index) {
2752                    config.allowedProtocols.set(index);
2753                }
2754            }
2755        }
2756
2757        value = mWifiNative.getNetworkVariable(config.networkId,
2758                WifiConfiguration.KeyMgmt.varName);
2759        if (!TextUtils.isEmpty(value)) {
2760            String vals[] = value.split(" ");
2761            for (String val : vals) {
2762                int index =
2763                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
2764                if (0 <= index) {
2765                    config.allowedKeyManagement.set(index);
2766                }
2767            }
2768        }
2769
2770        value = mWifiNative.getNetworkVariable(config.networkId,
2771                WifiConfiguration.AuthAlgorithm.varName);
2772        if (!TextUtils.isEmpty(value)) {
2773            String vals[] = value.split(" ");
2774            for (String val : vals) {
2775                int index =
2776                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
2777                if (0 <= index) {
2778                    config.allowedAuthAlgorithms.set(index);
2779                }
2780            }
2781        }
2782
2783        value = mWifiNative.getNetworkVariable(config.networkId,
2784                WifiConfiguration.PairwiseCipher.varName);
2785        if (!TextUtils.isEmpty(value)) {
2786            String vals[] = value.split(" ");
2787            for (String val : vals) {
2788                int index =
2789                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
2790                if (0 <= index) {
2791                    config.allowedPairwiseCiphers.set(index);
2792                }
2793            }
2794        }
2795
2796        value = mWifiNative.getNetworkVariable(config.networkId,
2797                WifiConfiguration.GroupCipher.varName);
2798        if (!TextUtils.isEmpty(value)) {
2799            String vals[] = value.split(" ");
2800            for (String val : vals) {
2801                int index =
2802                    lookupString(val, WifiConfiguration.GroupCipher.strings);
2803                if (0 <= index) {
2804                    config.allowedGroupCiphers.set(index);
2805                }
2806            }
2807        }
2808
2809        if (config.enterpriseConfig == null) {
2810            config.enterpriseConfig = new WifiEnterpriseConfig();
2811        }
2812        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
2813        for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
2814            value = mWifiNative.getNetworkVariable(netId, key);
2815            if (!TextUtils.isEmpty(value)) {
2816                if (!enterpriseConfigKeyShouldBeQuoted(key)) {
2817                    value = removeDoubleQuotes(value);
2818                }
2819                enterpriseFields.put(key, value);
2820            } else {
2821                enterpriseFields.put(key, EMPTY_VALUE);
2822            }
2823        }
2824
2825        if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
2826            saveConfig();
2827        }
2828
2829        migrateCerts(config.enterpriseConfig);
2830        // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
2831    }
2832
2833    private static String removeDoubleQuotes(String string) {
2834        int length = string.length();
2835        if ((length > 1) && (string.charAt(0) == '"')
2836                && (string.charAt(length - 1) == '"')) {
2837            return string.substring(1, length - 1);
2838        }
2839        return string;
2840    }
2841
2842    private static String makeString(BitSet set, String[] strings) {
2843        StringBuffer buf = new StringBuffer();
2844        int nextSetBit = -1;
2845
2846        /* Make sure all set bits are in [0, strings.length) to avoid
2847         * going out of bounds on strings.  (Shouldn't happen, but...) */
2848        set = set.get(0, strings.length);
2849
2850        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
2851            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
2852        }
2853
2854        // remove trailing space
2855        if (set.cardinality() > 0) {
2856            buf.setLength(buf.length() - 1);
2857        }
2858
2859        return buf.toString();
2860    }
2861
2862    private int lookupString(String string, String[] strings) {
2863        int size = strings.length;
2864
2865        string = string.replace('-', '_');
2866
2867        for (int i = 0; i < size; i++)
2868            if (string.equals(strings[i]))
2869                return i;
2870
2871        // if we ever get here, we should probably add the
2872        // value to WifiConfiguration to reflect that it's
2873        // supported by the WPA supplicant
2874        loge("Failed to look-up a string: " + string);
2875
2876        return -1;
2877    }
2878
2879    /* return the allowed key management based on a scan result */
2880
2881    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
2882        WifiConfiguration config = new WifiConfiguration();
2883
2884        config.SSID = "\"" + result.SSID + "\"";
2885
2886        if (VDBG) {
2887            loge("WifiConfiguration from scan results " +
2888                    config.SSID + " cap " + result.capabilities);
2889        }
2890        if (result.capabilities.contains("WEP")) {
2891            config.allowedKeyManagement.set(KeyMgmt.NONE);
2892            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
2893            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
2894        }
2895
2896        if (result.capabilities.contains("PSK")) {
2897            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
2898        }
2899
2900        if (result.capabilities.contains("EAP")) {
2901            //this is probably wrong, as we don't have a way to enter the enterprise config
2902            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
2903            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
2904        }
2905
2906        config.scanResultCache = new HashMap<String, ScanResult>();
2907        if (config.scanResultCache == null)
2908            return null;
2909        config.scanResultCache.put(result.BSSID, result);
2910
2911        return config;
2912    }
2913
2914
2915    /* Returns a unique for a given configuration */
2916    private static int configKey(WifiConfiguration config) {
2917        String key = config.configKey();
2918        return key.hashCode();
2919    }
2920
2921    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2922        pw.println("WifiConfigStore");
2923        pw.println("mLastPriority " + mLastPriority);
2924        pw.println("Configured networks");
2925        for (WifiConfiguration conf : getConfiguredNetworks()) {
2926            pw.println(conf);
2927        }
2928        pw.println();
2929
2930        if (mLocalLog != null) {
2931            pw.println("WifiConfigStore - Log Begin ----");
2932            mLocalLog.dump(fd, pw, args);
2933            pw.println("WifiConfigStore - Log End ----");
2934        }
2935    }
2936
2937    public String getConfigFile() {
2938        return ipConfigFile;
2939    }
2940
2941    protected void loge(String s) {
2942        loge(s, false);
2943    }
2944
2945    protected void loge(String s, boolean stack) {
2946        if (stack) {
2947            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2948                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
2949                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
2950                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
2951        } else {
2952            Log.e(TAG, s);
2953        }
2954    }
2955
2956    protected void log(String s) {
2957        Log.d(TAG, s);
2958    }
2959
2960    private void localLog(String s) {
2961        if (mLocalLog != null) {
2962            mLocalLog.log(s);
2963        }
2964    }
2965
2966    private void localLog(String s, int netId) {
2967        if (mLocalLog == null) {
2968            return;
2969        }
2970
2971        WifiConfiguration config;
2972        synchronized(mConfiguredNetworks) {
2973            config = mConfiguredNetworks.get(netId);
2974        }
2975
2976        if (config != null) {
2977            mLocalLog.log(s + " " + config.getPrintableSsid());
2978        } else {
2979            mLocalLog.log(s + " " + netId);
2980        }
2981    }
2982
2983    // Certificate and private key management for EnterpriseConfig
2984    static boolean needsKeyStore(WifiEnterpriseConfig config) {
2985        // Has no keys to be installed
2986        if (config.getClientCertificate() == null && config.getCaCertificate() == null)
2987            return false;
2988        return true;
2989    }
2990
2991    static boolean isHardwareBackedKey(PrivateKey key) {
2992        return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
2993    }
2994
2995    static boolean hasHardwareBackedKey(Certificate certificate) {
2996        return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
2997    }
2998
2999    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
3000        String client = config.getClientCertificateAlias();
3001        if (!TextUtils.isEmpty(client)) {
3002            // a valid client certificate is configured
3003
3004            // BUGBUG: keyStore.get() never returns certBytes; because it is not
3005            // taking WIFI_UID as a parameter. It always looks for certificate
3006            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
3007            // all certificates need software keystore until we get the get() API
3008            // fixed.
3009
3010            return true;
3011        }
3012
3013        /*
3014        try {
3015
3016            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
3017                    .USER_CERTIFICATE + client);
3018
3019            CertificateFactory factory = CertificateFactory.getInstance("X.509");
3020            if (factory == null) {
3021                Slog.e(TAG, "Error getting certificate factory");
3022                return;
3023            }
3024
3025            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
3026            if (certBytes != null) {
3027                Certificate cert = (X509Certificate) factory.generateCertificate(
3028                        new ByteArrayInputStream(certBytes));
3029
3030                if (cert != null) {
3031                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
3032
3033                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
3034                            .USER_CERTIFICATE + client);
3035                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
3036                            "does not need" ) + " software key store");
3037                } else {
3038                    Slog.d(TAG, "could not generate certificate");
3039                }
3040            } else {
3041                Slog.e(TAG, "Could not load client certificate " + Credentials
3042                        .USER_CERTIFICATE + client);
3043                mNeedsSoftwareKeystore = true;
3044            }
3045
3046        } catch(CertificateException e) {
3047            Slog.e(TAG, "Could not read certificates");
3048            mCaCert = null;
3049            mClientCertificate = null;
3050        }
3051        */
3052
3053        return false;
3054    }
3055
3056    /** called when CS ask WiFistateMachine to disconnect the current network
3057     * because the score is bad.
3058     */
3059    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
3060        /* TODO verify the bad network is current */
3061        WifiConfiguration config = mConfiguredNetworks.get(netId);
3062        if (config != null) {
3063            if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
3064                    && info.is24GHz()) || (info.getRssi() <
3065                            WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
3066                //we got disconnected and RSSI was bad, so disable light
3067                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
3068                        + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
3069                loge("handleBadNetworkDisconnectReport (+4) "
3070                        + Integer.toString(netId) + " " + info);
3071            } else {
3072                //we got disabled but RSSI is good, so disable hard
3073                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
3074                        + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
3075                loge("handleBadNetworkDisconnectReport (+8) "
3076                        + Integer.toString(netId) + " " + info);
3077            }
3078        }
3079    }
3080
3081    boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) {
3082        boolean found = false;
3083        if (BSSID == null)
3084            return found;
3085
3086        // Look for the BSSID in our config store
3087        for (WifiConfiguration config : mConfiguredNetworks.values()) {
3088            if (config.scanResultCache != null) {
3089                for (ScanResult result: config.scanResultCache.values()) {
3090                    if (result.BSSID.equals(BSSID)) {
3091                        if (enable) {
3092                            result.status = ScanResult.ENABLED;
3093                        } else {
3094                            // Black list the BSSID we we were trying to join
3095                            // so as the Roam state machine
3096                            // doesn't pick it up over and over
3097                            result.status = ScanResult.AUTO_ROAM_DISABLED;
3098                            found = true;
3099                        }
3100                    }
3101                }
3102            }
3103        }
3104        return found;
3105    }
3106
3107    int getMaxDhcpRetries() {
3108        return Settings.Global.getInt(mContext.getContentResolver(),
3109                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
3110                DEFAULT_MAX_DHCP_RETRIES);
3111    }
3112
3113    void handleSSIDStateChange(int netId, boolean enabled, String message) {
3114        WifiConfiguration config = mConfiguredNetworks.get(netId);
3115        if (config != null) {
3116            if (enabled) {
3117                loge("SSID re-enabled for  " + config.configKey() +
3118                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
3119                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
3120                //TODO: http://b/16381983 Fix Wifi Network Blacklisting
3121                //TODO: really I don't know if re-enabling is right but we
3122                //TODO: should err on the side of trying to connect
3123                //TODO: even if the attempt will fail
3124                if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
3125                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
3126                }
3127            } else {
3128                loge("SSID temp disabled for  " + config.configKey() +
3129                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
3130                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
3131                if (message != null) {
3132                    loge(" message=" + message);
3133                }
3134                if (config.selfAdded && config.lastConnected == 0) {
3135                    // This is a network we self added, and we never succeeded,
3136                    // the user did not create this network and never entered its credentials,
3137                    // so we want to be very aggressive in disabling it completely.
3138                    disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE);
3139                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
3140                    config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE;
3141                } else {
3142                    if (message != null) {
3143                        if (message.contains("WRONG_KEY")
3144                                || message.contains("AUTH_FAILED")) {
3145                            // This configuration has received an auth failure, so disable it
3146                            // temporarily because we don't want auto-join to try it out.
3147                            // this network may be re-enabled by the "usual"
3148                            // enableAllNetwork function
3149                            //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE:
3150                            //TODO: if we could count on the wrong_ley or auth_failure
3151                            //TODO: message to be correct
3152                            //TODO: then we could just mark the configuration as
3153                            //TODO: DISABLED_ON_AUTH_FAILURE
3154                            //TODO: and the configuration will stay there until
3155                            //TODO: user enter new credentials
3156                            //TODO: It is not the case however, so instead  of disabling, let's
3157                            //TODO: start blacklisting hard
3158                            //TODO: http://b/16381983 Fix Wifi Network Blacklisting
3159                            if (config.autoJoinStatus <=
3160                                    WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
3161                                // 4 auth failure will reach 128 and disable permanently
3162                                // autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128
3163                                config.setAutoJoinStatus(4 + config.autoJoinStatus * 4);
3164                                if (config.autoJoinStatus >
3165                                        WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)
3166                                    config.setAutoJoinStatus
3167                                            (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
3168                            }
3169                            if (DBG) {
3170                                loge("blacklisted " + config.configKey() + " to "
3171                                        + Integer.toString(config.autoJoinStatus));
3172                            }
3173                        } else if (message.contains("DHCP FAILURE")) {
3174                            config.numConnectionFailures++;
3175                            config.lastConnectionFailure = System.currentTimeMillis();
3176                            int maxRetries = getMaxDhcpRetries();
3177                            // maxRetries == 0 means keep trying forever
3178                            if (maxRetries > 0 && config.numConnectionFailures > maxRetries) {
3179                                /**
3180                                 * If we've exceeded the maximum number of retries for DHCP
3181                                 * to a given network, disable the network
3182                                 */
3183                                config.setAutoJoinStatus
3184                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
3185                                disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
3186                            }
3187                            if (DBG) {
3188                                loge("blacklisted " + config.configKey() + " to "
3189                                        + config.autoJoinStatus
3190                                        + " due to DHCP failure, count="
3191                                        + config.numConnectionFailures);
3192                            }
3193                        }
3194                        message.replace("\n", "");
3195                        message.replace("\r", "");
3196                        config.lastFailure = message;
3197                    }
3198                }
3199            }
3200        }
3201    }
3202
3203    boolean installKeys(WifiEnterpriseConfig config, String name) {
3204        boolean ret = true;
3205        String privKeyName = Credentials.USER_PRIVATE_KEY + name;
3206        String userCertName = Credentials.USER_CERTIFICATE + name;
3207        String caCertName = Credentials.CA_CERTIFICATE + name;
3208        if (config.getClientCertificate() != null) {
3209            byte[] privKeyData = config.getClientPrivateKey().getEncoded();
3210            if (isHardwareBackedKey(config.getClientPrivateKey())) {
3211                // Hardware backed key store is secure enough to store keys un-encrypted, this
3212                // removes the need for user to punch a PIN to get access to these keys
3213                if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
3214                ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
3215                        KeyStore.FLAG_NONE);
3216            } else {
3217                // Software backed key store is NOT secure enough to store keys un-encrypted.
3218                // Save keys encrypted so they are protected with user's PIN. User will
3219                // have to unlock phone before being able to use these keys and connect to
3220                // networks.
3221                if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
3222                ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
3223                        KeyStore.FLAG_ENCRYPTED);
3224            }
3225            if (ret == false) {
3226                return ret;
3227            }
3228
3229            ret = putCertInKeyStore(userCertName, config.getClientCertificate());
3230            if (ret == false) {
3231                // Remove private key installed
3232                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
3233                return ret;
3234            }
3235        }
3236
3237        if (config.getCaCertificate() != null) {
3238            ret = putCertInKeyStore(caCertName, config.getCaCertificate());
3239            if (ret == false) {
3240                if (config.getClientCertificate() != null) {
3241                    // Remove client key+cert
3242                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
3243                    mKeyStore.delete(userCertName, Process.WIFI_UID);
3244                }
3245                return ret;
3246            }
3247        }
3248
3249        // Set alias names
3250        if (config.getClientCertificate() != null) {
3251            config.setClientCertificateAlias(name);
3252            config.resetClientKeyEntry();
3253        }
3254
3255        if (config.getCaCertificate() != null) {
3256            config.setCaCertificateAlias(name);
3257            config.resetCaCertificate();
3258        }
3259
3260        return ret;
3261    }
3262
3263    private boolean putCertInKeyStore(String name, Certificate cert) {
3264        try {
3265            byte[] certData = Credentials.convertToPem(cert);
3266            if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
3267            return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
3268
3269        } catch (IOException e1) {
3270            return false;
3271        } catch (CertificateException e2) {
3272            return false;
3273        }
3274    }
3275
3276    void removeKeys(WifiEnterpriseConfig config) {
3277        String client = config.getClientCertificateAlias();
3278        // a valid client certificate is configured
3279        if (!TextUtils.isEmpty(client)) {
3280            if (DBG) Log.d(TAG, "removing client private key and user cert");
3281            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
3282            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
3283        }
3284
3285        String ca = config.getCaCertificateAlias();
3286        // a valid ca certificate is configured
3287        if (!TextUtils.isEmpty(ca)) {
3288            if (DBG) Log.d(TAG, "removing CA cert");
3289            mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
3290        }
3291    }
3292
3293
3294    /** Migrates the old style TLS config to the new config style. This should only be used
3295     * when restoring an old wpa_supplicant.conf or upgrading from a previous
3296     * platform version.
3297     * @return true if the config was updated
3298     * @hide
3299     */
3300    boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
3301        String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
3302        /*
3303         * If the old configuration value is not present, then there is nothing
3304         * to do.
3305         */
3306        if (TextUtils.isEmpty(oldPrivateKey)) {
3307            return false;
3308        } else {
3309            // Also ignore it if it's empty quotes.
3310            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
3311            if (TextUtils.isEmpty(oldPrivateKey)) {
3312                return false;
3313            }
3314        }
3315
3316        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
3317        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
3318                WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
3319
3320        /*
3321        * The old key started with the keystore:// URI prefix, but we don't
3322        * need that anymore. Trim it off if it exists.
3323        */
3324        final String keyName;
3325        if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
3326            keyName = new String(
3327                    oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
3328        } else {
3329            keyName = oldPrivateKey;
3330        }
3331        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
3332
3333        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
3334                config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
3335
3336        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
3337                config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
3338
3339        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
3340                config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
3341
3342        // Remove old private_key string so we don't run this again.
3343        mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
3344
3345        return true;
3346    }
3347
3348    /** Migrate certs from global pool to wifi UID if not already done */
3349    void migrateCerts(WifiEnterpriseConfig config) {
3350        String client = config.getClientCertificateAlias();
3351        // a valid client certificate is configured
3352        if (!TextUtils.isEmpty(client)) {
3353            if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
3354                mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
3355                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
3356                mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
3357                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
3358            }
3359        }
3360
3361        String ca = config.getCaCertificateAlias();
3362        // a valid ca certificate is configured
3363        if (!TextUtils.isEmpty(ca)) {
3364            if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
3365                mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
3366                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
3367            }
3368        }
3369    }
3370
3371}
3372