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