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