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