WifiConfigStore.java revision 439935a9ec391392a1aea323d7f18c328fea2490
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.LinkAddress;
22import android.net.LinkProperties;
23import android.net.NetworkUtils;
24import android.net.NetworkInfo.DetailedState;
25import android.net.ProxyInfo;
26import android.net.RouteInfo;
27import android.net.wifi.WifiConfiguration;
28import android.net.wifi.WifiConfiguration.IpAssignment;
29import android.net.wifi.WifiConfiguration.KeyMgmt;
30import android.net.wifi.WifiConfiguration.ProxySettings;
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.os.Environment;
40import android.os.FileObserver;
41import android.os.Handler;
42import android.os.HandlerThread;
43import android.os.Process;
44import android.os.UserHandle;
45import android.security.Credentials;
46import android.security.KeyChain;
47import android.security.KeyStore;
48import android.text.TextUtils;
49import android.util.LocalLog;
50import android.util.Log;
51
52import java.io.BufferedInputStream;
53import java.io.BufferedOutputStream;
54import java.io.BufferedReader;
55import java.io.DataInputStream;
56import java.io.DataOutputStream;
57import java.io.EOFException;
58import java.io.File;
59import java.io.FileDescriptor;
60import java.io.FileInputStream;
61import java.io.FileNotFoundException;
62import java.io.FileOutputStream;
63import java.io.FileReader;
64import java.io.IOException;
65import java.io.PrintWriter;
66import java.net.InetAddress;
67import java.security.PrivateKey;
68import java.security.cert.Certificate;
69import java.security.cert.CertificateException;
70import java.util.ArrayList;
71import java.util.BitSet;
72import java.util.Collection;
73import java.util.HashMap;
74import java.util.List;
75
76/**
77 * This class provides the API to manage configured
78 * wifi networks. The API is not thread safe is being
79 * used only from WifiStateMachine.
80 *
81 * It deals with the following
82 * - Add/update/remove a WifiConfiguration
83 *   The configuration contains two types of information.
84 *     = IP and proxy configuration that is handled by WifiConfigStore and
85 *       is saved to disk on any change.
86 *
87 *       The format of configuration file is as follows:
88 *       <version>
89 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
90 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
91 *       ..
92 *
93 *       (key, value) pairs for a given network are grouped together and can
94 *       be in any order. A EOS at the end of a set of (key, value) pairs
95 *       indicates that the next set of (key, value) pairs are for a new
96 *       network. A network is identified by a unique ID_KEY. If there is no
97 *       ID_KEY in the (key, value) pairs, the data is discarded.
98 *
99 *       An invalid version on read would result in discarding the contents of
100 *       the file. On the next write, the latest version is written to file.
101 *
102 *       Any failures during read or write to the configuration file are ignored
103 *       without reporting to the user since the likelihood of these errors are
104 *       low and the impact on connectivity is low.
105 *
106 *     = SSID & security details that is pushed to the supplicant.
107 *       supplicant saves these details to the disk on calling
108 *       saveConfigCommand().
109 *
110 *       We have two kinds of APIs exposed:
111 *        > public API calls that provide fine grained control
112 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
113 *          removeNetwork(). For these calls, the config is not persisted
114 *          to the disk. (TODO: deprecate these calls in WifiManager)
115 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
116 *          These calls persist the supplicant config to disk.
117 *
118 * - Maintain a list of configured networks for quick access
119 *
120 */
121public class WifiConfigStore {
122
123    private Context mContext;
124    private static final String TAG = "WifiConfigStore";
125    private static final boolean DBG = true;
126    private static final boolean VDBG = false;
127
128    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
129
130    /* configured networks with network id as the key */
131    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
132            new HashMap<Integer, WifiConfiguration>();
133
134    /* A network id is a unique identifier for a network configured in the
135     * supplicant. Network ids are generated when the supplicant reads
136     * the configuration file at start and can thus change for networks.
137     * We store the IP configuration for networks along with a unique id
138     * that is generated from SSID and security type of the network. A mapping
139     * from the generated unique id to network id of the network is needed to
140     * map supplicant config to IP configuration. */
141    private HashMap<Integer, Integer> mNetworkIds =
142            new HashMap<Integer, Integer>();
143
144    /* Tracks the highest priority of configured networks */
145    private int mLastPriority = -1;
146
147    private static final String ipConfigFile = Environment.getDataDirectory() +
148            "/misc/wifi/ipconfig.txt";
149
150    private static final int IPCONFIG_FILE_VERSION = 2;
151
152    /* IP and proxy configuration keys */
153    private static final String ID_KEY = "id";
154    private static final String IP_ASSIGNMENT_KEY = "ipAssignment";
155    private static final String LINK_ADDRESS_KEY = "linkAddress";
156    private static final String GATEWAY_KEY = "gateway";
157    private static final String DNS_KEY = "dns";
158    private static final String PROXY_SETTINGS_KEY = "proxySettings";
159    private static final String PROXY_HOST_KEY = "proxyHost";
160    private static final String PROXY_PORT_KEY = "proxyPort";
161    private static final String PROXY_PAC_FILE = "proxyPac";
162    private static final String EXCLUSION_LIST_KEY = "exclusionList";
163    private static final String EOS = "eos";
164
165
166    /* Enterprise configuration keys */
167    /**
168     * In old configurations, the "private_key" field was used. However, newer
169     * configurations use the key_id field with the engine_id set to "keystore".
170     * If this field is found in the configuration, the migration code is
171     * triggered.
172     */
173    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
174
175    /** This represents an empty value of an enterprise field.
176     * NULL is used at wpa_supplicant to indicate an empty value
177     */
178    static final String EMPTY_VALUE = "NULL";
179
180    /** Internal use only */
181    private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
182            WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
183            WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
184            WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
185            WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
186            WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
187            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
188
189    private final LocalLog mLocalLog;
190    private final WpaConfigFileObserver mFileObserver;
191
192    private WifiNative mWifiNative;
193    private final KeyStore mKeyStore = KeyStore.getInstance();
194
195    WifiConfigStore(Context c, WifiNative wn) {
196        mContext = c;
197        mWifiNative = wn;
198
199        if (VDBG) {
200            mLocalLog = mWifiNative.getLocalLog();
201            mFileObserver = new WpaConfigFileObserver();
202            mFileObserver.startWatching();
203        } else {
204            mLocalLog = null;
205            mFileObserver = null;
206        }
207    }
208
209    class WpaConfigFileObserver extends FileObserver {
210
211        public WpaConfigFileObserver() {
212            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
213        }
214
215        @Override
216        public void onEvent(int event, String path) {
217            if (event == CLOSE_WRITE) {
218                File file = new File(SUPPLICANT_CONFIG_FILE);
219                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
220            }
221        }
222    }
223
224
225    /**
226     * Fetch the list of configured networks
227     * and enable all stored networks in supplicant.
228     */
229    void loadAndEnableAllNetworks() {
230        if (DBG) log("Loading config and enabling all networks");
231        loadConfiguredNetworks();
232        enableAllNetworks();
233    }
234
235    /**
236     * Fetch the list of currently configured networks
237     * @return List of networks
238     */
239    List<WifiConfiguration> getConfiguredNetworks() {
240        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
241        for(WifiConfiguration config : mConfiguredNetworks.values()) {
242            networks.add(new WifiConfiguration(config));
243        }
244        return networks;
245    }
246
247    /**
248     * enable all networks and save config. This will be a no-op if the list
249     * of configured networks indicates all networks as being enabled
250     */
251    void enableAllNetworks() {
252        boolean networkEnabledStateChanged = false;
253        for(WifiConfiguration config : mConfiguredNetworks.values()) {
254            if(config != null && config.status == Status.DISABLED) {
255                if(mWifiNative.enableNetwork(config.networkId, false)) {
256                    networkEnabledStateChanged = true;
257                    config.status = Status.ENABLED;
258                } else {
259                    loge("Enable network failed on " + config.networkId);
260                }
261            }
262        }
263
264        if (networkEnabledStateChanged) {
265            mWifiNative.saveConfig();
266            sendConfiguredNetworksChangedBroadcast();
267        }
268    }
269
270
271    /**
272     * Selects the specified network for connection. This involves
273     * updating the priority of all the networks and enabling the given
274     * network while disabling others.
275     *
276     * Selecting a network will leave the other networks disabled and
277     * a call to enableAllNetworks() needs to be issued upon a connection
278     * or a failure event from supplicant
279     *
280     * @param netId network to select for connection
281     * @return false if the network id is invalid
282     */
283    boolean selectNetwork(int netId) {
284        if (VDBG) localLog("selectNetwork", netId);
285        if (netId == INVALID_NETWORK_ID) return false;
286
287        // Reset the priority of each network at start or if it goes too high.
288        if (mLastPriority == -1 || mLastPriority > 1000000) {
289            for(WifiConfiguration config : mConfiguredNetworks.values()) {
290                if (config.networkId != INVALID_NETWORK_ID) {
291                    config.priority = 0;
292                    addOrUpdateNetworkNative(config);
293                }
294            }
295            mLastPriority = 0;
296        }
297
298        // Set to the highest priority and save the configuration.
299        WifiConfiguration config = new WifiConfiguration();
300        config.networkId = netId;
301        config.priority = ++mLastPriority;
302
303        addOrUpdateNetworkNative(config);
304        mWifiNative.saveConfig();
305
306        /* Enable the given network while disabling all other networks */
307        enableNetworkWithoutBroadcast(netId, true);
308
309       /* Avoid saving the config & sending a broadcast to prevent settings
310        * from displaying a disabled list of networks */
311        return true;
312    }
313
314    /**
315     * Add/update the specified configuration and save config
316     *
317     * @param config WifiConfiguration to be saved
318     * @return network update result
319     */
320    NetworkUpdateResult saveNetwork(WifiConfiguration config) {
321        if (VDBG) localLog("saveNetwork", config.networkId);
322        // A new network cannot have null SSID
323        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
324                config.SSID == null)) {
325            return new NetworkUpdateResult(INVALID_NETWORK_ID);
326        }
327
328        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
329        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
330        int netId = result.getNetworkId();
331        /* enable a new network */
332        if (newNetwork && netId != INVALID_NETWORK_ID) {
333            mWifiNative.enableNetwork(netId, false);
334            mConfiguredNetworks.get(netId).status = Status.ENABLED;
335        }
336        mWifiNative.saveConfig();
337        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
338                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
339        return result;
340    }
341
342    void updateStatus(int netId, DetailedState state) {
343        if (netId != INVALID_NETWORK_ID) {
344            WifiConfiguration config = mConfiguredNetworks.get(netId);
345            if (config == null) return;
346            switch (state) {
347                case CONNECTED:
348                    config.status = Status.CURRENT;
349                    break;
350                case DISCONNECTED:
351                    //If network is already disabled, keep the status
352                    if (config.status == Status.CURRENT) {
353                        config.status = Status.ENABLED;
354                    }
355                    break;
356                default:
357                    //do nothing, retain the existing state
358                    break;
359            }
360        }
361    }
362
363    /**
364     * Forget the specified network and save config
365     *
366     * @param netId network to forget
367     * @return {@code true} if it succeeds, {@code false} otherwise
368     */
369    boolean forgetNetwork(int netId) {
370        if (VDBG) localLog("forgetNetwork", netId);
371        if (mWifiNative.removeNetwork(netId)) {
372            mWifiNative.saveConfig();
373            removeConfigAndSendBroadcastIfNeeded(netId);
374            return true;
375        } else {
376            loge("Failed to remove network " + netId);
377            return false;
378        }
379    }
380
381    /**
382     * Add/update a network. Note that there is no saveConfig operation.
383     * This function is retained for compatibility with the public
384     * API. The more powerful saveNetwork() is used by the
385     * state machine
386     *
387     * @param config wifi configuration to add/update
388     * @return network Id
389     */
390    int addOrUpdateNetwork(WifiConfiguration config) {
391        if (VDBG) localLog("addOrUpdateNetwork", config.networkId);
392        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
393        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
394            sendConfiguredNetworksChangedBroadcast(mConfiguredNetworks.get(result.getNetworkId()),
395                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
396                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
397        }
398        return result.getNetworkId();
399    }
400
401    /**
402     * Remove a network. Note that there is no saveConfig operation.
403     * This function is retained for compatibility with the public
404     * API. The more powerful forgetNetwork() is used by the
405     * state machine for network removal
406     *
407     * @param netId network to be removed
408     * @return {@code true} if it succeeds, {@code false} otherwise
409     */
410    boolean removeNetwork(int netId) {
411        if (VDBG) localLog("removeNetwork", netId);
412        boolean ret = mWifiNative.removeNetwork(netId);
413        if (ret) {
414            removeConfigAndSendBroadcastIfNeeded(netId);
415        }
416        return ret;
417    }
418
419    private void removeConfigAndSendBroadcastIfNeeded(int netId) {
420        WifiConfiguration config = mConfiguredNetworks.get(netId);
421        if (config != null) {
422            // Remove any associated keys
423            if (config.enterpriseConfig != null) {
424                removeKeys(config.enterpriseConfig);
425            }
426            mConfiguredNetworks.remove(netId);
427            mNetworkIds.remove(configKey(config));
428
429            writeIpAndProxyConfigurations();
430            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
431        }
432    }
433
434    /**
435     * Enable a network. Note that there is no saveConfig operation.
436     * This function is retained for compatibility with the public
437     * API. The more powerful selectNetwork()/saveNetwork() is used by the
438     * state machine for connecting to a network
439     *
440     * @param netId network to be enabled
441     * @return {@code true} if it succeeds, {@code false} otherwise
442     */
443    boolean enableNetwork(int netId, boolean disableOthers) {
444        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
445        if (disableOthers) {
446            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
447            sendConfiguredNetworksChangedBroadcast();
448        } else {
449            if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
450            WifiConfiguration enabledNetwork = null;
451            synchronized(mConfiguredNetworks) {
452                enabledNetwork = mConfiguredNetworks.get(netId);
453            }
454            // check just in case the network was removed by someone else.
455            if (enabledNetwork != null) {
456                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
457                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
458            }
459        }
460        return ret;
461    }
462
463    boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
464        boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
465
466        WifiConfiguration config = mConfiguredNetworks.get(netId);
467        if (config != null) config.status = Status.ENABLED;
468
469        if (disableOthers) {
470            markAllNetworksDisabledExcept(netId);
471        }
472        return ret;
473    }
474
475    void disableAllNetworks() {
476        if (VDBG) localLog("disableAllNetworks");
477        boolean networkDisabled = false;
478        for(WifiConfiguration config : mConfiguredNetworks.values()) {
479            if(config != null && config.status != Status.DISABLED) {
480                if(mWifiNative.disableNetwork(config.networkId)) {
481                    networkDisabled = true;
482                    config.status = Status.DISABLED;
483                } else {
484                    loge("Disable network failed on " + config.networkId);
485                }
486            }
487        }
488
489        if (networkDisabled) {
490            sendConfiguredNetworksChangedBroadcast();
491        }
492    }
493    /**
494     * Disable a network. Note that there is no saveConfig operation.
495     * @param netId network to be disabled
496     * @return {@code true} if it succeeds, {@code false} otherwise
497     */
498    boolean disableNetwork(int netId) {
499        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
500    }
501
502    /**
503     * Disable a network. Note that there is no saveConfig operation.
504     * @param netId network to be disabled
505     * @param reason reason code network was disabled
506     * @return {@code true} if it succeeds, {@code false} otherwise
507     */
508    boolean disableNetwork(int netId, int reason) {
509        if (VDBG) localLog("disableNetwork", netId);
510        boolean ret = mWifiNative.disableNetwork(netId);
511        WifiConfiguration network = null;
512        WifiConfiguration config = mConfiguredNetworks.get(netId);
513        /* Only change the reason if the network was not previously disabled */
514        if (config != null && config.status != Status.DISABLED) {
515            config.status = Status.DISABLED;
516            config.disableReason = reason;
517            network = config;
518        }
519        if (network != null) {
520            sendConfiguredNetworksChangedBroadcast(network,
521                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
522        }
523        return ret;
524    }
525
526    /**
527     * Save the configured networks in supplicant to disk
528     * @return {@code true} if it succeeds, {@code false} otherwise
529     */
530    boolean saveConfig() {
531        return mWifiNative.saveConfig();
532    }
533
534    /**
535     * Start WPS pin method configuration with pin obtained
536     * from the access point
537     * @param config WPS configuration
538     * @return Wps result containing status and pin
539     */
540    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
541        WpsResult result = new WpsResult();
542        if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
543            /* WPS leaves all networks disabled */
544            markAllNetworksDisabled();
545            result.status = WpsResult.Status.SUCCESS;
546        } else {
547            loge("Failed to start WPS pin method configuration");
548            result.status = WpsResult.Status.FAILURE;
549        }
550        return result;
551    }
552
553    /**
554     * Start WPS pin method configuration with pin obtained
555     * from the device
556     * @return WpsResult indicating status and pin
557     */
558    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
559        WpsResult result = new WpsResult();
560        result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
561        /* WPS leaves all networks disabled */
562        if (!TextUtils.isEmpty(result.pin)) {
563            markAllNetworksDisabled();
564            result.status = WpsResult.Status.SUCCESS;
565        } else {
566            loge("Failed to start WPS pin method configuration");
567            result.status = WpsResult.Status.FAILURE;
568        }
569        return result;
570    }
571
572    /**
573     * Start WPS push button configuration
574     * @param config WPS configuration
575     * @return WpsResult indicating status and pin
576     */
577    WpsResult startWpsPbc(WpsInfo config) {
578        WpsResult result = new WpsResult();
579        if (mWifiNative.startWpsPbc(config.BSSID)) {
580            /* WPS leaves all networks disabled */
581            markAllNetworksDisabled();
582            result.status = WpsResult.Status.SUCCESS;
583        } else {
584            loge("Failed to start WPS push button configuration");
585            result.status = WpsResult.Status.FAILURE;
586        }
587        return result;
588    }
589
590    /**
591     * Fetch the link properties for a given network id
592     * @return LinkProperties for the given network id
593     */
594    LinkProperties getLinkProperties(int netId) {
595        WifiConfiguration config = mConfiguredNetworks.get(netId);
596        if (config != null) return new LinkProperties(config.linkProperties);
597        return null;
598    }
599
600    /**
601     * set IP configuration for a given network id
602     */
603    void setLinkProperties(int netId, LinkProperties linkProperties) {
604        WifiConfiguration config = mConfiguredNetworks.get(netId);
605        if (config != null) {
606            // add old proxy details - TODO - is this still needed?
607            if(config.linkProperties != null) {
608                linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
609            }
610            config.linkProperties = linkProperties;
611        }
612    }
613
614    /**
615     * clear IP configuration for a given network id
616     * @param network id
617     */
618    void clearLinkProperties(int netId) {
619        WifiConfiguration config = mConfiguredNetworks.get(netId);
620        if (config != null && config.linkProperties != null) {
621            // Clear everything except proxy
622            ProxyInfo proxy = config.linkProperties.getHttpProxy();
623            config.linkProperties.clear();
624            config.linkProperties.setHttpProxy(proxy);
625        }
626    }
627
628
629    /**
630     * Fetch the proxy properties for a given network id
631     * @param network id
632     * @return ProxyInfo for the network id
633     */
634    ProxyInfo getProxyProperties(int netId) {
635        LinkProperties linkProperties = getLinkProperties(netId);
636        if (linkProperties != null) {
637            return new ProxyInfo(linkProperties.getHttpProxy());
638        }
639        return null;
640    }
641
642    /**
643     * Return if the specified network is using static IP
644     * @param network id
645     * @return {@code true} if using static ip for netId
646     */
647    boolean isUsingStaticIp(int netId) {
648        WifiConfiguration config = mConfiguredNetworks.get(netId);
649        if (config != null && config.ipAssignment == IpAssignment.STATIC) {
650            return true;
651        }
652        return false;
653    }
654
655    /**
656     * Should be called when a single network configuration is made.
657     * @param network The network configuration that changed.
658     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
659     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
660     */
661    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
662            int reason) {
663        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
664        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
665        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
666        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
667        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
668        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
669    }
670
671    /**
672     * Should be called when multiple network configuration changes are made.
673     */
674    private void sendConfiguredNetworksChangedBroadcast() {
675        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
676        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
677        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
678        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
679    }
680
681    void loadConfiguredNetworks() {
682        String listStr = mWifiNative.listNetworks();
683        mLastPriority = 0;
684
685        mConfiguredNetworks.clear();
686        mNetworkIds.clear();
687
688        if (listStr == null)
689            return;
690
691        String[] lines = listStr.split("\n");
692        // Skip the first line, which is a header
693        for (int i = 1; i < lines.length; i++) {
694            String[] result = lines[i].split("\t");
695            // network-id | ssid | bssid | flags
696            WifiConfiguration config = new WifiConfiguration();
697            try {
698                config.networkId = Integer.parseInt(result[0]);
699            } catch(NumberFormatException e) {
700                loge("Failed to read network-id '" + result[0] + "'");
701                continue;
702            }
703            if (result.length > 3) {
704                if (result[3].indexOf("[CURRENT]") != -1)
705                    config.status = WifiConfiguration.Status.CURRENT;
706                else if (result[3].indexOf("[DISABLED]") != -1)
707                    config.status = WifiConfiguration.Status.DISABLED;
708                else
709                    config.status = WifiConfiguration.Status.ENABLED;
710            } else {
711                config.status = WifiConfiguration.Status.ENABLED;
712            }
713            readNetworkVariables(config);
714            if (config.priority > mLastPriority) {
715                mLastPriority = config.priority;
716            }
717            config.ipAssignment = IpAssignment.DHCP;
718            config.proxySettings = ProxySettings.NONE;
719
720            if (mNetworkIds.containsKey(configKey(config))) {
721                // That SSID is already known, just ignore this duplicate entry
722                if (VDBG) localLog("discarded duplicate network", config.networkId);
723            } else {
724                mConfiguredNetworks.put(config.networkId, config);
725                mNetworkIds.put(configKey(config), config.networkId);
726                if (VDBG) localLog("loaded configured network", config.networkId);
727            }
728        }
729
730        readIpAndProxyConfigurations();
731        sendConfiguredNetworksChangedBroadcast();
732
733        if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
734
735        if (mNetworkIds.size() == 0) {
736            // no networks? Lets log if the wpa_supplicant.conf file contents
737            BufferedReader reader = null;
738            try {
739                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
740                if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---");
741                for (String line = reader.readLine(); line != null; line = reader.readLine()) {
742                    if (VDBG) localLog(line);
743                }
744                if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---");
745            } catch (FileNotFoundException e) {
746                if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
747            } catch (IOException e) {
748                if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
749            } finally {
750                try {
751                    if (reader != null) {
752                        reader.close();
753                    }
754                } catch (IOException e) {
755                    // Just ignore the fact that we couldn't close
756                }
757            }
758        }
759    }
760
761    /* Mark all networks except specified netId as disabled */
762    private void markAllNetworksDisabledExcept(int netId) {
763        for(WifiConfiguration config : mConfiguredNetworks.values()) {
764            if(config != null && config.networkId != netId) {
765                if (config.status != Status.DISABLED) {
766                    config.status = Status.DISABLED;
767                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
768                }
769            }
770        }
771    }
772
773    private void markAllNetworksDisabled() {
774        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
775    }
776
777    boolean needsUnlockedKeyStore() {
778
779        // Any network using certificates to authenticate access requires
780        // unlocked key store; unless the certificates can be stored with
781        // hardware encryption
782
783        for(WifiConfiguration config : mConfiguredNetworks.values()) {
784
785            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
786                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
787
788                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
789                    return true;
790                }
791            }
792        }
793
794        return false;
795    }
796
797    private void writeIpAndProxyConfigurations() {
798
799        /* Make a copy */
800        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
801        for(WifiConfiguration config : mConfiguredNetworks.values()) {
802            networks.add(new WifiConfiguration(config));
803        }
804
805        DelayedDiskWrite.write(networks);
806    }
807
808    private static class DelayedDiskWrite {
809
810        private static HandlerThread sDiskWriteHandlerThread;
811        private static Handler sDiskWriteHandler;
812        /* Tracks multiple writes on the same thread */
813        private static int sWriteSequence = 0;
814        private static final String TAG = "DelayedDiskWrite";
815
816        static void write (final List<WifiConfiguration> networks) {
817
818            /* Do a delayed write to disk on a seperate handler thread */
819            synchronized (DelayedDiskWrite.class) {
820                if (++sWriteSequence == 1) {
821                    sDiskWriteHandlerThread = new HandlerThread("WifiConfigThread");
822                    sDiskWriteHandlerThread.start();
823                    sDiskWriteHandler = new Handler(sDiskWriteHandlerThread.getLooper());
824                }
825            }
826
827            sDiskWriteHandler.post(new Runnable() {
828                    @Override
829                    public void run() {
830                        onWriteCalled(networks);
831                    }
832                });
833        }
834
835        private static void onWriteCalled(List<WifiConfiguration> networks) {
836
837            DataOutputStream out = null;
838            try {
839                out = new DataOutputStream(new BufferedOutputStream(
840                            new FileOutputStream(ipConfigFile)));
841
842                out.writeInt(IPCONFIG_FILE_VERSION);
843
844                for(WifiConfiguration config : networks) {
845                    boolean writeToFile = false;
846
847                    try {
848                        LinkProperties linkProperties = config.linkProperties;
849                        switch (config.ipAssignment) {
850                            case STATIC:
851                                out.writeUTF(IP_ASSIGNMENT_KEY);
852                                out.writeUTF(config.ipAssignment.toString());
853                                for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
854                                    out.writeUTF(LINK_ADDRESS_KEY);
855                                    out.writeUTF(linkAddr.getAddress().getHostAddress());
856                                    out.writeInt(linkAddr.getNetworkPrefixLength());
857                                }
858                                for (RouteInfo route : linkProperties.getRoutes()) {
859                                    out.writeUTF(GATEWAY_KEY);
860                                    LinkAddress dest = route.getDestination();
861                                    if (dest != null) {
862                                        out.writeInt(1);
863                                        out.writeUTF(dest.getAddress().getHostAddress());
864                                        out.writeInt(dest.getNetworkPrefixLength());
865                                    } else {
866                                        out.writeInt(0);
867                                    }
868                                    if (route.getGateway() != null) {
869                                        out.writeInt(1);
870                                        out.writeUTF(route.getGateway().getHostAddress());
871                                    } else {
872                                        out.writeInt(0);
873                                    }
874                                }
875                                for (InetAddress inetAddr : linkProperties.getDnses()) {
876                                    out.writeUTF(DNS_KEY);
877                                    out.writeUTF(inetAddr.getHostAddress());
878                                }
879                                writeToFile = true;
880                                break;
881                            case DHCP:
882                                out.writeUTF(IP_ASSIGNMENT_KEY);
883                                out.writeUTF(config.ipAssignment.toString());
884                                writeToFile = true;
885                                break;
886                            case UNASSIGNED:
887                                /* Ignore */
888                                break;
889                            default:
890                                loge("Ignore invalid ip assignment while writing");
891                                break;
892                        }
893
894                        switch (config.proxySettings) {
895                            case STATIC:
896                                ProxyInfo proxyProperties = linkProperties.getHttpProxy();
897                                String exclusionList = proxyProperties.getExclusionList();
898                                out.writeUTF(PROXY_SETTINGS_KEY);
899                                out.writeUTF(config.proxySettings.toString());
900                                out.writeUTF(PROXY_HOST_KEY);
901                                out.writeUTF(proxyProperties.getHost());
902                                out.writeUTF(PROXY_PORT_KEY);
903                                out.writeInt(proxyProperties.getPort());
904                                out.writeUTF(EXCLUSION_LIST_KEY);
905                                out.writeUTF(exclusionList);
906                                writeToFile = true;
907                                break;
908                            case PAC:
909                                ProxyInfo proxyPacProperties = linkProperties.getHttpProxy();
910                                out.writeUTF(PROXY_SETTINGS_KEY);
911                                out.writeUTF(config.proxySettings.toString());
912                                out.writeUTF(PROXY_PAC_FILE);
913                                out.writeUTF(proxyPacProperties.getPacFileUrl());
914                                writeToFile = true;
915                                break;
916                            case NONE:
917                                out.writeUTF(PROXY_SETTINGS_KEY);
918                                out.writeUTF(config.proxySettings.toString());
919                                writeToFile = true;
920                                break;
921                            case UNASSIGNED:
922                                /* Ignore */
923                                break;
924                            default:
925                                loge("Ignthisore invalid proxy settings while writing");
926                                break;
927                        }
928                        if (writeToFile) {
929                            out.writeUTF(ID_KEY);
930                            out.writeInt(configKey(config));
931                        }
932                    } catch (NullPointerException e) {
933                        loge("Failure in writing " + config.linkProperties + e);
934                    }
935                    out.writeUTF(EOS);
936                }
937
938            } catch (IOException e) {
939                loge("Error writing data file");
940            } finally {
941                if (out != null) {
942                    try {
943                        out.close();
944                    } catch (Exception e) {}
945                }
946
947                //Quit if no more writes sent
948                synchronized (DelayedDiskWrite.class) {
949                    if (--sWriteSequence == 0) {
950                        sDiskWriteHandler.getLooper().quit();
951                        sDiskWriteHandler = null;
952                        sDiskWriteHandlerThread = null;
953                    }
954                }
955            }
956        }
957
958        private static void loge(String s) {
959            Log.e(TAG, s);
960        }
961    }
962
963    private void readIpAndProxyConfigurations() {
964
965        DataInputStream in = null;
966        try {
967            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
968                    ipConfigFile)));
969
970            int version = in.readInt();
971            if (version != 2 && version != 1) {
972                loge("Bad version on IP configuration file, ignore read");
973                return;
974            }
975
976            while (true) {
977                int id = -1;
978                // Default is DHCP with no proxy
979                IpAssignment ipAssignment = IpAssignment.DHCP;
980                ProxySettings proxySettings = ProxySettings.NONE;
981                LinkProperties linkProperties = new LinkProperties();
982                String proxyHost = null;
983                String pacFileUrl = null;
984                int proxyPort = -1;
985                String exclusionList = null;
986                String key;
987
988                do {
989                    key = in.readUTF();
990                    try {
991                        if (key.equals(ID_KEY)) {
992                            id = in.readInt();
993                        } else if (key.equals(IP_ASSIGNMENT_KEY)) {
994                            ipAssignment = IpAssignment.valueOf(in.readUTF());
995                        } else if (key.equals(LINK_ADDRESS_KEY)) {
996                            LinkAddress linkAddr = new LinkAddress(
997                                    NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
998                            linkProperties.addLinkAddress(linkAddr);
999                        } else if (key.equals(GATEWAY_KEY)) {
1000                            LinkAddress dest = null;
1001                            InetAddress gateway = null;
1002                            if (version == 1) {
1003                                // only supported default gateways - leave the dest/prefix empty
1004                                gateway = NetworkUtils.numericToInetAddress(in.readUTF());
1005                            } else {
1006                                if (in.readInt() == 1) {
1007                                    dest = new LinkAddress(
1008                                            NetworkUtils.numericToInetAddress(in.readUTF()),
1009                                            in.readInt());
1010                                }
1011                                if (in.readInt() == 1) {
1012                                    gateway = NetworkUtils.numericToInetAddress(in.readUTF());
1013                                }
1014                            }
1015                            linkProperties.addRoute(new RouteInfo(dest, gateway));
1016                        } else if (key.equals(DNS_KEY)) {
1017                            linkProperties.addDns(
1018                                    NetworkUtils.numericToInetAddress(in.readUTF()));
1019                        } else if (key.equals(PROXY_SETTINGS_KEY)) {
1020                            proxySettings = ProxySettings.valueOf(in.readUTF());
1021                        } else if (key.equals(PROXY_HOST_KEY)) {
1022                            proxyHost = in.readUTF();
1023                        } else if (key.equals(PROXY_PORT_KEY)) {
1024                            proxyPort = in.readInt();
1025                        } else if (key.equals(PROXY_PAC_FILE)) {
1026                            pacFileUrl = in.readUTF();
1027                        } else if (key.equals(EXCLUSION_LIST_KEY)) {
1028                            exclusionList = in.readUTF();
1029                        } else if (key.equals(EOS)) {
1030                            break;
1031                        } else {
1032                            loge("Ignore unknown key " + key + "while reading");
1033                        }
1034                    } catch (IllegalArgumentException e) {
1035                        loge("Ignore invalid address while reading" + e);
1036                    }
1037                } while (true);
1038
1039                if (id != -1) {
1040                    WifiConfiguration config = mConfiguredNetworks.get(
1041                            mNetworkIds.get(id));
1042
1043                    if (config == null) {
1044                        loge("configuration found for missing network, ignored");
1045                    } else {
1046                        config.linkProperties = linkProperties;
1047                        switch (ipAssignment) {
1048                            case STATIC:
1049                            case DHCP:
1050                                config.ipAssignment = ipAssignment;
1051                                break;
1052                            case UNASSIGNED:
1053                                loge("BUG: Found UNASSIGNED IP on file, use DHCP");
1054                                config.ipAssignment = IpAssignment.DHCP;
1055                                break;
1056                            default:
1057                                loge("Ignore invalid ip assignment while reading");
1058                                break;
1059                        }
1060
1061                        switch (proxySettings) {
1062                            case STATIC:
1063                                config.proxySettings = proxySettings;
1064                                ProxyInfo proxyProperties =
1065                                    new ProxyInfo(proxyHost, proxyPort, exclusionList);
1066                                linkProperties.setHttpProxy(proxyProperties);
1067                                break;
1068                            case PAC:
1069                                config.proxySettings = proxySettings;
1070                                ProxyInfo proxyPacProperties =
1071                                        new ProxyInfo(pacFileUrl);
1072                                linkProperties.setHttpProxy(proxyPacProperties);
1073                                break;
1074                            case NONE:
1075                                config.proxySettings = proxySettings;
1076                                break;
1077                            case UNASSIGNED:
1078                                loge("BUG: Found UNASSIGNED proxy on file, use NONE");
1079                                config.proxySettings = ProxySettings.NONE;
1080                                break;
1081                            default:
1082                                loge("Ignore invalid proxy settings while reading");
1083                                break;
1084                        }
1085                    }
1086                } else {
1087                    if (DBG) log("Missing id while parsing configuration");
1088                }
1089            }
1090        } catch (EOFException ignore) {
1091        } catch (IOException e) {
1092            loge("Error parsing configuration" + e);
1093        } finally {
1094            if (in != null) {
1095                try {
1096                    in.close();
1097                } catch (Exception e) {}
1098            }
1099        }
1100    }
1101
1102    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
1103        /*
1104         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1105         * network configuration. Otherwise, the networkId should
1106         * refer to an existing configuration.
1107         */
1108
1109        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1110
1111        int netId = config.networkId;
1112        boolean newNetwork = false;
1113        // networkId of INVALID_NETWORK_ID means we want to create a new network
1114        if (netId == INVALID_NETWORK_ID) {
1115            Integer savedNetId = mNetworkIds.get(configKey(config));
1116            if (savedNetId != null) {
1117                netId = savedNetId;
1118            } else {
1119                newNetwork = true;
1120                netId = mWifiNative.addNetwork();
1121                if (netId < 0) {
1122                    loge("Failed to add a network!");
1123                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
1124                }
1125            }
1126        }
1127
1128        boolean updateFailed = true;
1129
1130        setVariables: {
1131
1132            if (config.SSID != null &&
1133                    !mWifiNative.setNetworkVariable(
1134                        netId,
1135                        WifiConfiguration.ssidVarName,
1136                        config.SSID)) {
1137                loge("failed to set SSID: "+config.SSID);
1138                break setVariables;
1139            }
1140
1141            if (config.BSSID != null &&
1142                    !mWifiNative.setNetworkVariable(
1143                        netId,
1144                        WifiConfiguration.bssidVarName,
1145                        config.BSSID)) {
1146                loge("failed to set BSSID: "+config.BSSID);
1147                break setVariables;
1148            }
1149
1150            String allowedKeyManagementString =
1151                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
1152            if (config.allowedKeyManagement.cardinality() != 0 &&
1153                    !mWifiNative.setNetworkVariable(
1154                        netId,
1155                        WifiConfiguration.KeyMgmt.varName,
1156                        allowedKeyManagementString)) {
1157                loge("failed to set key_mgmt: "+
1158                        allowedKeyManagementString);
1159                break setVariables;
1160            }
1161
1162            String allowedProtocolsString =
1163                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
1164            if (config.allowedProtocols.cardinality() != 0 &&
1165                    !mWifiNative.setNetworkVariable(
1166                        netId,
1167                        WifiConfiguration.Protocol.varName,
1168                        allowedProtocolsString)) {
1169                loge("failed to set proto: "+
1170                        allowedProtocolsString);
1171                break setVariables;
1172            }
1173
1174            String allowedAuthAlgorithmsString =
1175                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1176            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
1177                    !mWifiNative.setNetworkVariable(
1178                        netId,
1179                        WifiConfiguration.AuthAlgorithm.varName,
1180                        allowedAuthAlgorithmsString)) {
1181                loge("failed to set auth_alg: "+
1182                        allowedAuthAlgorithmsString);
1183                break setVariables;
1184            }
1185
1186            String allowedPairwiseCiphersString =
1187                    makeString(config.allowedPairwiseCiphers,
1188                    WifiConfiguration.PairwiseCipher.strings);
1189            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
1190                    !mWifiNative.setNetworkVariable(
1191                        netId,
1192                        WifiConfiguration.PairwiseCipher.varName,
1193                        allowedPairwiseCiphersString)) {
1194                loge("failed to set pairwise: "+
1195                        allowedPairwiseCiphersString);
1196                break setVariables;
1197            }
1198
1199            String allowedGroupCiphersString =
1200                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1201            if (config.allowedGroupCiphers.cardinality() != 0 &&
1202                    !mWifiNative.setNetworkVariable(
1203                        netId,
1204                        WifiConfiguration.GroupCipher.varName,
1205                        allowedGroupCiphersString)) {
1206                loge("failed to set group: "+
1207                        allowedGroupCiphersString);
1208                break setVariables;
1209            }
1210
1211            // Prevent client screw-up by passing in a WifiConfiguration we gave it
1212            // by preventing "*" as a key.
1213            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
1214                    !mWifiNative.setNetworkVariable(
1215                        netId,
1216                        WifiConfiguration.pskVarName,
1217                        config.preSharedKey)) {
1218                loge("failed to set psk");
1219                break setVariables;
1220            }
1221
1222            boolean hasSetKey = false;
1223            if (config.wepKeys != null) {
1224                for (int i = 0; i < config.wepKeys.length; i++) {
1225                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
1226                    // by preventing "*" as a key.
1227                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
1228                        if (!mWifiNative.setNetworkVariable(
1229                                    netId,
1230                                    WifiConfiguration.wepKeyVarNames[i],
1231                                    config.wepKeys[i])) {
1232                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
1233                            break setVariables;
1234                        }
1235                        hasSetKey = true;
1236                    }
1237                }
1238            }
1239
1240            if (hasSetKey) {
1241                if (!mWifiNative.setNetworkVariable(
1242                            netId,
1243                            WifiConfiguration.wepTxKeyIdxVarName,
1244                            Integer.toString(config.wepTxKeyIndex))) {
1245                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
1246                    break setVariables;
1247                }
1248            }
1249
1250            if (!mWifiNative.setNetworkVariable(
1251                        netId,
1252                        WifiConfiguration.priorityVarName,
1253                        Integer.toString(config.priority))) {
1254                loge(config.SSID + ": failed to set priority: "
1255                        +config.priority);
1256                break setVariables;
1257            }
1258
1259            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
1260                        netId,
1261                        WifiConfiguration.hiddenSSIDVarName,
1262                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
1263                loge(config.SSID + ": failed to set hiddenSSID: "+
1264                        config.hiddenSSID);
1265                break setVariables;
1266            }
1267
1268            if (config.enterpriseConfig != null &&
1269                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1270
1271                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
1272
1273                if (needsKeyStore(enterpriseConfig)) {
1274                    /**
1275                     * Keyguard settings may eventually be controlled by device policy.
1276                     * We check here if keystore is unlocked before installing
1277                     * credentials.
1278                     * TODO: Do we need a dialog here ?
1279                     */
1280                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
1281                        loge(config.SSID + ": key store is locked");
1282                        break setVariables;
1283                    }
1284
1285                    try {
1286                        /* config passed may include only fields being updated.
1287                         * In order to generate the key id, fetch uninitialized
1288                         * fields from the currently tracked configuration
1289                         */
1290                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1291                        String keyId = config.getKeyIdForCredentials(currentConfig);
1292
1293                        if (!installKeys(enterpriseConfig, keyId)) {
1294                            loge(config.SSID + ": failed to install keys");
1295                            break setVariables;
1296                        }
1297                    } catch (IllegalStateException e) {
1298                        loge(config.SSID + " invalid config for key installation");
1299                        break setVariables;
1300                    }
1301                }
1302
1303                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
1304                for (String key : enterpriseFields.keySet()) {
1305                        String value = enterpriseFields.get(key);
1306                        if (!mWifiNative.setNetworkVariable(
1307                                    netId,
1308                                    key,
1309                                    value)) {
1310                            removeKeys(enterpriseConfig);
1311                            loge(config.SSID + ": failed to set " + key +
1312                                    ": " + value);
1313                            break setVariables;
1314                        }
1315                }
1316            }
1317            updateFailed = false;
1318        } //end of setVariables
1319
1320        if (updateFailed) {
1321            if (newNetwork) {
1322                mWifiNative.removeNetwork(netId);
1323                loge("Failed to set a network variable, removed network: " + netId);
1324            }
1325            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1326        }
1327
1328        /* An update of the network variables requires reading them
1329         * back from the supplicant to update mConfiguredNetworks.
1330         * This is because some of the variables (SSID, wep keys &
1331         * passphrases) reflect different values when read back than
1332         * when written. For example, wep key is stored as * irrespective
1333         * of the value sent to the supplicant
1334         */
1335        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1336        if (currentConfig == null) {
1337            currentConfig = new WifiConfiguration();
1338            currentConfig.ipAssignment = IpAssignment.DHCP;
1339            currentConfig.proxySettings = ProxySettings.NONE;
1340            currentConfig.networkId = netId;
1341        }
1342
1343        readNetworkVariables(currentConfig);
1344
1345        mConfiguredNetworks.put(netId, currentConfig);
1346        mNetworkIds.put(configKey(currentConfig), netId);
1347
1348        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
1349        result.setIsNewNetwork(newNetwork);
1350        result.setNetworkId(netId);
1351        return result;
1352    }
1353
1354    /* Compare current and new configuration and write to file on change */
1355    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
1356            WifiConfiguration currentConfig,
1357            WifiConfiguration newConfig) {
1358        boolean ipChanged = false;
1359        boolean proxyChanged = false;
1360        LinkProperties linkProperties = null;
1361
1362        switch (newConfig.ipAssignment) {
1363            case STATIC:
1364                Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties
1365                        .getLinkAddresses();
1366                Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties
1367                        .getLinkAddresses();
1368                Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
1369                Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
1370                Collection<RouteInfo> currentRoutes = currentConfig.linkProperties.getRoutes();
1371                Collection<RouteInfo> newRoutes = newConfig.linkProperties.getRoutes();
1372
1373                boolean linkAddressesDiffer =
1374                        (currentLinkAddresses.size() != newLinkAddresses.size()) ||
1375                        !currentLinkAddresses.containsAll(newLinkAddresses);
1376                boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
1377                        !currentDnses.containsAll(newDnses);
1378                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
1379                        !currentRoutes.containsAll(newRoutes);
1380
1381                if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
1382                        linkAddressesDiffer ||
1383                        dnsesDiffer ||
1384                        routesDiffer) {
1385                    ipChanged = true;
1386                }
1387                break;
1388            case DHCP:
1389                if (currentConfig.ipAssignment != newConfig.ipAssignment) {
1390                    ipChanged = true;
1391                }
1392                break;
1393            case UNASSIGNED:
1394                /* Ignore */
1395                break;
1396            default:
1397                loge("Ignore invalid ip assignment during write");
1398                break;
1399        }
1400
1401        switch (newConfig.proxySettings) {
1402            case STATIC:
1403            case PAC:
1404                ProxyInfo newHttpProxy = newConfig.linkProperties.getHttpProxy();
1405                ProxyInfo currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
1406
1407                if (newHttpProxy != null) {
1408                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
1409                } else {
1410                    proxyChanged = (currentHttpProxy != null);
1411                }
1412                break;
1413            case NONE:
1414                if (currentConfig.proxySettings != newConfig.proxySettings) {
1415                    proxyChanged = true;
1416                }
1417                break;
1418            case UNASSIGNED:
1419                /* Ignore */
1420                break;
1421            default:
1422                loge("Ignore invalid proxy configuration during write");
1423                break;
1424        }
1425
1426        if (!ipChanged) {
1427            linkProperties = copyIpSettingsFromConfig(currentConfig);
1428        } else {
1429            currentConfig.ipAssignment = newConfig.ipAssignment;
1430            linkProperties = copyIpSettingsFromConfig(newConfig);
1431            log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
1432                    linkProperties.toString());
1433        }
1434
1435
1436        if (!proxyChanged) {
1437            linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy());
1438        } else {
1439            currentConfig.proxySettings = newConfig.proxySettings;
1440            linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy());
1441            log("proxy changed SSID = " + currentConfig.SSID);
1442            if (linkProperties.getHttpProxy() != null) {
1443                log(" proxyProperties: " + linkProperties.getHttpProxy().toString());
1444            }
1445        }
1446
1447        if (ipChanged || proxyChanged) {
1448            currentConfig.linkProperties = linkProperties;
1449            writeIpAndProxyConfigurations();
1450            sendConfiguredNetworksChangedBroadcast(currentConfig,
1451                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1452        }
1453        return new NetworkUpdateResult(ipChanged, proxyChanged);
1454    }
1455
1456    private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
1457        LinkProperties linkProperties = new LinkProperties();
1458        linkProperties.setInterfaceName(config.linkProperties.getInterfaceName());
1459        for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
1460            linkProperties.addLinkAddress(linkAddr);
1461        }
1462        for (RouteInfo route : config.linkProperties.getRoutes()) {
1463            linkProperties.addRoute(route);
1464        }
1465        for (InetAddress dns : config.linkProperties.getDnses()) {
1466            linkProperties.addDns(dns);
1467        }
1468        return linkProperties;
1469    }
1470
1471    /**
1472     * Read the variables from the supplicant daemon that are needed to
1473     * fill in the WifiConfiguration object.
1474     *
1475     * @param config the {@link WifiConfiguration} object to be filled in.
1476     */
1477    private void readNetworkVariables(WifiConfiguration config) {
1478
1479        int netId = config.networkId;
1480        if (netId < 0)
1481            return;
1482
1483        /*
1484         * TODO: maybe should have a native method that takes an array of
1485         * variable names and returns an array of values. But we'd still
1486         * be doing a round trip to the supplicant daemon for each variable.
1487         */
1488        String value;
1489
1490        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
1491        if (!TextUtils.isEmpty(value)) {
1492            if (value.charAt(0) != '"') {
1493                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
1494                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
1495                //supplicant string
1496            } else {
1497                config.SSID = value;
1498            }
1499        } else {
1500            config.SSID = null;
1501        }
1502
1503        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
1504        if (!TextUtils.isEmpty(value)) {
1505            config.BSSID = value;
1506        } else {
1507            config.BSSID = null;
1508        }
1509
1510        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
1511        config.priority = -1;
1512        if (!TextUtils.isEmpty(value)) {
1513            try {
1514                config.priority = Integer.parseInt(value);
1515            } catch (NumberFormatException ignore) {
1516            }
1517        }
1518
1519        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
1520        config.hiddenSSID = false;
1521        if (!TextUtils.isEmpty(value)) {
1522            try {
1523                config.hiddenSSID = Integer.parseInt(value) != 0;
1524            } catch (NumberFormatException ignore) {
1525            }
1526        }
1527
1528        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
1529        config.wepTxKeyIndex = -1;
1530        if (!TextUtils.isEmpty(value)) {
1531            try {
1532                config.wepTxKeyIndex = Integer.parseInt(value);
1533            } catch (NumberFormatException ignore) {
1534            }
1535        }
1536
1537        for (int i = 0; i < 4; i++) {
1538            value = mWifiNative.getNetworkVariable(netId,
1539                    WifiConfiguration.wepKeyVarNames[i]);
1540            if (!TextUtils.isEmpty(value)) {
1541                config.wepKeys[i] = value;
1542            } else {
1543                config.wepKeys[i] = null;
1544            }
1545        }
1546
1547        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
1548        if (!TextUtils.isEmpty(value)) {
1549            config.preSharedKey = value;
1550        } else {
1551            config.preSharedKey = null;
1552        }
1553
1554        value = mWifiNative.getNetworkVariable(config.networkId,
1555                WifiConfiguration.Protocol.varName);
1556        if (!TextUtils.isEmpty(value)) {
1557            String vals[] = value.split(" ");
1558            for (String val : vals) {
1559                int index =
1560                    lookupString(val, WifiConfiguration.Protocol.strings);
1561                if (0 <= index) {
1562                    config.allowedProtocols.set(index);
1563                }
1564            }
1565        }
1566
1567        value = mWifiNative.getNetworkVariable(config.networkId,
1568                WifiConfiguration.KeyMgmt.varName);
1569        if (!TextUtils.isEmpty(value)) {
1570            String vals[] = value.split(" ");
1571            for (String val : vals) {
1572                int index =
1573                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
1574                if (0 <= index) {
1575                    config.allowedKeyManagement.set(index);
1576                }
1577            }
1578        }
1579
1580        value = mWifiNative.getNetworkVariable(config.networkId,
1581                WifiConfiguration.AuthAlgorithm.varName);
1582        if (!TextUtils.isEmpty(value)) {
1583            String vals[] = value.split(" ");
1584            for (String val : vals) {
1585                int index =
1586                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
1587                if (0 <= index) {
1588                    config.allowedAuthAlgorithms.set(index);
1589                }
1590            }
1591        }
1592
1593        value = mWifiNative.getNetworkVariable(config.networkId,
1594                WifiConfiguration.PairwiseCipher.varName);
1595        if (!TextUtils.isEmpty(value)) {
1596            String vals[] = value.split(" ");
1597            for (String val : vals) {
1598                int index =
1599                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
1600                if (0 <= index) {
1601                    config.allowedPairwiseCiphers.set(index);
1602                }
1603            }
1604        }
1605
1606        value = mWifiNative.getNetworkVariable(config.networkId,
1607                WifiConfiguration.GroupCipher.varName);
1608        if (!TextUtils.isEmpty(value)) {
1609            String vals[] = value.split(" ");
1610            for (String val : vals) {
1611                int index =
1612                    lookupString(val, WifiConfiguration.GroupCipher.strings);
1613                if (0 <= index) {
1614                    config.allowedGroupCiphers.set(index);
1615                }
1616            }
1617        }
1618
1619        if (config.enterpriseConfig == null) {
1620            config.enterpriseConfig = new WifiEnterpriseConfig();
1621        }
1622        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
1623        for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
1624            value = mWifiNative.getNetworkVariable(netId, key);
1625            if (!TextUtils.isEmpty(value)) {
1626                enterpriseFields.put(key, removeDoubleQuotes(value));
1627            } else {
1628                enterpriseFields.put(key, EMPTY_VALUE);
1629            }
1630        }
1631
1632        if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
1633            saveConfig();
1634        }
1635
1636        migrateCerts(config.enterpriseConfig);
1637        // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
1638    }
1639
1640    private static String removeDoubleQuotes(String string) {
1641        int length = string.length();
1642        if ((length > 1) && (string.charAt(0) == '"')
1643                && (string.charAt(length - 1) == '"')) {
1644            return string.substring(1, length - 1);
1645        }
1646        return string;
1647    }
1648
1649    private static String convertToQuotedString(String string) {
1650        return "\"" + string + "\"";
1651    }
1652
1653    private static String makeString(BitSet set, String[] strings) {
1654        StringBuffer buf = new StringBuffer();
1655        int nextSetBit = -1;
1656
1657        /* Make sure all set bits are in [0, strings.length) to avoid
1658         * going out of bounds on strings.  (Shouldn't happen, but...) */
1659        set = set.get(0, strings.length);
1660
1661        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1662            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
1663        }
1664
1665        // remove trailing space
1666        if (set.cardinality() > 0) {
1667            buf.setLength(buf.length() - 1);
1668        }
1669
1670        return buf.toString();
1671    }
1672
1673    private int lookupString(String string, String[] strings) {
1674        int size = strings.length;
1675
1676        string = string.replace('-', '_');
1677
1678        for (int i = 0; i < size; i++)
1679            if (string.equals(strings[i]))
1680                return i;
1681
1682        // if we ever get here, we should probably add the
1683        // value to WifiConfiguration to reflect that it's
1684        // supported by the WPA supplicant
1685        loge("Failed to look-up a string: " + string);
1686
1687        return -1;
1688    }
1689
1690    /* Returns a unique for a given configuration */
1691    private static int configKey(WifiConfiguration config) {
1692        String key;
1693
1694        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1695            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
1696        } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1697                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1698            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
1699        } else if (config.wepKeys[0] != null) {
1700            key = config.SSID + "WEP";
1701        } else {
1702            key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
1703        }
1704
1705        return key.hashCode();
1706    }
1707
1708    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1709        pw.println("WifiConfigStore");
1710        pw.println("mLastPriority " + mLastPriority);
1711        pw.println("Configured networks");
1712        for (WifiConfiguration conf : getConfiguredNetworks()) {
1713            pw.println(conf);
1714        }
1715        pw.println();
1716
1717        if (mLocalLog != null) {
1718            pw.println("WifiConfigStore - Log Begin ----");
1719            mLocalLog.dump(fd, pw, args);
1720            pw.println("WifiConfigStore - Log End ----");
1721        }
1722    }
1723
1724    public String getConfigFile() {
1725        return ipConfigFile;
1726    }
1727
1728    private void loge(String s) {
1729        Log.e(TAG, s);
1730    }
1731
1732    private void log(String s) {
1733        Log.d(TAG, s);
1734    }
1735
1736    private void localLog(String s) {
1737        if (mLocalLog != null) {
1738            mLocalLog.log(s);
1739        }
1740    }
1741
1742    private void localLog(String s, int netId) {
1743        if (mLocalLog == null) {
1744            return;
1745        }
1746
1747        WifiConfiguration config;
1748        synchronized(mConfiguredNetworks) {
1749            config = mConfiguredNetworks.get(netId);
1750        }
1751
1752        if (config != null) {
1753            mLocalLog.log(s + " " + config.getPrintableSsid());
1754        } else {
1755            mLocalLog.log(s + " " + netId);
1756        }
1757    }
1758
1759    // Certificate and private key management for EnterpriseConfig
1760    static boolean needsKeyStore(WifiEnterpriseConfig config) {
1761        // Has no keys to be installed
1762        if (config.getClientCertificate() == null && config.getCaCertificate() == null)
1763            return false;
1764        return true;
1765    }
1766
1767    static boolean isHardwareBackedKey(PrivateKey key) {
1768        return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
1769    }
1770
1771    static boolean hasHardwareBackedKey(Certificate certificate) {
1772        return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
1773    }
1774
1775    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
1776        String client = config.getClientCertificateAlias();
1777        if (!TextUtils.isEmpty(client)) {
1778            // a valid client certificate is configured
1779
1780            // BUGBUG: keyStore.get() never returns certBytes; because it is not
1781            // taking WIFI_UID as a parameter. It always looks for certificate
1782            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
1783            // all certificates need software keystore until we get the get() API
1784            // fixed.
1785
1786            return true;
1787        }
1788
1789        /*
1790        try {
1791
1792            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
1793                    .USER_CERTIFICATE + client);
1794
1795            CertificateFactory factory = CertificateFactory.getInstance("X.509");
1796            if (factory == null) {
1797                Slog.e(TAG, "Error getting certificate factory");
1798                return;
1799            }
1800
1801            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
1802            if (certBytes != null) {
1803                Certificate cert = (X509Certificate) factory.generateCertificate(
1804                        new ByteArrayInputStream(certBytes));
1805
1806                if (cert != null) {
1807                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
1808
1809                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
1810                            .USER_CERTIFICATE + client);
1811                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
1812                            "does not need" ) + " software key store");
1813                } else {
1814                    Slog.d(TAG, "could not generate certificate");
1815                }
1816            } else {
1817                Slog.e(TAG, "Could not load client certificate " + Credentials
1818                        .USER_CERTIFICATE + client);
1819                mNeedsSoftwareKeystore = true;
1820            }
1821
1822        } catch(CertificateException e) {
1823            Slog.e(TAG, "Could not read certificates");
1824            mCaCert = null;
1825            mClientCertificate = null;
1826        }
1827        */
1828
1829        return false;
1830    }
1831
1832    boolean installKeys(WifiEnterpriseConfig config, String name) {
1833        boolean ret = true;
1834        String privKeyName = Credentials.USER_PRIVATE_KEY + name;
1835        String userCertName = Credentials.USER_CERTIFICATE + name;
1836        String caCertName = Credentials.CA_CERTIFICATE + name;
1837        if (config.getClientCertificate() != null) {
1838            byte[] privKeyData = config.getClientPrivateKey().getEncoded();
1839            if (isHardwareBackedKey(config.getClientPrivateKey())) {
1840                // Hardware backed key store is secure enough to store keys un-encrypted, this
1841                // removes the need for user to punch a PIN to get access to these keys
1842                if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
1843                ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
1844                        KeyStore.FLAG_NONE);
1845            } else {
1846                // Software backed key store is NOT secure enough to store keys un-encrypted.
1847                // Save keys encrypted so they are protected with user's PIN. User will
1848                // have to unlock phone before being able to use these keys and connect to
1849                // networks.
1850                if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
1851                ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
1852                        KeyStore.FLAG_ENCRYPTED);
1853            }
1854            if (ret == false) {
1855                return ret;
1856            }
1857
1858            ret = putCertInKeyStore(userCertName, config.getClientCertificate());
1859            if (ret == false) {
1860                // Remove private key installed
1861                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
1862                return ret;
1863            }
1864        }
1865
1866        if (config.getCaCertificate() != null) {
1867            ret = putCertInKeyStore(caCertName, config.getCaCertificate());
1868            if (ret == false) {
1869                if (config.getClientCertificate() != null) {
1870                    // Remove client key+cert
1871                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
1872                    mKeyStore.delete(userCertName, Process.WIFI_UID);
1873                }
1874                return ret;
1875            }
1876        }
1877
1878        // Set alias names
1879        if (config.getClientCertificate() != null) {
1880            config.setClientCertificateAlias(name);
1881            config.resetClientKeyEntry();
1882        }
1883
1884        if (config.getCaCertificate() != null) {
1885            config.setCaCertificateAlias(name);
1886            config.resetCaCertificate();
1887        }
1888
1889        return ret;
1890    }
1891
1892    private boolean putCertInKeyStore(String name, Certificate cert) {
1893        try {
1894            byte[] certData = Credentials.convertToPem(cert);
1895            if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
1896            return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
1897
1898        } catch (IOException e1) {
1899            return false;
1900        } catch (CertificateException e2) {
1901            return false;
1902        }
1903    }
1904
1905    void removeKeys(WifiEnterpriseConfig config) {
1906        String client = config.getClientCertificateAlias();
1907        // a valid client certificate is configured
1908        if (!TextUtils.isEmpty(client)) {
1909            if (DBG) Log.d(TAG, "removing client private key and user cert");
1910            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
1911            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
1912        }
1913
1914        String ca = config.getCaCertificateAlias();
1915        // a valid ca certificate is configured
1916        if (!TextUtils.isEmpty(ca)) {
1917            if (DBG) Log.d(TAG, "removing CA cert");
1918            mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
1919        }
1920    }
1921
1922
1923    /** Migrates the old style TLS config to the new config style. This should only be used
1924     * when restoring an old wpa_supplicant.conf or upgrading from a previous
1925     * platform version.
1926     * @return true if the config was updated
1927     * @hide
1928     */
1929    boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
1930        String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
1931        /*
1932         * If the old configuration value is not present, then there is nothing
1933         * to do.
1934         */
1935        if (TextUtils.isEmpty(oldPrivateKey)) {
1936            return false;
1937        } else {
1938            // Also ignore it if it's empty quotes.
1939            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
1940            if (TextUtils.isEmpty(oldPrivateKey)) {
1941                return false;
1942            }
1943        }
1944
1945        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
1946        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
1947                WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
1948
1949        /*
1950        * The old key started with the keystore:// URI prefix, but we don't
1951        * need that anymore. Trim it off if it exists.
1952        */
1953        final String keyName;
1954        if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
1955            keyName = new String(
1956                    oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
1957        } else {
1958            keyName = oldPrivateKey;
1959        }
1960        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
1961
1962        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
1963                config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
1964
1965        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
1966                config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
1967
1968        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
1969                config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
1970
1971        // Remove old private_key string so we don't run this again.
1972        mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
1973
1974        return true;
1975    }
1976
1977    /** Migrate certs from global pool to wifi UID if not already done */
1978    void migrateCerts(WifiEnterpriseConfig config) {
1979        String client = config.getClientCertificateAlias();
1980        // a valid client certificate is configured
1981        if (!TextUtils.isEmpty(client)) {
1982            if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
1983                mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
1984                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
1985                mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
1986                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
1987            }
1988        }
1989
1990        String ca = config.getCaCertificateAlias();
1991        // a valid ca certificate is configured
1992        if (!TextUtils.isEmpty(ca)) {
1993            if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
1994                mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
1995                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
1996            }
1997        }
1998    }
1999
2000}
2001
2002