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