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