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