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