WifiConfigStore.java revision a2903b7c6fc2419d48fd9976fa24852370653c4a
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;
40import android.net.wifi.WifiInfo;
41
42import android.os.Environment;
43import android.os.FileObserver;
44import android.os.Process;
45import android.os.SystemClock;
46import android.os.UserHandle;
47import android.security.Credentials;
48import android.security.KeyChain;
49import android.security.KeyStore;
50import android.text.TextUtils;
51import android.util.LocalLog;
52import android.util.Log;
53import android.util.SparseArray;
54
55import com.android.server.net.DelayedDiskWrite;
56import com.android.server.net.IpConfigStore;
57
58import java.io.BufferedReader;
59import java.io.BufferedInputStream;
60import java.io.DataInputStream;
61import java.io.DataOutputStream;
62import java.io.EOFException;
63import java.io.File;
64import java.io.FileDescriptor;
65import java.io.FileInputStream;
66import java.io.FileNotFoundException;
67import java.io.FileReader;
68import java.io.IOException;
69import java.io.PrintWriter;
70import java.math.BigInteger;
71import java.net.InetAddress;
72import java.nio.charset.Charset;
73import java.security.PrivateKey;
74import java.security.cert.Certificate;
75import java.security.cert.CertificateException;
76import java.text.SimpleDateFormat;
77import java.text.DateFormat;
78import java.util.*;
79
80/**
81 * This class provides the API to manage configured
82 * wifi networks. The API is not thread safe is being
83 * used only from WifiStateMachine.
84 *
85 * It deals with the following
86 * - Add/update/remove a WifiConfiguration
87 *   The configuration contains two types of information.
88 *     = IP and proxy configuration that is handled by WifiConfigStore and
89 *       is saved to disk on any change.
90 *
91 *       The format of configuration file is as follows:
92 *       <version>
93 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
94 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
95 *       ..
96 *
97 *       (key, value) pairs for a given network are grouped together and can
98 *       be in any order. A EOS at the end of a set of (key, value) pairs
99 *       indicates that the next set of (key, value) pairs are for a new
100 *       network. A network is identified by a unique ID_KEY. If there is no
101 *       ID_KEY in the (key, value) pairs, the data is discarded.
102 *
103 *       An invalid version on read would result in discarding the contents of
104 *       the file. On the next write, the latest version is written to file.
105 *
106 *       Any failures during read or write to the configuration file are ignored
107 *       without reporting to the user since the likelihood of these errors are
108 *       low and the impact on connectivity is low.
109 *
110 *     = SSID & security details that is pushed to the supplicant.
111 *       supplicant saves these details to the disk on calling
112 *       saveConfigCommand().
113 *
114 *       We have two kinds of APIs exposed:
115 *        > public API calls that provide fine grained control
116 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
117 *          removeNetwork(). For these calls, the config is not persisted
118 *          to the disk. (TODO: deprecate these calls in WifiManager)
119 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
120 *          These calls persist the supplicant config to disk.
121 *
122 * - Maintain a list of configured networks for quick access
123 *
124 */
125public class WifiConfigStore extends IpConfigStore {
126
127    private Context mContext;
128    private static final String TAG = "WifiConfigStore";
129    private static final boolean DBG = true;
130    private static boolean VDBG = false;
131
132    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
133
134    /* configured networks with network id as the key */
135    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
136            new HashMap<Integer, WifiConfiguration>();
137
138    /* A network id is a unique identifier for a network configured in the
139     * supplicant. Network ids are generated when the supplicant reads
140     * the configuration file at start and can thus change for networks.
141     * We store the IP configuration for networks along with a unique id
142     * that is generated from SSID and security type of the network. A mapping
143     * from the generated unique id to network id of the network is needed to
144     * map supplicant config to IP configuration. */
145    private HashMap<Integer, Integer> mNetworkIds =
146            new HashMap<Integer, Integer>();
147
148    /* Tracks the highest priority of configured networks */
149    private int mLastPriority = -1;
150
151    private static final String ipConfigFile = Environment.getDataDirectory() +
152            "/misc/wifi/ipconfig.txt";
153
154    private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
155            "/misc/wifi/networkHistory.txt";
156
157    /* Network History Keys */
158    private static final String SSID_KEY = "SSID:  ";
159    private static final String CONFIG_KEY = "CONFIG:  ";
160    private static final String CHOICE_KEY = "CHOICE:  ";
161    private static final String LINK_KEY = "LINK:  ";
162    private static final String BSSID_KEY = "BSSID:  ";
163    private static final String BSSID_KEY_END = "/BSSID:  ";
164    private static final String RSSI_KEY = "RSSI:  ";
165    private static final String FREQ_KEY = "FREQ:  ";
166    private static final String DATE_KEY = "DATE:  ";
167    private static final String MILLI_KEY = "MILLI:  ";
168    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI:  ";
169    private static final String NETWORK_ID_KEY = "ID:  ";
170    private static final String PRIORITY_KEY = "PRIORITY:  ";
171    private static final String DEFAULT_GW_KEY = "DEFAULT_GW:  ";
172    private static final String AUTH_KEY = "AUTH:  ";
173    private static final String SEPARATOR_KEY = "\n";
174    private static final String STATUS_KEY = "AUTO_JOIN_STATUS:  ";
175    private static final String SELF_ADDED_KEY = "SELF_ADDED:  ";
176    private static final String FAILURE_KEY = "FAILURE:  ";
177    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD:  ";
178    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION:  ";
179    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY:  ";
180    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY:  ";
181    private static final String UPDATE_UID_KEY = "UPDATE_UID:  ";
182    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS:  ";
183    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON:  ";
184    /* Enterprise configuration keys */
185    /**
186     * In old configurations, the "private_key" field was used. However, newer
187     * configurations use the key_id field with the engine_id set to "keystore".
188     * If this field is found in the configuration, the migration code is
189     * triggered.
190     */
191    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
192
193    /** This represents an empty value of an enterprise field.
194     * NULL is used at wpa_supplicant to indicate an empty value
195     */
196    static final String EMPTY_VALUE = "NULL";
197
198    /** Internal use only */
199    private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
200            WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
201            WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
202            WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
203            WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
204            WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
205            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
206
207    private final LocalLog mLocalLog;
208    private final WpaConfigFileObserver mFileObserver;
209
210    private WifiNative mWifiNative;
211    private final KeyStore mKeyStore = KeyStore.getInstance();
212
213    /*
214     * lastSelectedConfiguration is used to remember which network was selected last by the user.
215     * The connection to this network may not be successful, as well
216     * the selection (i.e. network priority) might not be persisted.
217     * WiFi state machine is the only object that sets this variable.
218     */
219    private String lastSelectedConfiguration = null;
220
221    WifiConfigStore(Context c, WifiNative wn) {
222        mContext = c;
223        mWifiNative = wn;
224
225        if (VDBG) {
226            mLocalLog = mWifiNative.getLocalLog();
227            mFileObserver = new WpaConfigFileObserver();
228            mFileObserver.startWatching();
229        } else {
230            mLocalLog = null;
231            mFileObserver = null;
232        }
233    }
234
235    void enableVerboseLogging(int verbose) {
236        if (verbose > 0) {
237            VDBG = true;
238        } else {
239            VDBG = false;
240        }
241    }
242
243    class WpaConfigFileObserver extends FileObserver {
244
245        public WpaConfigFileObserver() {
246            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
247        }
248
249        @Override
250        public void onEvent(int event, String path) {
251            if (event == CLOSE_WRITE) {
252                File file = new File(SUPPLICANT_CONFIG_FILE);
253                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
254            }
255        }
256    }
257
258
259    /**
260     * Fetch the list of configured networks
261     * and enable all stored networks in supplicant.
262     */
263    void loadAndEnableAllNetworks() {
264        if (DBG) log("Loading config and enabling all networks ");
265        loadConfiguredNetworks();
266        enableAllNetworks();
267    }
268
269    /**
270     * Fetch the list of currently configured networks
271     * @return List of networks
272     */
273    List<WifiConfiguration> getConfiguredNetworks() {
274        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
275        for(WifiConfiguration config : mConfiguredNetworks.values()) {
276            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
277                //do not enumerate and return this configuration to any one,
278                //for instance WiFi Picker.
279                //instead treat it as unknown. the configuration can still be retrieved
280                //directly by the key or networkId
281                continue;
282            }
283            networks.add(new WifiConfiguration(config));
284        }
285        return networks;
286    }
287
288    /**
289     * Fetch the list of currently configured networks that were recently seen
290     *
291     * @return List of networks
292     */
293    List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
294        List<WifiConfiguration> networks = null;
295
296        for (WifiConfiguration config : mConfiguredNetworks.values()) {
297            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
298                //do not enumerate and return this configuration to any one,
299                //instead treat it as unknown. the configuration can still be retrieved
300                //directly by the key or networkId
301                continue;
302            }
303
304            // calculate the RSSI for scan results that are more recent than milli
305            config.setVisibility(milli);
306
307            if (config.visibility == null) {
308                continue;
309            }
310            if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
311                    config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
312                continue;
313            }
314            if (networks == null)
315                networks = new ArrayList<WifiConfiguration>();
316            if (copy) {
317                networks.add(new WifiConfiguration(config));
318            } else {
319                networks.add(config);
320            }
321        }
322        return networks;
323    }
324
325    /**
326     * get the Wificonfiguration for this netId
327     *
328     * @return Wificonfiguration
329     */
330    WifiConfiguration getWifiConfiguration(int netId) {
331        if (mConfiguredNetworks == null)
332            return null;
333        return mConfiguredNetworks.get(netId);
334    }
335
336    /**
337     * get the Wificonfiguration for this key
338     *
339     * @return Wificonfiguration
340     */
341    WifiConfiguration getWifiConfiguration(String key) {
342        if (key == null)
343            return null;
344        int hash = key.hashCode();
345        if (mNetworkIds == null)
346            return null;
347        Integer n = mNetworkIds.get(hash);
348        if (n == null)
349            return null;
350        int netId = n.intValue();
351        return getWifiConfiguration(netId);
352    }
353
354    /**
355     * enable all networks and save config. This will be a no-op if the list
356     * of configured networks indicates all networks as being enabled
357     */
358    void enableAllNetworks() {
359        boolean networkEnabledStateChanged = false;
360        for(WifiConfiguration config : mConfiguredNetworks.values()) {
361            if(config != null && config.status == Status.DISABLED
362                    && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) {
363                if(mWifiNative.enableNetwork(config.networkId, false)) {
364                    networkEnabledStateChanged = true;
365                    config.status = Status.ENABLED;
366                } else {
367                    loge("Enable network failed on " + config.networkId);
368                }
369            }
370        }
371
372        if (networkEnabledStateChanged) {
373            mWifiNative.saveConfig();
374            sendConfiguredNetworksChangedBroadcast();
375        }
376    }
377
378
379    /**
380     * Selects the specified network for connection. This involves
381     * updating the priority of all the networks and enabling the given
382     * network while disabling others.
383     *
384     * Selecting a network will leave the other networks disabled and
385     * a call to enableAllNetworks() needs to be issued upon a connection
386     * or a failure event from supplicant
387     *
388     * @param netId network to select for connection
389     * @return false if the network id is invalid
390     */
391    boolean selectNetwork(int netId) {
392        if (VDBG) localLog("selectNetwork", netId);
393        if (netId == INVALID_NETWORK_ID) return false;
394
395        // Reset the priority of each network at start or if it goes too high.
396        if (mLastPriority == -1 || mLastPriority > 1000000) {
397            for(WifiConfiguration config : mConfiguredNetworks.values()) {
398                if (config.networkId != INVALID_NETWORK_ID) {
399                    config.priority = 0;
400                    addOrUpdateNetworkNative(config);
401                }
402            }
403            mLastPriority = 0;
404        }
405
406        // Set to the highest priority and save the configuration.
407        WifiConfiguration config = new WifiConfiguration();
408        config.networkId = netId;
409        config.priority = ++mLastPriority;
410
411        addOrUpdateNetworkNative(config);
412        mWifiNative.saveConfig();
413
414        /* Enable the given network while disabling all other networks */
415        enableNetworkWithoutBroadcast(netId, true);
416
417       /* Avoid saving the config & sending a broadcast to prevent settings
418        * from displaying a disabled list of networks */
419        return true;
420    }
421
422    /**
423     * Add/update the specified configuration and save config
424     *
425     * @param config WifiConfiguration to be saved
426     * @return network update result
427     */
428    NetworkUpdateResult saveNetwork(WifiConfiguration config) {
429        WifiConfiguration conf;
430
431        // A new network cannot have null SSID
432        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
433                config.SSID == null)) {
434            return new NetworkUpdateResult(INVALID_NETWORK_ID);
435        }
436        if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
437        if (VDBG) {
438            loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
439                    + " SSID=" + config.SSID
440                    + " Uid=" + Integer.toString(config.creatorUid)
441                    + "/" + Integer.toString(config.lastUpdateUid));
442        }
443        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
444        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
445        int netId = result.getNetworkId();
446
447        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
448
449        /* enable a new network */
450        if (newNetwork && netId != INVALID_NETWORK_ID) {
451            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
452
453            mWifiNative.enableNetwork(netId, false);
454            conf = mConfiguredNetworks.get(netId);
455            if (conf != null)
456                conf.status = Status.ENABLED;
457        }
458
459        conf = mConfiguredNetworks.get(netId);
460        if (conf != null) {
461            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
462                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
463
464                // reenable autojoin, since new information has been provided
465                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
466                enableNetworkWithoutBroadcast(conf.networkId, false);
467            }
468            if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId="
469                    + Integer.toString(netId)
470                    + " uid=" + Integer.toString(config.creatorUid));
471        }
472
473        mWifiNative.saveConfig();
474        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
475                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
476        return result;
477    }
478
479    void updateStatus(int netId, DetailedState state) {
480        if (netId != INVALID_NETWORK_ID) {
481            WifiConfiguration config = mConfiguredNetworks.get(netId);
482            if (config == null) return;
483            switch (state) {
484                case CONNECTED:
485                    config.status = Status.CURRENT;
486                    //we successfully connected, hence remove the blacklist
487                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
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.setAutoJoinStatus(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(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
1128                            + SEPARATOR_KEY);
1129                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
1130                            + Integer.toString(config.disableReason)
1131                            + SEPARATOR_KEY);
1132                    out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
1133                            + SEPARATOR_KEY);
1134                    out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
1135                            + SEPARATOR_KEY);
1136                    out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
1137                            + SEPARATOR_KEY);
1138                    if (config.peerWifiConfiguration != null) {
1139                        out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
1140                                + SEPARATOR_KEY);
1141                    }
1142                    out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
1143                            + SEPARATOR_KEY);
1144                    out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
1145                            + SEPARATOR_KEY);
1146                    out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
1147                            + SEPARATOR_KEY);
1148                    out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
1149                            + SEPARATOR_KEY);
1150                    String allowedKeyManagementString =
1151                            makeString(config.allowedKeyManagement,
1152                                    WifiConfiguration.KeyMgmt.strings);
1153                    out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
1154
1155                    if (config.connectChoices != null) {
1156                        for (String key : config.connectChoices.keySet()) {
1157                            out.writeUTF(CHOICE_KEY + key + SEPARATOR_KEY);
1158                        }
1159                    }
1160                    if (config.linkedConfigurations != null) {
1161                        for (String key : config.linkedConfigurations.keySet()) {
1162                            out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
1163                        }
1164                    }
1165
1166                    if (config.getLinkProperties() != null) {
1167                        String macAddress = config.defaultGwMacAddress;
1168                        if (macAddress != null) {
1169                            out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
1170                        }
1171                    }
1172
1173                    if (config.scanResultCache != null) {
1174                        for (ScanResult result : config.scanResultCache.values()) {
1175                            out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
1176
1177                            out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
1178                                    + SEPARATOR_KEY);
1179
1180                            out.writeUTF(RSSI_KEY + Integer.toString(result.level)
1181                                    + SEPARATOR_KEY);
1182
1183                            if (result.seen != 0) {
1184                                out.writeUTF(MILLI_KEY + Long.toString(result.seen)
1185                                        + SEPARATOR_KEY);
1186                            }
1187                            out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
1188                        }
1189                    }
1190                    if (config.lastFailure != null) {
1191                        out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
1192                    }
1193                    out.writeUTF(SEPARATOR_KEY);
1194                }
1195            }
1196
1197        });
1198    }
1199
1200    public void setLastSelectedConfiguration(int netId) {
1201        if (DBG) {
1202            loge("setLastSelectedConfiguration " + Integer.toString(netId));
1203        }
1204        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
1205            lastSelectedConfiguration = null;
1206        } else {
1207            WifiConfiguration selected = getWifiConfiguration(netId);
1208            if (selected == null) {
1209                lastSelectedConfiguration = null;
1210            } else {
1211                lastSelectedConfiguration = selected.configKey();
1212                if (VDBG) {
1213                    loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
1214                }
1215            }
1216        }
1217    }
1218
1219    public String getLastSelectedConfiguration() {
1220        return lastSelectedConfiguration;
1221    }
1222
1223    private void readNetworkHistory() {
1224        if (VDBG) {
1225            loge("readNetworkHistory path:" + networkHistoryConfigFile, true);
1226        }
1227        DataInputStream in = null;
1228        try {
1229            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
1230                    networkHistoryConfigFile)));
1231            WifiConfiguration config = null;
1232            while (true) {
1233                int id = -1;
1234                String key = in.readUTF();
1235                String bssid = null;
1236                String ssid = null;
1237
1238                int freq = 0;
1239                long seen = 0;
1240                int rssi = WifiConfiguration.INVALID_RSSI;
1241                String caps = null;
1242                if (key.startsWith(CONFIG_KEY)) {
1243
1244                    if (config != null) {
1245                        config = null;
1246                    }
1247                    String configKey = key.replace(CONFIG_KEY, "");
1248                    configKey = configKey.replace(SEPARATOR_KEY, "");
1249                    // get the networkId for that config Key
1250                    Integer n = mNetworkIds.get(configKey.hashCode());
1251                    // skip reading that configuration data
1252                    // since we don't have a corresponding network ID
1253                    if (n == null) {
1254                        loge("readNetworkHistory didnt find netid for hash="
1255                                + Integer.toString(configKey.hashCode())
1256                                + " key: " + configKey);
1257                        continue;
1258                    }
1259                    config = mConfiguredNetworks.get(n);
1260                    if (config == null) {
1261                        loge("readNetworkHistory didnt find config for netid="
1262                                + n.toString()
1263                                + " key: " + configKey);
1264                    }
1265                    ssid = null;
1266                    bssid = null;
1267                    freq = 0;
1268                    seen = 0;
1269                    rssi = WifiConfiguration.INVALID_RSSI;
1270                    caps = null;
1271
1272                } else if (config != null) {
1273                    if (key.startsWith(SSID_KEY)) {
1274                        ssid = key.replace(SSID_KEY, "");
1275                        ssid = ssid.replace(SEPARATOR_KEY, "");
1276                        if (config.SSID != null && !config.SSID.equals(ssid)) {
1277                            loge("Error parsing network history file, mismatched SSIDs");
1278                            config = null; //error
1279                            ssid = null;
1280                        } else {
1281                            config.SSID = ssid;
1282                        }
1283                    }
1284
1285                    if (key.startsWith(DEFAULT_GW_KEY)) {
1286                        String gateway = key.replace(DEFAULT_GW_KEY, "");
1287                        gateway = gateway.replace(SEPARATOR_KEY, "");
1288                        config.defaultGwMacAddress = gateway;
1289                    }
1290
1291                    if (key.startsWith(STATUS_KEY)) {
1292                        String status = key.replace(STATUS_KEY, "");
1293                        status = status.replace(SEPARATOR_KEY, "");
1294                        config.autoJoinStatus = Integer.parseInt(status);
1295                    }
1296
1297                    /*
1298                    if (key.startsWith(SUPPLICANT_STATUS_KEY)) {
1299                        String status = key.replace(SUPPLICANT_STATUS_KEY, "");
1300                        status = status.replace(SEPARATOR_KEY, "");
1301                        config.status = Integer.parseInt(status);
1302                    }
1303
1304                    if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
1305                        String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
1306                        reason = reason.replace(SEPARATOR_KEY, "");
1307                        config.disableReason = Integer.parseInt(reason);
1308                    }*/
1309
1310                    if (key.startsWith(SELF_ADDED_KEY)) {
1311                        String selfAdded = key.replace(SELF_ADDED_KEY, "");
1312                        selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
1313                        config.selfAdded = Boolean.parseBoolean(selfAdded);
1314                    }
1315
1316                    if (key.startsWith(DID_SELF_ADD_KEY)) {
1317                        String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
1318                        didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
1319                        config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
1320                    }
1321
1322                    if (key.startsWith(CREATOR_UID_KEY)) {
1323                        String uid = key.replace(CREATOR_UID_KEY, "");
1324                        uid = uid.replace(SEPARATOR_KEY, "");
1325                        config.creatorUid = Integer.parseInt(uid);
1326                    }
1327
1328                    if (key.startsWith(BLACKLIST_MILLI_KEY)) {
1329                        String milli = key.replace(BLACKLIST_MILLI_KEY, "");
1330                        milli = milli.replace(SEPARATOR_KEY, "");
1331                        config.blackListTimestamp = Long.parseLong(milli);
1332                    }
1333
1334                    if (key.startsWith(CONNECT_UID_KEY)) {
1335                        String uid = key.replace(CONNECT_UID_KEY, "");
1336                        uid = uid.replace(SEPARATOR_KEY, "");
1337                        config.lastConnectUid = Integer.parseInt(uid);
1338                    }
1339
1340                    if (key.startsWith(UPDATE_UID_KEY)) {
1341                        String uid = key.replace(UPDATE_UID_KEY, "");
1342                        uid = uid.replace(SEPARATOR_KEY, "");
1343                        config.lastUpdateUid = Integer.parseInt(uid);
1344                    }
1345
1346                    if (key.startsWith(FAILURE_KEY)) {
1347                        config.lastFailure = key.replace(FAILURE_KEY, "");
1348                        config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
1349                    }
1350
1351                    if (key.startsWith(PEER_CONFIGURATION_KEY)) {
1352                        config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
1353                        config.peerWifiConfiguration =
1354                                config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
1355                    }
1356
1357                    if (key.startsWith(CHOICE_KEY)) {
1358                        String configKey = key.replace(CHOICE_KEY, "");
1359                        configKey = configKey.replace(SEPARATOR_KEY, "");
1360                        if (config.connectChoices == null) {
1361                            config.connectChoices = new HashMap<String, Integer>();
1362                        }
1363                        config.connectChoices.put(configKey, -1);
1364                    }
1365
1366                    if (key.startsWith(LINK_KEY)) {
1367                        String configKey = key.replace(LINK_KEY, "");
1368                        configKey = configKey.replace(SEPARATOR_KEY, "");
1369                        if (config.linkedConfigurations == null) {
1370                            config.linkedConfigurations = new HashMap<String, Integer>();
1371                        }
1372                        if (config.linkedConfigurations != null) {
1373                            config.linkedConfigurations.put(configKey, -1);
1374                        }
1375                    }
1376
1377                    if (key.startsWith(BSSID_KEY)) {
1378                        if (key.startsWith(BSSID_KEY)) {
1379                            bssid = key.replace(BSSID_KEY, "");
1380                            bssid = bssid.replace(SEPARATOR_KEY, "");
1381                            freq = 0;
1382                            seen = 0;
1383                            rssi = WifiConfiguration.INVALID_RSSI;
1384                            caps = "";
1385                        }
1386
1387                        if (key.startsWith(RSSI_KEY)) {
1388                            String lvl = key.replace(RSSI_KEY, "");
1389                            lvl = lvl.replace(SEPARATOR_KEY, "");
1390                            rssi = Integer.parseInt(lvl);
1391                        }
1392
1393                        if (key.startsWith(FREQ_KEY)) {
1394                            String channel = key.replace(FREQ_KEY, "");
1395                            channel = channel.replace(SEPARATOR_KEY, "");
1396                            freq = Integer.parseInt(channel);
1397                        }
1398
1399                        if (key.startsWith(DATE_KEY)) {
1400                        /*
1401                         * when reading the configuration from file we don't update the date
1402                         * so as to avoid reading back stale or non-sensical data that would
1403                         * depend on network time.
1404                         * The date of a WifiConfiguration should only come from actual scan result.
1405                         *
1406                        String s = key.replace(FREQ_KEY, "");
1407                        seen = Integer.getInteger(s);
1408                        */
1409                        }
1410
1411                        if (key.startsWith(BSSID_KEY_END)) {
1412
1413                            if ((bssid != null) && (ssid != null)) {
1414
1415                                if (config.scanResultCache == null) {
1416                                    config.scanResultCache = new HashMap<String, ScanResult>();
1417                                }
1418                                WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
1419                                ScanResult result = new ScanResult(wssid, bssid,
1420                                        caps, rssi, freq, (long) 0);
1421                                result.seen = seen;
1422                                config.scanResultCache.put(bssid, result);
1423                            }
1424                        }
1425                    }
1426                }
1427            }
1428        } catch (EOFException ignore) {
1429            if (in != null) {
1430                try {
1431                    in.close();
1432                } catch (Exception e) {
1433                    loge("readNetworkHistory: Error reading file" + e);
1434                }
1435            }
1436        } catch (IOException e) {
1437            loge("readNetworkHistory: Error parsing configuration" + e);
1438        }
1439
1440        if(in!=null) {
1441            try {
1442                in.close();
1443            } catch (Exception e) {
1444                loge("readNetworkHistory: Error closing file" + e);
1445            }
1446        }
1447    }
1448
1449    private void writeIpAndProxyConfigurations() {
1450        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
1451        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1452            if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
1453                networks.put(configKey(config), config.getIpConfiguration());
1454            }
1455        }
1456
1457        super.writeIpAndProxyConfigurations(ipConfigFile, networks);
1458    }
1459
1460    private void readIpAndProxyConfigurations() {
1461        SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
1462
1463        if (networks.size() == 0) {
1464            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
1465            return;
1466        }
1467
1468        for (int i = 0; i < networks.size(); i++) {
1469            Integer id = (Integer) i;
1470            WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
1471
1472
1473            if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1474                loge("configuration found for missing network, nid="+Integer.toString(id)
1475                        +", ignored, networks.size=" + Integer.toString(networks.size()));
1476            } else {
1477                config.setIpConfiguration(networks.valueAt(i));
1478            }
1479        }
1480    }
1481
1482    /*
1483     * Convert string to Hexadecimal before passing to wifi native layer
1484     * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
1485     * conversion to hex is required because SSIDs can have space characters in them;
1486     * and that can confuses the supplicant because it uses space charaters as delimiters
1487     */
1488
1489    private String encodeSSID(String str){
1490        String tmp = removeDoubleQuotes(str);
1491        return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
1492    }
1493
1494    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
1495        /*
1496         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1497         * network configuration. Otherwise, the networkId should
1498         * refer to an existing configuration.
1499         */
1500
1501        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1502
1503        int netId = config.networkId;
1504        boolean newNetwork = false;
1505        // networkId of INVALID_NETWORK_ID means we want to create a new network
1506        if (netId == INVALID_NETWORK_ID) {
1507            Integer savedNetId = mNetworkIds.get(configKey(config));
1508            //paranoia: check if either we have a network Id or a WifiConfiguration
1509            //matching the one we are trying to add.
1510            if (savedNetId == null) {
1511                for (WifiConfiguration test : mConfiguredNetworks.values()) {
1512                    if (test.configKey().equals(config.configKey())) {
1513                        savedNetId = test.networkId;
1514                        loge("addOrUpdateNetworkNative " + config.configKey()
1515                                + " was found, but no network Id");
1516                        break;
1517                    }
1518                }
1519            }
1520            if (savedNetId != null) {
1521                netId = savedNetId;
1522            } else {
1523                newNetwork = true;
1524                netId = mWifiNative.addNetwork();
1525                if (netId < 0) {
1526                    loge("Failed to add a network!");
1527                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
1528                } else {
1529                    loge("addOrUpdateNetworkNative created netId=" + netId);
1530                }
1531            }
1532        }
1533
1534        boolean updateFailed = true;
1535
1536        setVariables: {
1537
1538            if (config.SSID != null &&
1539                    !mWifiNative.setNetworkVariable(
1540                        netId,
1541                        WifiConfiguration.ssidVarName,
1542                        encodeSSID(config.SSID))) {
1543                loge("failed to set SSID: "+config.SSID);
1544                break setVariables;
1545            }
1546
1547            if (config.BSSID != null) {
1548                loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
1549                if (!mWifiNative.setNetworkVariable(
1550                        netId,
1551                        WifiConfiguration.bssidVarName,
1552                        config.BSSID)) {
1553                    loge("failed to set BSSID: " + config.BSSID);
1554                    break setVariables;
1555                }
1556            }
1557
1558            String allowedKeyManagementString =
1559                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
1560            if (config.allowedKeyManagement.cardinality() != 0 &&
1561                    !mWifiNative.setNetworkVariable(
1562                        netId,
1563                        WifiConfiguration.KeyMgmt.varName,
1564                        allowedKeyManagementString)) {
1565                loge("failed to set key_mgmt: "+
1566                        allowedKeyManagementString);
1567                break setVariables;
1568            }
1569
1570            String allowedProtocolsString =
1571                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
1572            if (config.allowedProtocols.cardinality() != 0 &&
1573                    !mWifiNative.setNetworkVariable(
1574                        netId,
1575                        WifiConfiguration.Protocol.varName,
1576                        allowedProtocolsString)) {
1577                loge("failed to set proto: "+
1578                        allowedProtocolsString);
1579                break setVariables;
1580            }
1581
1582            String allowedAuthAlgorithmsString =
1583                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1584            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
1585                    !mWifiNative.setNetworkVariable(
1586                        netId,
1587                        WifiConfiguration.AuthAlgorithm.varName,
1588                        allowedAuthAlgorithmsString)) {
1589                loge("failed to set auth_alg: "+
1590                        allowedAuthAlgorithmsString);
1591                break setVariables;
1592            }
1593
1594            String allowedPairwiseCiphersString =
1595                    makeString(config.allowedPairwiseCiphers,
1596                    WifiConfiguration.PairwiseCipher.strings);
1597            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
1598                    !mWifiNative.setNetworkVariable(
1599                        netId,
1600                        WifiConfiguration.PairwiseCipher.varName,
1601                        allowedPairwiseCiphersString)) {
1602                loge("failed to set pairwise: "+
1603                        allowedPairwiseCiphersString);
1604                break setVariables;
1605            }
1606
1607            String allowedGroupCiphersString =
1608                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1609            if (config.allowedGroupCiphers.cardinality() != 0 &&
1610                    !mWifiNative.setNetworkVariable(
1611                        netId,
1612                        WifiConfiguration.GroupCipher.varName,
1613                        allowedGroupCiphersString)) {
1614                loge("failed to set group: "+
1615                        allowedGroupCiphersString);
1616                break setVariables;
1617            }
1618
1619            // Prevent client screw-up by passing in a WifiConfiguration we gave it
1620            // by preventing "*" as a key.
1621            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
1622                    !mWifiNative.setNetworkVariable(
1623                        netId,
1624                        WifiConfiguration.pskVarName,
1625                        config.preSharedKey)) {
1626                loge("failed to set psk");
1627                break setVariables;
1628            }
1629
1630            boolean hasSetKey = false;
1631            if (config.wepKeys != null) {
1632                for (int i = 0; i < config.wepKeys.length; i++) {
1633                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
1634                    // by preventing "*" as a key.
1635                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
1636                        if (!mWifiNative.setNetworkVariable(
1637                                    netId,
1638                                    WifiConfiguration.wepKeyVarNames[i],
1639                                    config.wepKeys[i])) {
1640                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
1641                            break setVariables;
1642                        }
1643                        hasSetKey = true;
1644                    }
1645                }
1646            }
1647
1648            if (hasSetKey) {
1649                if (!mWifiNative.setNetworkVariable(
1650                            netId,
1651                            WifiConfiguration.wepTxKeyIdxVarName,
1652                            Integer.toString(config.wepTxKeyIndex))) {
1653                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
1654                    break setVariables;
1655                }
1656            }
1657
1658            if (!mWifiNative.setNetworkVariable(
1659                        netId,
1660                        WifiConfiguration.priorityVarName,
1661                        Integer.toString(config.priority))) {
1662                loge(config.SSID + ": failed to set priority: "
1663                        +config.priority);
1664                break setVariables;
1665            }
1666
1667            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
1668                        netId,
1669                        WifiConfiguration.hiddenSSIDVarName,
1670                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
1671                loge(config.SSID + ": failed to set hiddenSSID: "+
1672                        config.hiddenSSID);
1673                break setVariables;
1674            }
1675
1676            if (config.enterpriseConfig != null &&
1677                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1678
1679                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
1680
1681                if (needsKeyStore(enterpriseConfig)) {
1682                    /**
1683                     * Keyguard settings may eventually be controlled by device policy.
1684                     * We check here if keystore is unlocked before installing
1685                     * credentials.
1686                     * TODO: Do we need a dialog here ?
1687                     */
1688                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
1689                        loge(config.SSID + ": key store is locked");
1690                        break setVariables;
1691                    }
1692
1693                    try {
1694                        /* config passed may include only fields being updated.
1695                         * In order to generate the key id, fetch uninitialized
1696                         * fields from the currently tracked configuration
1697                         */
1698                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1699                        String keyId = config.getKeyIdForCredentials(currentConfig);
1700
1701                        if (!installKeys(enterpriseConfig, keyId)) {
1702                            loge(config.SSID + ": failed to install keys");
1703                            break setVariables;
1704                        }
1705                    } catch (IllegalStateException e) {
1706                        loge(config.SSID + " invalid config for key installation");
1707                        break setVariables;
1708                    }
1709                }
1710
1711                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
1712                for (String key : enterpriseFields.keySet()) {
1713                        String value = enterpriseFields.get(key);
1714                        if (!mWifiNative.setNetworkVariable(
1715                                    netId,
1716                                    key,
1717                                    value)) {
1718                            removeKeys(enterpriseConfig);
1719                            loge(config.SSID + ": failed to set " + key +
1720                                    ": " + value);
1721                            break setVariables;
1722                        }
1723                }
1724            }
1725            updateFailed = false;
1726        } //end of setVariables
1727
1728        if (updateFailed) {
1729            if (newNetwork) {
1730                mWifiNative.removeNetwork(netId);
1731                loge("Failed to set a network variable, removed network: " + netId);
1732            }
1733            return new NetworkUpdateResult(INVALID_NETWORK_ID);
1734        }
1735
1736        /* An update of the network variables requires reading them
1737         * back from the supplicant to update mConfiguredNetworks.
1738         * This is because some of the variables (SSID, wep keys &
1739         * passphrases) reflect different values when read back than
1740         * when written. For example, wep key is stored as * irrespective
1741         * of the value sent to the supplicant
1742         */
1743        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
1744        if (currentConfig == null) {
1745            currentConfig = new WifiConfiguration();
1746            currentConfig.setIpAssignment(IpAssignment.DHCP);
1747            currentConfig.setProxySettings(ProxySettings.NONE);
1748            currentConfig.networkId = netId;
1749            if (config != null) {
1750                //carry over the creation parameters
1751                currentConfig.selfAdded = config.selfAdded;
1752                currentConfig.didSelfAdd = config.didSelfAdd;
1753                currentConfig.lastConnectUid = config.lastConnectUid;
1754                currentConfig.lastUpdateUid = config.lastUpdateUid;
1755                currentConfig.creatorUid = config.creatorUid;
1756                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
1757            }
1758            if (DBG) {
1759                loge("created new config netId=" + Integer.toString(netId)
1760                        + " uid=" + Integer.toString(currentConfig.creatorUid));
1761            }
1762        }
1763
1764        if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1765            //make sure the configuration is not deleted anymore since we just
1766            //added or modified it.
1767            currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
1768            currentConfig.selfAdded = false;
1769            currentConfig.didSelfAdd = false;
1770        }
1771
1772        if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
1773
1774        readNetworkVariables(currentConfig);
1775
1776        mConfiguredNetworks.put(netId, currentConfig);
1777        mNetworkIds.put(configKey(currentConfig), netId);
1778
1779        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
1780        result.setIsNewNetwork(newNetwork);
1781        result.setNetworkId(netId);
1782        return result;
1783    }
1784
1785
1786    public void linkConfiguration(WifiConfiguration config) {
1787        for (WifiConfiguration link : mConfiguredNetworks.values()) {
1788            boolean doLink = false;
1789
1790            if (link.configKey().equals(config.configKey())) {
1791                continue;
1792            }
1793
1794            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1795                continue;
1796            }
1797
1798            //autojoin will be allowed to dynamically jump from a linked configuration
1799            //to another, hence only link configurations that have equivalent level of security
1800            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
1801                continue;
1802            }
1803
1804            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
1805                //if both default GW are known, compare based on RSSI only if the GW is equal
1806                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
1807
1808                    if (VDBG) {
1809                        loge("linkConfiguration link due to same gw" + link.SSID +
1810                                " and " + config.SSID + " GW " + config.defaultGwMacAddress);
1811                    }
1812                    doLink = true;
1813                }
1814            } else {
1815                // we do not know BOTH default gateways hence we will try to link
1816                // hoping that WifiConfigurations are indeed behind the same gateway
1817                // once both WifiConfiguration will have been tried we will know
1818                // the default gateway and revisit the choice of linking them
1819                if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
1820                        && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
1821                    String abssid = "";
1822                    String bbssid = "";
1823                    for (String key : config.scanResultCache.keySet()) {
1824                        abssid = key;
1825                    }
1826                    for (String key : link.scanResultCache.keySet()) {
1827                        bbssid = key;
1828                    }
1829                    if (VDBG) {
1830                        loge("linkConfiguration try to link due to DBDC BSSID match " + link.SSID +
1831                                " and " + config.SSID + " bssida " + abssid + " bssidb " + bbssid);
1832                    }
1833                    if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
1834                        //if first 16 ascii characters of BSSID matches, we assume this is a DBDC
1835                        doLink = true;
1836                    }
1837                }
1838            }
1839
1840            if (doLink) {
1841                if (VDBG) {
1842                    loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID);
1843                }
1844                if (link.linkedConfigurations == null) {
1845                    link.linkedConfigurations = new HashMap<String, Integer>();
1846                }
1847                if (config.linkedConfigurations == null) {
1848                    config.linkedConfigurations = new HashMap<String, Integer>();
1849                }
1850                link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
1851                config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
1852            } else {
1853                //todo if they are linked, break the link
1854            }
1855        }
1856    }
1857
1858    /*
1859     * We try to link a scan result with a WifiConfiguration for which SSID and ket management dont match,
1860     * for instance, we try identify the 5GHz SSID of a DBDC AP, even though we know only of the 2.4GHz
1861     *
1862     * Obviously, this function is not optimal since it is used to compare every scan
1863     * result with every Saved WifiConfiguration, with a string.equals operation.
1864     * As a speed up, might be better to implement the mConfiguredNetworks store as a
1865     * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
1866     * so as to speed this up. Also to prevent the tiny probability of hash collision.
1867     *
1868     */
1869    public WifiConfiguration associateWithConfiguration(ScanResult result) {
1870        String configKey = WifiConfiguration.configKey(result);
1871        if (configKey == null) {
1872            if (DBG) loge("associateWithConfiguration(): no config key " );
1873            return null;
1874        }
1875
1876        //need to compare with quoted string
1877        String SSID = "\"" + result.SSID + "\"";
1878
1879        WifiConfiguration config = null;
1880        for (WifiConfiguration link : mConfiguredNetworks.values()) {
1881            boolean doLink = false;
1882
1883            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) {
1884                //make sure we dont associate the scan result to a deleted config
1885                continue;
1886            }
1887
1888            if (configKey.equals(link.configKey())) {
1889                if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
1890                return link; //found it exactly
1891            }
1892
1893            if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) {
1894                String bssid = "";
1895                for (String key : link.scanResultCache.keySet()) {
1896                    bssid = key;
1897                }
1898
1899                if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
1900                        && SSID.regionMatches(false, 0, link.SSID, 0, 3)) {
1901                    // if first 16 ascii characters of BSSID matches, and first 3
1902                    // characters of SSID match, we assume this is a home setup
1903                    // and thus we will try to transfer the password from the known
1904                    // BSSID/SSID to the recently found BSSID/SSID
1905
1906                    //if (VDBG)
1907                    //    loge("associateWithConfiguration OK " );
1908                    doLink = true;
1909                }
1910            }
1911
1912            if (doLink) {
1913                //try to make a non verified WifiConfiguration, but only if the original
1914                //configuration was not self already added
1915                if (VDBG) {
1916                    loge("associateWithConfiguration: will create " +
1917                            result.SSID + " and associate it with: " + link.SSID);
1918                }
1919                config = wifiConfigurationFromScanResult(result);
1920                if (config != null) {
1921                    config.selfAdded = true;
1922                    config.didSelfAdd = true;
1923                    config.peerWifiConfiguration = link.configKey();
1924                    if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
1925                            config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1926                        //transfer the credentials from the configuration we are linking from
1927                        String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
1928                        if (psk != null) {
1929                            config.preSharedKey = psk;
1930                            if (VDBG) {
1931                                if (config.preSharedKey != null)
1932                                    loge(" transfer PSK : " + config.preSharedKey);
1933                            }
1934
1935                            //link configurations
1936                            if (link.linkedConfigurations == null) {
1937                                link.linkedConfigurations = new HashMap<String, Integer>();
1938                            }
1939                            if (config.linkedConfigurations == null) {
1940                                config.linkedConfigurations = new HashMap<String, Integer>();
1941                            }
1942                            link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
1943                            config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
1944                        } else {
1945                            config = null;
1946                        }
1947                    } else {
1948                        config = null;
1949                    }
1950                }
1951            } else {
1952                //todo if they are linked, break the link
1953            }
1954        }
1955        return config;
1956    }
1957
1958    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
1959        if (config == null)
1960            return null;
1961        long now_ms = System.currentTimeMillis();
1962
1963        HashSet<Integer> channels = new HashSet<Integer>();
1964
1965        //get channels for this configuration, if there are at least 2 BSSIDs
1966        if (config.scanResultCache == null && config.linkedConfigurations == null) {
1967            return null;
1968        }
1969
1970        if (VDBG) {
1971            StringBuilder dbg = new StringBuilder();
1972            dbg.append("makeChannelList age=" + Integer.toString(age) + " for " + config.configKey());
1973            if (config.scanResultCache != null) {
1974                dbg.append(" bssids=" + config.scanResultCache.size());
1975            }
1976            if (config.linkedConfigurations != null) {
1977                dbg.append(" linked=" + config.linkedConfigurations.size());
1978            }
1979            loge(dbg.toString());
1980        }
1981
1982        if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
1983            for (ScanResult result : config.scanResultCache.values()) {
1984                if (VDBG) {
1985                    boolean test = (now_ms - result.seen) < age;
1986                    loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
1987                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
1988                }
1989                if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
1990                    channels.add(result.frequency);
1991                }
1992            }
1993        }
1994
1995        //get channels for linked configurations
1996        if (config.linkedConfigurations != null) {
1997            for (String key : config.linkedConfigurations.keySet()) {
1998                WifiConfiguration linked = getWifiConfiguration(key);
1999                if (linked == null)
2000                    continue;
2001                if (linked.scanResultCache == null) {
2002                    continue;
2003                }
2004                for (ScanResult result : linked.scanResultCache.values()) {
2005                    if (VDBG) {
2006                        loge("has link: " + result.BSSID
2007                                + " freq=" + Integer.toString(result.frequency)
2008                                + " age=" + Long.toString(now_ms - result.seen));
2009                    }
2010                    if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2011                        channels.add(result.frequency);
2012                    }
2013                }
2014            }
2015        }
2016        return channels;
2017    }
2018
2019    public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) {
2020        WifiConfiguration found = null;
2021        if (scanResult == null)
2022            return found;
2023
2024        //first step, look for this scan Result by SSID + Key Management
2025        String key = WifiConfiguration.configKey(scanResult);
2026        int hash = key.hashCode();
2027
2028        Integer netId = mNetworkIds.get(hash);
2029        if (netId == null) return null;
2030        WifiConfiguration config = mConfiguredNetworks.get(netId);
2031        if (config != null) {
2032           if (config.scanResultCache == null) {
2033                config.scanResultCache = new HashMap<String, ScanResult>();
2034           }
2035           if (config.scanResultCache == null) {
2036                return null;
2037           }
2038           //add the scan result to this WifiConfiguration
2039           config.scanResultCache.put(scanResult.BSSID, scanResult);
2040           mConfiguredNetworks.put(netId, config);
2041           linkConfiguration(config);
2042           found = config;
2043        }
2044
2045        if (VDBG) {
2046            config = mConfiguredNetworks.get(netId);
2047            if (config != null) {
2048                if (config.scanResultCache != null) {
2049                    loge("                    got known scan result " +
2050                            scanResult.BSSID + " key : " + key + " num: " +
2051                            Integer.toString(config.scanResultCache.size())
2052                            + " rssi=" + Integer.toString(scanResult.level));
2053                } else {
2054                    loge("                    got known scan result and no cache" +
2055                            scanResult.BSSID + " key : " + key);
2056                }
2057            }
2058        }
2059        return found;
2060
2061    }
2062
2063    /* Compare current and new configuration and write to file on change */
2064    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
2065            WifiConfiguration currentConfig,
2066            WifiConfiguration newConfig) {
2067        boolean ipChanged = false;
2068        boolean proxyChanged = false;
2069        LinkProperties linkProperties = null;
2070
2071        if (VDBG) {
2072            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
2073                    newConfig.SSID + " path: " + ipConfigFile);
2074        }
2075
2076
2077        switch (newConfig.getIpAssignment()) {
2078            case STATIC:
2079                Collection<LinkAddress> currentLinkAddresses = currentConfig.getLinkProperties()
2080                        .getLinkAddresses();
2081                Collection<LinkAddress> newLinkAddresses = newConfig.getLinkProperties()
2082                        .getLinkAddresses();
2083                Collection<InetAddress> currentDnses =
2084                        currentConfig.getLinkProperties().getDnsServers();
2085                Collection<InetAddress> newDnses = newConfig.getLinkProperties().getDnsServers();
2086                Collection<RouteInfo> currentRoutes = currentConfig.getLinkProperties().getRoutes();
2087                Collection<RouteInfo> newRoutes = newConfig.getLinkProperties().getRoutes();
2088
2089                boolean linkAddressesDiffer =
2090                        (currentLinkAddresses.size() != newLinkAddresses.size()) ||
2091                        !currentLinkAddresses.containsAll(newLinkAddresses);
2092                boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
2093                        !currentDnses.containsAll(newDnses);
2094                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
2095                        !currentRoutes.containsAll(newRoutes);
2096
2097                if ((currentConfig.getIpAssignment() != newConfig.getIpAssignment()) ||
2098                        linkAddressesDiffer ||
2099                        dnsesDiffer ||
2100                        routesDiffer) {
2101                    ipChanged = true;
2102                }
2103                break;
2104            case DHCP:
2105                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2106                    ipChanged = true;
2107                }
2108                break;
2109            case UNASSIGNED:
2110                /* Ignore */
2111                break;
2112            default:
2113                loge("Ignore invalid ip assignment during write");
2114                break;
2115        }
2116
2117        switch (newConfig.getProxySettings()) {
2118            case STATIC:
2119            case PAC:
2120                ProxyInfo newHttpProxy = newConfig.getLinkProperties().getHttpProxy();
2121                ProxyInfo currentHttpProxy = currentConfig.getLinkProperties().getHttpProxy();
2122
2123                if (newHttpProxy != null) {
2124                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
2125                } else {
2126                    proxyChanged = (currentHttpProxy != null);
2127                }
2128                break;
2129            case NONE:
2130                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
2131                    proxyChanged = true;
2132                }
2133                break;
2134            case UNASSIGNED:
2135                /* Ignore */
2136                break;
2137            default:
2138                loge("Ignore invalid proxy configuration during write");
2139                break;
2140        }
2141
2142        if (!ipChanged) {
2143            linkProperties = copyIpSettingsFromConfig(currentConfig);
2144        } else {
2145            currentConfig.setIpAssignment(newConfig.getIpAssignment());
2146            linkProperties = copyIpSettingsFromConfig(newConfig);
2147            log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
2148                    linkProperties.toString());
2149        }
2150
2151
2152        if (!proxyChanged) {
2153            linkProperties.setHttpProxy(currentConfig.getLinkProperties().getHttpProxy());
2154        } else {
2155            currentConfig.setProxySettings(newConfig.getProxySettings());
2156            linkProperties.setHttpProxy(newConfig.getLinkProperties().getHttpProxy());
2157            log("proxy changed SSID = " + currentConfig.SSID);
2158            if (linkProperties.getHttpProxy() != null) {
2159                log(" proxyProperties: " + linkProperties.getHttpProxy().toString());
2160            }
2161        }
2162
2163        if (ipChanged || proxyChanged) {
2164            currentConfig.setLinkProperties(linkProperties);
2165            writeIpAndProxyConfigurations();
2166            sendConfiguredNetworksChangedBroadcast(currentConfig,
2167                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
2168        }
2169        return new NetworkUpdateResult(ipChanged, proxyChanged);
2170    }
2171
2172    private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
2173        LinkProperties linkProperties = new LinkProperties();
2174        linkProperties.setInterfaceName(config.getLinkProperties().getInterfaceName());
2175        for (LinkAddress linkAddr : config.getLinkProperties().getLinkAddresses()) {
2176            linkProperties.addLinkAddress(linkAddr);
2177        }
2178        for (RouteInfo route : config.getLinkProperties().getRoutes()) {
2179            linkProperties.addRoute(route);
2180        }
2181        for (InetAddress dns : config.getLinkProperties().getDnsServers()) {
2182            linkProperties.addDnsServer(dns);
2183        }
2184        return linkProperties;
2185    }
2186
2187    /**
2188     * Read the variables from the supplicant daemon that are needed to
2189     * fill in the WifiConfiguration object.
2190     *
2191     * @param config the {@link WifiConfiguration} object to be filled in.
2192     */
2193    private void readNetworkVariables(WifiConfiguration config) {
2194
2195        int netId = config.networkId;
2196        if (netId < 0)
2197            return;
2198
2199        /*
2200         * TODO: maybe should have a native method that takes an array of
2201         * variable names and returns an array of values. But we'd still
2202         * be doing a round trip to the supplicant daemon for each variable.
2203         */
2204        String value;
2205
2206        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
2207        if (!TextUtils.isEmpty(value)) {
2208            if (value.charAt(0) != '"') {
2209                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
2210                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
2211                //supplicant string
2212            } else {
2213                config.SSID = value;
2214            }
2215        } else {
2216            config.SSID = null;
2217        }
2218
2219        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
2220        if (!TextUtils.isEmpty(value)) {
2221            config.BSSID = value;
2222        } else {
2223            config.BSSID = null;
2224        }
2225
2226        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
2227        config.priority = -1;
2228        if (!TextUtils.isEmpty(value)) {
2229            try {
2230                config.priority = Integer.parseInt(value);
2231            } catch (NumberFormatException ignore) {
2232            }
2233        }
2234
2235        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
2236        config.hiddenSSID = false;
2237        if (!TextUtils.isEmpty(value)) {
2238            try {
2239                config.hiddenSSID = Integer.parseInt(value) != 0;
2240            } catch (NumberFormatException ignore) {
2241            }
2242        }
2243
2244        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
2245        config.wepTxKeyIndex = -1;
2246        if (!TextUtils.isEmpty(value)) {
2247            try {
2248                config.wepTxKeyIndex = Integer.parseInt(value);
2249            } catch (NumberFormatException ignore) {
2250            }
2251        }
2252
2253        for (int i = 0; i < 4; i++) {
2254            value = mWifiNative.getNetworkVariable(netId,
2255                    WifiConfiguration.wepKeyVarNames[i]);
2256            if (!TextUtils.isEmpty(value)) {
2257                config.wepKeys[i] = value;
2258            } else {
2259                config.wepKeys[i] = null;
2260            }
2261        }
2262
2263        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
2264        if (!TextUtils.isEmpty(value)) {
2265            config.preSharedKey = value;
2266        } else {
2267            config.preSharedKey = null;
2268        }
2269
2270        value = mWifiNative.getNetworkVariable(config.networkId,
2271                WifiConfiguration.Protocol.varName);
2272        if (!TextUtils.isEmpty(value)) {
2273            String vals[] = value.split(" ");
2274            for (String val : vals) {
2275                int index =
2276                    lookupString(val, WifiConfiguration.Protocol.strings);
2277                if (0 <= index) {
2278                    config.allowedProtocols.set(index);
2279                }
2280            }
2281        }
2282
2283        value = mWifiNative.getNetworkVariable(config.networkId,
2284                WifiConfiguration.KeyMgmt.varName);
2285        if (!TextUtils.isEmpty(value)) {
2286            String vals[] = value.split(" ");
2287            for (String val : vals) {
2288                int index =
2289                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
2290                if (0 <= index) {
2291                    config.allowedKeyManagement.set(index);
2292                }
2293            }
2294        }
2295
2296        value = mWifiNative.getNetworkVariable(config.networkId,
2297                WifiConfiguration.AuthAlgorithm.varName);
2298        if (!TextUtils.isEmpty(value)) {
2299            String vals[] = value.split(" ");
2300            for (String val : vals) {
2301                int index =
2302                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
2303                if (0 <= index) {
2304                    config.allowedAuthAlgorithms.set(index);
2305                }
2306            }
2307        }
2308
2309        value = mWifiNative.getNetworkVariable(config.networkId,
2310                WifiConfiguration.PairwiseCipher.varName);
2311        if (!TextUtils.isEmpty(value)) {
2312            String vals[] = value.split(" ");
2313            for (String val : vals) {
2314                int index =
2315                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
2316                if (0 <= index) {
2317                    config.allowedPairwiseCiphers.set(index);
2318                }
2319            }
2320        }
2321
2322        value = mWifiNative.getNetworkVariable(config.networkId,
2323                WifiConfiguration.GroupCipher.varName);
2324        if (!TextUtils.isEmpty(value)) {
2325            String vals[] = value.split(" ");
2326            for (String val : vals) {
2327                int index =
2328                    lookupString(val, WifiConfiguration.GroupCipher.strings);
2329                if (0 <= index) {
2330                    config.allowedGroupCiphers.set(index);
2331                }
2332            }
2333        }
2334
2335        if (config.enterpriseConfig == null) {
2336            config.enterpriseConfig = new WifiEnterpriseConfig();
2337        }
2338        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
2339        for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
2340            value = mWifiNative.getNetworkVariable(netId, key);
2341            if (!TextUtils.isEmpty(value)) {
2342                enterpriseFields.put(key, removeDoubleQuotes(value));
2343            } else {
2344                enterpriseFields.put(key, EMPTY_VALUE);
2345            }
2346        }
2347
2348        if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
2349            saveConfig();
2350        }
2351
2352        migrateCerts(config.enterpriseConfig);
2353        // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
2354    }
2355
2356    private static String removeDoubleQuotes(String string) {
2357        int length = string.length();
2358        if ((length > 1) && (string.charAt(0) == '"')
2359                && (string.charAt(length - 1) == '"')) {
2360            return string.substring(1, length - 1);
2361        }
2362        return string;
2363    }
2364
2365    private static String makeString(BitSet set, String[] strings) {
2366        StringBuffer buf = new StringBuffer();
2367        int nextSetBit = -1;
2368
2369        /* Make sure all set bits are in [0, strings.length) to avoid
2370         * going out of bounds on strings.  (Shouldn't happen, but...) */
2371        set = set.get(0, strings.length);
2372
2373        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
2374            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
2375        }
2376
2377        // remove trailing space
2378        if (set.cardinality() > 0) {
2379            buf.setLength(buf.length() - 1);
2380        }
2381
2382        return buf.toString();
2383    }
2384
2385    private int lookupString(String string, String[] strings) {
2386        int size = strings.length;
2387
2388        string = string.replace('-', '_');
2389
2390        for (int i = 0; i < size; i++)
2391            if (string.equals(strings[i]))
2392                return i;
2393
2394        // if we ever get here, we should probably add the
2395        // value to WifiConfiguration to reflect that it's
2396        // supported by the WPA supplicant
2397        loge("Failed to look-up a string: " + string);
2398
2399        return -1;
2400    }
2401
2402    /* return the allowed key management based on a scan result */
2403
2404    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
2405        WifiConfiguration config = new WifiConfiguration();
2406
2407        config.SSID = "\"" + result.SSID + "\"";
2408
2409        if (VDBG) {
2410            loge("WifiConfiguration from scan results " +
2411                    config.SSID + " cap " + result.capabilities);
2412        }
2413        if (result.capabilities.contains("WEP")) {
2414            config.allowedKeyManagement.set(KeyMgmt.NONE);
2415            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
2416            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
2417        }
2418
2419        if (result.capabilities.contains("PSK")) {
2420            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
2421        }
2422
2423        if (result.capabilities.contains("EAP")) {
2424            //this is probably wrong, as we don't have a way to enter the enterprise config
2425            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
2426            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
2427        }
2428
2429        config.scanResultCache = new HashMap<String, ScanResult>();
2430        if (config.scanResultCache == null)
2431            return null;
2432        config.scanResultCache.put(result.BSSID, result);
2433
2434        return config;
2435    }
2436
2437
2438    /* Returns a unique for a given configuration */
2439    private static int configKey(WifiConfiguration config) {
2440        String key = config.configKey();
2441        return key.hashCode();
2442    }
2443
2444    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2445        pw.println("WifiConfigStore");
2446        pw.println("mLastPriority " + mLastPriority);
2447        pw.println("Configured networks");
2448        for (WifiConfiguration conf : getConfiguredNetworks()) {
2449            pw.println(conf);
2450        }
2451        pw.println();
2452
2453        if (mLocalLog != null) {
2454            pw.println("WifiConfigStore - Log Begin ----");
2455            mLocalLog.dump(fd, pw, args);
2456            pw.println("WifiConfigStore - Log End ----");
2457        }
2458    }
2459
2460    public String getConfigFile() {
2461        return ipConfigFile;
2462    }
2463
2464    protected void loge(String s) {
2465        loge(s, false);
2466    }
2467
2468    protected void loge(String s, boolean stack) {
2469        if (stack) {
2470            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2471                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
2472                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
2473                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
2474        } else {
2475            Log.e(TAG, s);
2476        }
2477    }
2478
2479    protected void log(String s) {
2480        Log.d(TAG, s);
2481    }
2482
2483    private void localLog(String s) {
2484        if (mLocalLog != null) {
2485            mLocalLog.log(s);
2486        }
2487    }
2488
2489    private void localLog(String s, int netId) {
2490        if (mLocalLog == null) {
2491            return;
2492        }
2493
2494        WifiConfiguration config;
2495        synchronized(mConfiguredNetworks) {
2496            config = mConfiguredNetworks.get(netId);
2497        }
2498
2499        if (config != null) {
2500            mLocalLog.log(s + " " + config.getPrintableSsid());
2501        } else {
2502            mLocalLog.log(s + " " + netId);
2503        }
2504    }
2505
2506    // Certificate and private key management for EnterpriseConfig
2507    static boolean needsKeyStore(WifiEnterpriseConfig config) {
2508        // Has no keys to be installed
2509        if (config.getClientCertificate() == null && config.getCaCertificate() == null)
2510            return false;
2511        return true;
2512    }
2513
2514    static boolean isHardwareBackedKey(PrivateKey key) {
2515        return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
2516    }
2517
2518    static boolean hasHardwareBackedKey(Certificate certificate) {
2519        return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
2520    }
2521
2522    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
2523        String client = config.getClientCertificateAlias();
2524        if (!TextUtils.isEmpty(client)) {
2525            // a valid client certificate is configured
2526
2527            // BUGBUG: keyStore.get() never returns certBytes; because it is not
2528            // taking WIFI_UID as a parameter. It always looks for certificate
2529            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
2530            // all certificates need software keystore until we get the get() API
2531            // fixed.
2532
2533            return true;
2534        }
2535
2536        /*
2537        try {
2538
2539            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
2540                    .USER_CERTIFICATE + client);
2541
2542            CertificateFactory factory = CertificateFactory.getInstance("X.509");
2543            if (factory == null) {
2544                Slog.e(TAG, "Error getting certificate factory");
2545                return;
2546            }
2547
2548            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
2549            if (certBytes != null) {
2550                Certificate cert = (X509Certificate) factory.generateCertificate(
2551                        new ByteArrayInputStream(certBytes));
2552
2553                if (cert != null) {
2554                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
2555
2556                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
2557                            .USER_CERTIFICATE + client);
2558                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
2559                            "does not need" ) + " software key store");
2560                } else {
2561                    Slog.d(TAG, "could not generate certificate");
2562                }
2563            } else {
2564                Slog.e(TAG, "Could not load client certificate " + Credentials
2565                        .USER_CERTIFICATE + client);
2566                mNeedsSoftwareKeystore = true;
2567            }
2568
2569        } catch(CertificateException e) {
2570            Slog.e(TAG, "Could not read certificates");
2571            mCaCert = null;
2572            mClientCertificate = null;
2573        }
2574        */
2575
2576        return false;
2577    }
2578
2579    /** called when CS ask WiFistateMachine to disconnect the current network
2580     * because the score is bad.
2581     */
2582    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
2583        /* TODO verify the bad network is current */
2584        WifiConfiguration config = mConfiguredNetworks.get(netId);
2585        if (config != null) {
2586            if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
2587                    && info.is24GHz()) || (info.getRssi() <
2588                            WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
2589                //we got disconnected and RSSI was bad, so disable light
2590                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
2591                        + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
2592                loge("handleBadNetworkDisconnectReport (+4) "
2593                        + Integer.toString(netId) + " " + info);
2594            } else {
2595                //we got disabled but RSSI is good, so disable hard
2596                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
2597                        + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
2598                loge("handleBadNetworkDisconnectReport (+8) "
2599                        + Integer.toString(netId) + " " + info);
2600            }
2601        }
2602    }
2603
2604    void handleSSIDStateChange(int netId, boolean enabled, String message) {
2605        WifiConfiguration config = mConfiguredNetworks.get(netId);
2606        if (config != null) {
2607            if (enabled) {
2608                loge("SSID re-enabled for  " + config.configKey() +
2609                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
2610                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
2611                //TODO: really I don't know if re-enabling is right but we should err on the side of trying to connect
2612                //TODO: even if the attempt will fail
2613                if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
2614                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
2615                }
2616            } else {
2617                loge("SSID temp disabled for  " + config.configKey() +
2618                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
2619                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
2620                if (message != null) {
2621                    loge(" wpa_supplicant message=" + message);
2622                }
2623                if (config.selfAdded && config.lastConnected == 0) {
2624                    //this is a network we self added, and we never succeeded,
2625                    //the user did not create this network and never entered its credentials, so we want
2626                    //to be very aggressive in disabling it completely.
2627                    disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE);
2628                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
2629                    config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE;
2630                } else {
2631                    if (message != null) {
2632                        if (message.contains("WRONG_KEY")
2633                                || message.contains("AUTH_FAILED")) {
2634                            //This configuration has received an auth failure, so disable it
2635                            //temporarily because we don't want auto-join to try it out.
2636                            //this network may be re-enabled by the "usual"
2637                            // enableAllNetwork function
2638                            //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE:
2639                            //TODO: if we could count on the wrong_ley or auth_failure message to be correct
2640                            //TODO: then we could just mark the configuration as DISABLED_ON_AUTH_FAILURE
2641                            //TODO: and the configuration will stay there until user enter new credentials
2642                            //TODO: It is not the case however, so instead  of disabling, let's start
2643                            //TODO: blacklisting hard
2644                            if (config.autoJoinStatus <=
2645                                    WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
2646                                //4 auth failure will reach 128 and disable permanently
2647                                //autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128
2648                                config.setAutoJoinStatus(4 + config.autoJoinStatus * 4);
2649                                if (config.autoJoinStatus > WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)
2650                                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
2651                            }
2652                            if (DBG) {
2653                                loge("blacklisted " + config.configKey() + " to "
2654                                        + Integer.toString(config.autoJoinStatus));
2655                            }
2656                        }
2657                        message.replace("\n", "");
2658                        message.replace("\r", "");
2659                        config.lastFailure = message;
2660                    }
2661                }
2662            }
2663        }
2664    }
2665
2666    boolean installKeys(WifiEnterpriseConfig config, String name) {
2667        boolean ret = true;
2668        String privKeyName = Credentials.USER_PRIVATE_KEY + name;
2669        String userCertName = Credentials.USER_CERTIFICATE + name;
2670        String caCertName = Credentials.CA_CERTIFICATE + name;
2671        if (config.getClientCertificate() != null) {
2672            byte[] privKeyData = config.getClientPrivateKey().getEncoded();
2673            if (isHardwareBackedKey(config.getClientPrivateKey())) {
2674                // Hardware backed key store is secure enough to store keys un-encrypted, this
2675                // removes the need for user to punch a PIN to get access to these keys
2676                if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
2677                ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
2678                        KeyStore.FLAG_NONE);
2679            } else {
2680                // Software backed key store is NOT secure enough to store keys un-encrypted.
2681                // Save keys encrypted so they are protected with user's PIN. User will
2682                // have to unlock phone before being able to use these keys and connect to
2683                // networks.
2684                if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
2685                ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
2686                        KeyStore.FLAG_ENCRYPTED);
2687            }
2688            if (ret == false) {
2689                return ret;
2690            }
2691
2692            ret = putCertInKeyStore(userCertName, config.getClientCertificate());
2693            if (ret == false) {
2694                // Remove private key installed
2695                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
2696                return ret;
2697            }
2698        }
2699
2700        if (config.getCaCertificate() != null) {
2701            ret = putCertInKeyStore(caCertName, config.getCaCertificate());
2702            if (ret == false) {
2703                if (config.getClientCertificate() != null) {
2704                    // Remove client key+cert
2705                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
2706                    mKeyStore.delete(userCertName, Process.WIFI_UID);
2707                }
2708                return ret;
2709            }
2710        }
2711
2712        // Set alias names
2713        if (config.getClientCertificate() != null) {
2714            config.setClientCertificateAlias(name);
2715            config.resetClientKeyEntry();
2716        }
2717
2718        if (config.getCaCertificate() != null) {
2719            config.setCaCertificateAlias(name);
2720            config.resetCaCertificate();
2721        }
2722
2723        return ret;
2724    }
2725
2726    private boolean putCertInKeyStore(String name, Certificate cert) {
2727        try {
2728            byte[] certData = Credentials.convertToPem(cert);
2729            if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
2730            return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
2731
2732        } catch (IOException e1) {
2733            return false;
2734        } catch (CertificateException e2) {
2735            return false;
2736        }
2737    }
2738
2739    void removeKeys(WifiEnterpriseConfig config) {
2740        String client = config.getClientCertificateAlias();
2741        // a valid client certificate is configured
2742        if (!TextUtils.isEmpty(client)) {
2743            if (DBG) Log.d(TAG, "removing client private key and user cert");
2744            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
2745            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
2746        }
2747
2748        String ca = config.getCaCertificateAlias();
2749        // a valid ca certificate is configured
2750        if (!TextUtils.isEmpty(ca)) {
2751            if (DBG) Log.d(TAG, "removing CA cert");
2752            mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
2753        }
2754    }
2755
2756
2757    /** Migrates the old style TLS config to the new config style. This should only be used
2758     * when restoring an old wpa_supplicant.conf or upgrading from a previous
2759     * platform version.
2760     * @return true if the config was updated
2761     * @hide
2762     */
2763    boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
2764        String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
2765        /*
2766         * If the old configuration value is not present, then there is nothing
2767         * to do.
2768         */
2769        if (TextUtils.isEmpty(oldPrivateKey)) {
2770            return false;
2771        } else {
2772            // Also ignore it if it's empty quotes.
2773            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
2774            if (TextUtils.isEmpty(oldPrivateKey)) {
2775                return false;
2776            }
2777        }
2778
2779        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
2780        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
2781                WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
2782
2783        /*
2784        * The old key started with the keystore:// URI prefix, but we don't
2785        * need that anymore. Trim it off if it exists.
2786        */
2787        final String keyName;
2788        if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
2789            keyName = new String(
2790                    oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
2791        } else {
2792            keyName = oldPrivateKey;
2793        }
2794        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
2795
2796        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
2797                config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
2798
2799        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
2800                config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
2801
2802        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
2803                config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
2804
2805        // Remove old private_key string so we don't run this again.
2806        mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
2807
2808        return true;
2809    }
2810
2811    /** Migrate certs from global pool to wifi UID if not already done */
2812    void migrateCerts(WifiEnterpriseConfig config) {
2813        String client = config.getClientCertificateAlias();
2814        // a valid client certificate is configured
2815        if (!TextUtils.isEmpty(client)) {
2816            if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
2817                mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
2818                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
2819                mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
2820                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
2821            }
2822        }
2823
2824        String ca = config.getCaCertificateAlias();
2825        // a valid ca certificate is configured
2826        if (!TextUtils.isEmpty(ca)) {
2827            if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
2828                mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
2829                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
2830            }
2831        }
2832    }
2833
2834}
2835