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