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