WifiConfigStore.java revision e6574ec7b6b2e7a678da7f77bdaaf31463852b2f
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.provider.Settings;
48import android.security.Credentials;
49import android.security.KeyChain;
50import android.security.KeyStore;
51import android.text.TextUtils;
52import android.util.LocalLog;
53import android.util.Log;
54import android.util.SparseArray;
55
56import com.android.server.net.DelayedDiskWrite;
57import com.android.server.net.IpConfigStore;
58
59import java.io.BufferedReader;
60import java.io.BufferedInputStream;
61import java.io.DataInputStream;
62import java.io.DataOutputStream;
63import java.io.EOFException;
64import java.io.File;
65import java.io.FileDescriptor;
66import java.io.FileInputStream;
67import java.io.FileNotFoundException;
68import java.io.FileReader;
69import java.io.IOException;
70import java.io.PrintWriter;
71import java.math.BigInteger;
72import java.net.InetAddress;
73import java.nio.charset.Charset;
74import java.security.PrivateKey;
75import java.security.cert.Certificate;
76import java.security.cert.CertificateException;
77import java.text.SimpleDateFormat;
78import java.text.DateFormat;
79import java.util.regex.Matcher;
80import java.util.regex.Pattern;
81import java.util.*;
82
83/**
84 * This class provides the API to manage configured
85 * wifi networks. The API is not thread safe is being
86 * used only from WifiStateMachine.
87 *
88 * It deals with the following
89 * - Add/update/remove a WifiConfiguration
90 *   The configuration contains two types of information.
91 *     = IP and proxy configuration that is handled by WifiConfigStore and
92 *       is saved to disk on any change.
93 *
94 *       The format of configuration file is as follows:
95 *       <version>
96 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
97 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
98 *       ..
99 *
100 *       (key, value) pairs for a given network are grouped together and can
101 *       be in any order. A EOS at the end of a set of (key, value) pairs
102 *       indicates that the next set of (key, value) pairs are for a new
103 *       network. A network is identified by a unique ID_KEY. If there is no
104 *       ID_KEY in the (key, value) pairs, the data is discarded.
105 *
106 *       An invalid version on read would result in discarding the contents of
107 *       the file. On the next write, the latest version is written to file.
108 *
109 *       Any failures during read or write to the configuration file are ignored
110 *       without reporting to the user since the likelihood of these errors are
111 *       low and the impact on connectivity is low.
112 *
113 *     = SSID & security details that is pushed to the supplicant.
114 *       supplicant saves these details to the disk on calling
115 *       saveConfigCommand().
116 *
117 *       We have two kinds of APIs exposed:
118 *        > public API calls that provide fine grained control
119 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
120 *          removeNetwork(). For these calls, the config is not persisted
121 *          to the disk. (TODO: deprecate these calls in WifiManager)
122 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
123 *          These calls persist the supplicant config to disk.
124 *
125 * - Maintain a list of configured networks for quick access
126 *
127 */
128public class WifiConfigStore extends IpConfigStore {
129
130    private Context mContext;
131    private static final String TAG = "WifiConfigStore";
132    private static final boolean DBG = true;
133    private static boolean VDBG = false;
134
135    private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
136
137    /* configured networks with network id as the key */
138    private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
139            new HashMap<Integer, WifiConfiguration>();
140
141    /* A network id is a unique identifier for a network configured in the
142     * supplicant. Network ids are generated when the supplicant reads
143     * the configuration file at start and can thus change for networks.
144     * We store the IP configuration for networks along with a unique id
145     * that is generated from SSID and security type of the network. A mapping
146     * from the generated unique id to network id of the network is needed to
147     * map supplicant config to IP configuration. */
148    private HashMap<Integer, Integer> mNetworkIds =
149            new HashMap<Integer, Integer>();
150
151    /* Tracks the highest priority of configured networks */
152    private int mLastPriority = -1;
153
154    private static final String ipConfigFile = Environment.getDataDirectory() +
155            "/misc/wifi/ipconfig.txt";
156
157    private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
158            "/misc/wifi/networkHistory.txt";
159
160    private static final String autoJoinConfigFile = Environment.getDataDirectory() +
161            "/misc/wifi/autojoinconfig.txt";
162
163    /* Network History Keys */
164    private static final String SSID_KEY = "SSID:  ";
165    private static final String CONFIG_KEY = "CONFIG:  ";
166    private static final String CHOICE_KEY = "CHOICE:  ";
167    private static final String LINK_KEY = "LINK:  ";
168    private static final String BSSID_KEY = "BSSID:  ";
169    private static final String BSSID_KEY_END = "/BSSID:  ";
170    private static final String RSSI_KEY = "RSSI:  ";
171    private static final String FREQ_KEY = "FREQ:  ";
172    private static final String DATE_KEY = "DATE:  ";
173    private static final String MILLI_KEY = "MILLI:  ";
174    private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI:  ";
175    private static final String NETWORK_ID_KEY = "ID:  ";
176    private static final String PRIORITY_KEY = "PRIORITY:  ";
177    private static final String DEFAULT_GW_KEY = "DEFAULT_GW:  ";
178    private static final String AUTH_KEY = "AUTH:  ";
179    private static final String SEPARATOR_KEY = "\n";
180    private static final String STATUS_KEY = "AUTO_JOIN_STATUS:  ";
181    private static final String BSSID_STATUS_KEY = "BSSID_STATUS:  ";
182    private static final String SELF_ADDED_KEY = "SELF_ADDED:  ";
183    private static final String FAILURE_KEY = "FAILURE:  ";
184    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD:  ";
185    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION:  ";
186    private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY:  ";
187    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY:  ";
188    private static final String UPDATE_UID_KEY = "UPDATE_UID:  ";
189    private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS:  ";
190    private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON:  ";
191    private static final String FQDN_KEY = "FQDN:  ";
192    private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES:  ";
193    private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE:  ";
194    private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH:  ";
195    private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION:  ";
196    private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
197            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G:  ";
198    private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
199            = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G:  ";
200    private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
201            = "THRESHOLD_UNBLACKLIST_HARD_5G:  ";
202    private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
203            = "THRESHOLD_UNBLACKLIST_SOFT_5G:  ";
204    private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
205            = "THRESHOLD_UNBLACKLIST_HARD_24G:  ";
206    private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
207            = "THRESHOLD_UNBLACKLIST_SOFT_24G:  ";
208    private static final String THRESHOLD_GOOD_RSSI_5_KEY
209            = "THRESHOLD_GOOD_RSSI_5:  ";
210    private static final String THRESHOLD_LOW_RSSI_5_KEY
211            = "THRESHOLD_LOW_RSSI_5:  ";
212    private static final String THRESHOLD_BAD_RSSI_5_KEY
213            = "THRESHOLD_BAD_RSSI_5:  ";
214    private static final String THRESHOLD_GOOD_RSSI_24_KEY
215            = "THRESHOLD_GOOD_RSSI_24:  ";
216    private static final String THRESHOLD_LOW_RSSI_24_KEY
217            = "THRESHOLD_LOW_RSSI_24:  ";
218    private static final String THRESHOLD_BAD_RSSI_24_KEY
219            = "THRESHOLD_BAD_RSSI_24:  ";
220    private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
221            = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING:   ";
222    private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
223            = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING:   ";
224
225    private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
226            "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW:   ";
227    private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
228            "A_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
229    private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
230            "G_BAND_PREFERENCE_RSSI_THRESHOLD:   ";
231
232    private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
233            = "ENABLE_AUTOJOIN_WHILE_ASSOCIATED:   ";
234
235    public boolean enableAutoJoinWhileAssociated = true;
236
237    public int maxTxPacketForNetworkSwitching = 40;
238    public int maxRxPacketForNetworkSwitching = 80;
239
240    public int thresholdInitialAutoJoinAttemptMin5RSSI
241            = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5;
242    public int thresholdInitialAutoJoinAttemptMin24RSSI
243            = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24;
244
245    public int thresholdBadRssi5 = WifiConfiguration.BAD_RSSI_5;
246    public int thresholdLowRssi5 = WifiConfiguration.LOW_RSSI_5;
247    public int thresholdGoodRssi5 = WifiConfiguration.GOOD_RSSI_5;
248    public int thresholdBadRssi24 = WifiConfiguration.BAD_RSSI_24;
249    public int thresholdLowRssi24 = WifiConfiguration.LOW_RSSI_24;
250    public int thresholdGoodRssi24 = WifiConfiguration.GOOD_RSSI_24;
251
252    public int thresholdBandPreferenceRssi24
253            = WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD;
254    public int thresholdBandPreferenceRssi5
255            = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD;
256    public int thresholdBandPreferenceLowRssi5
257            = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW;
258
259    public int thresholdUnblacklistThreshold5Hard
260            = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD;
261    public int thresholdUnblacklistThreshold5Soft
262            = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT;
263    public int thresholdUnblacklistThreshold24Hard
264            = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD;
265    public int thresholdUnblacklistThreshold24Soft
266            = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT;
267
268    /**
269     * Regex pattern for extracting a connect choice.
270     * Matches a strings like the following:
271     * <configKey>=([0:9]+)
272     */
273    private static Pattern mConnectChoice =
274            Pattern.compile("(.*)=([0-9]+)");
275
276
277    /* Enterprise configuration keys */
278    /**
279     * In old configurations, the "private_key" field was used. However, newer
280     * configurations use the key_id field with the engine_id set to "keystore".
281     * If this field is found in the configuration, the migration code is
282     * triggered.
283     */
284    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
285
286    /**
287     * This represents an empty value of an enterprise field.
288     * NULL is used at wpa_supplicant to indicate an empty value
289     */
290    static final String EMPTY_VALUE = "NULL";
291
292    // Internal use only
293    private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
294            WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
295            WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
296            WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
297            WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
298            WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
299            WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
300
301
302    /**
303     * The maximum number of times we will retry a connection to an access point
304     * for which we have failed in acquiring an IP address from DHCP. A value of
305     * N means that we will make N+1 connection attempts in all.
306     * <p>
307     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
308     * value if a Settings value is not present.
309     */
310    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
311
312
313    private final LocalLog mLocalLog;
314    private final WpaConfigFileObserver mFileObserver;
315
316    private WifiNative mWifiNative;
317    private final KeyStore mKeyStore = KeyStore.getInstance();
318
319    /**
320     * The lastSelectedConfiguration is used to remember which network
321     * was selected last by the user.
322     * The connection to this network may not be successful, as well
323     * the selection (i.e. network priority) might not be persisted.
324     * WiFi state machine is the only object that sets this variable.
325     */
326    private String lastSelectedConfiguration = null;
327
328    WifiConfigStore(Context c, WifiNative wn) {
329        mContext = c;
330        mWifiNative = wn;
331
332        if (VDBG) {
333            mLocalLog = mWifiNative.getLocalLog();
334            mFileObserver = new WpaConfigFileObserver();
335            mFileObserver.startWatching();
336        } else {
337            mLocalLog = null;
338            mFileObserver = null;
339        }
340    }
341
342    void enableVerboseLogging(int verbose) {
343        if (verbose > 0) {
344            VDBG = true;
345        } else {
346            VDBG = false;
347        }
348    }
349
350    class WpaConfigFileObserver extends FileObserver {
351
352        public WpaConfigFileObserver() {
353            super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
354        }
355
356        @Override
357        public void onEvent(int event, String path) {
358            if (event == CLOSE_WRITE) {
359                File file = new File(SUPPLICANT_CONFIG_FILE);
360                if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
361            }
362        }
363    }
364
365
366    /**
367     * Fetch the list of configured networks
368     * and enable all stored networks in supplicant.
369     */
370    void loadAndEnableAllNetworks() {
371        if (DBG) log("Loading config and enabling all networks ");
372        loadConfiguredNetworks();
373        enableAllNetworks();
374    }
375
376    int getConfiguredNetworksSize() {
377        return mConfiguredNetworks.size();
378    }
379
380    private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) {
381        List<WifiConfiguration> networks = new ArrayList<>();
382        for(WifiConfiguration config : mConfiguredNetworks.values()) {
383            WifiConfiguration newConfig = new WifiConfiguration(config);
384            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
385                //do not enumerate and return this configuration to any one,
386                //for instance WiFi Picker.
387                //instead treat it as unknown. the configuration can still be retrieved
388                //directly by the key or networkId
389                continue;
390            }
391            if (pskMap != null && config.allowedKeyManagement != null
392                    && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
393                    && pskMap.containsKey(config.SSID)) {
394                newConfig.preSharedKey = pskMap.get(config.SSID);
395            }
396            networks.add(newConfig);
397        }
398        return networks;
399    }
400
401    /**
402     * Fetch the list of currently configured networks
403     * @return List of networks
404     */
405    List<WifiConfiguration> getConfiguredNetworks() {
406        return getConfiguredNetworks(null);
407    }
408
409    /**
410     * Fetch the list of currently configured networks, filled with real preSharedKeys
411     * @return List of networks
412     */
413    List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
414        Map<String, String> pskMap = getCredentialsBySsidMap();
415        return getConfiguredNetworks(pskMap);
416    }
417
418    /**
419     * Fetch the preSharedKeys for all networks.
420     * @return a map from Ssid to preSharedKey.
421     */
422    private Map<String, String> getCredentialsBySsidMap() {
423        return readNetworkVariablesFromSupplicantFile("psk");
424    }
425
426    /**
427     * Fetch the list of currently configured networks that were recently seen
428     *
429     * @return List of networks
430     */
431    List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
432        List<WifiConfiguration> networks = null;
433
434        for (WifiConfiguration config : mConfiguredNetworks.values()) {
435            if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
436                // Do not enumerate and return this configuration to any one,
437                // instead treat it as unknown. the configuration can still be retrieved
438                // directly by the key or networkId
439                continue;
440            }
441
442            // Calculate the RSSI for scan results that are more recent than milli
443            config.setVisibility(milli);
444
445            if (config.visibility == null) {
446                continue;
447            }
448            if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
449                    config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
450                continue;
451            }
452            if (networks == null)
453                networks = new ArrayList<WifiConfiguration>();
454            if (copy) {
455                networks.add(new WifiConfiguration(config));
456            } else {
457                networks.add(config);
458            }
459        }
460        return networks;
461    }
462
463    /**
464     *  Update the configuration and BSSID with latest RSSI value.
465     */
466    void updateConfiguration(WifiInfo info) {
467        WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
468        if (config != null && config.scanResultCache != null) {
469            ScanResult result = config.scanResultCache.get(info.getBSSID());
470            if (result != null) {
471                long previousSeen = result.seen;
472                int previousRssi = result.level;
473
474                // Update the scan result
475                result.seen = System.currentTimeMillis();
476                result.level = info.getRssi();
477
478                // Average the RSSI value
479                result.averageRssi(previousRssi, previousSeen,
480                        WifiAutoJoinController.mScanResultMaximumAge);
481                if (VDBG) {
482                    loge("updateConfiguration freq=" + result.frequency
483                        + " BSSID=" + result.BSSID
484                        + " RSSI=" + result.level
485                        + " " + config.configKey());
486                }
487            }
488        }
489    }
490
491    /**
492     * get the Wificonfiguration for this netId
493     *
494     * @return Wificonfiguration
495     */
496    WifiConfiguration getWifiConfiguration(int netId) {
497        if (mConfiguredNetworks == null)
498            return null;
499        return mConfiguredNetworks.get(netId);
500    }
501
502    /**
503     * Get the Wificonfiguration for this key
504     * @return Wificonfiguration
505     */
506    WifiConfiguration getWifiConfiguration(String key) {
507        if (key == null)
508            return null;
509        int hash = key.hashCode();
510        if (mNetworkIds == null)
511            return null;
512        Integer n = mNetworkIds.get(hash);
513        if (n == null)
514            return null;
515        int netId = n.intValue();
516        return getWifiConfiguration(netId);
517    }
518
519    /**
520     * Enable all networks and save config. This will be a no-op if the list
521     * of configured networks indicates all networks as being enabled
522     */
523    void enableAllNetworks() {
524        boolean networkEnabledStateChanged = false;
525        for(WifiConfiguration config : mConfiguredNetworks.values()) {
526            if(config != null && config.status == Status.DISABLED
527                    && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) {
528                if(mWifiNative.enableNetwork(config.networkId, false)) {
529                    networkEnabledStateChanged = true;
530                    config.status = Status.ENABLED;
531                } else {
532                    loge("Enable network failed on " + config.networkId);
533                }
534            }
535        }
536
537        if (networkEnabledStateChanged) {
538            mWifiNative.saveConfig();
539            sendConfiguredNetworksChangedBroadcast();
540        }
541    }
542
543
544    /**
545     * Selects the specified network for connection. This involves
546     * updating the priority of all the networks and enabling the given
547     * network while disabling others.
548     *
549     * Selecting a network will leave the other networks disabled and
550     * a call to enableAllNetworks() needs to be issued upon a connection
551     * or a failure event from supplicant
552     *
553     * @param netId network to select for connection
554     * @return false if the network id is invalid
555     */
556    boolean selectNetwork(int netId) {
557        if (VDBG) localLog("selectNetwork", netId);
558        if (netId == INVALID_NETWORK_ID) return false;
559
560        // Reset the priority of each network at start or if it goes too high.
561        if (mLastPriority == -1 || mLastPriority > 1000000) {
562            for(WifiConfiguration config : mConfiguredNetworks.values()) {
563                if (config.networkId != INVALID_NETWORK_ID) {
564                    config.priority = 0;
565                    addOrUpdateNetworkNative(config);
566                }
567            }
568            mLastPriority = 0;
569        }
570
571        // Set to the highest priority and save the configuration.
572        WifiConfiguration config = new WifiConfiguration();
573        config.networkId = netId;
574        config.priority = ++mLastPriority;
575
576        addOrUpdateNetworkNative(config);
577        mWifiNative.saveConfig();
578
579        /* Enable the given network while disabling all other networks */
580        enableNetworkWithoutBroadcast(netId, true);
581
582       /* Avoid saving the config & sending a broadcast to prevent settings
583        * from displaying a disabled list of networks */
584        return true;
585    }
586
587    /**
588     * Add/update the specified configuration and save config
589     *
590     * @param config WifiConfiguration to be saved
591     * @return network update result
592     */
593    NetworkUpdateResult saveNetwork(WifiConfiguration config) {
594        WifiConfiguration conf;
595
596        // A new network cannot have null SSID
597        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
598                config.SSID == null)) {
599            return new NetworkUpdateResult(INVALID_NETWORK_ID);
600        }
601        if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
602        if (VDBG) {
603            loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
604                    + " SSID=" + config.SSID
605                    + " Uid=" + Integer.toString(config.creatorUid)
606                    + "/" + Integer.toString(config.lastUpdateUid));
607        }
608        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
609        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
610        int netId = result.getNetworkId();
611
612        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
613
614        /* enable a new network */
615        if (newNetwork && netId != INVALID_NETWORK_ID) {
616            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
617
618            mWifiNative.enableNetwork(netId, false);
619            conf = mConfiguredNetworks.get(netId);
620            if (conf != null)
621                conf.status = Status.ENABLED;
622        }
623
624        conf = mConfiguredNetworks.get(netId);
625        if (conf != null) {
626            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
627                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
628
629                // reenable autojoin, since new information has been provided
630                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
631                enableNetworkWithoutBroadcast(conf.networkId, false);
632            }
633            if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId="
634                    + Integer.toString(netId)
635                    + " uid=" + Integer.toString(config.creatorUid));
636        }
637
638        mWifiNative.saveConfig();
639        sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
640                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
641        return result;
642    }
643
644    void updateStatus(int netId, DetailedState state) {
645        if (netId != INVALID_NETWORK_ID) {
646            WifiConfiguration config = mConfiguredNetworks.get(netId);
647            if (config == null) return;
648            switch (state) {
649                case CONNECTED:
650                    config.status = Status.CURRENT;
651                    //we successfully connected, hence remove the blacklist
652                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
653                    break;
654                case DISCONNECTED:
655                    //If network is already disabled, keep the status
656                    if (config.status == Status.CURRENT) {
657                        config.status = Status.ENABLED;
658                    }
659                    break;
660                default:
661                    //do nothing, retain the existing state
662                    break;
663            }
664        }
665    }
666
667    /**
668     * Forget the specified network and save config
669     *
670     * @param netId network to forget
671     * @return {@code true} if it succeeds, {@code false} otherwise
672     */
673    boolean forgetNetwork(int netId) {
674        if (VDBG) localLog("forgetNetwork", netId);
675
676        boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
677        if (!remove) {
678            //success but we dont want to remove the network from supplicant conf file
679            return true;
680        }
681        if (mWifiNative.removeNetwork(netId)) {
682            mWifiNative.saveConfig();
683            return true;
684        } else {
685            loge("Failed to remove network " + netId);
686            return false;
687        }
688    }
689
690    /**
691     * Add/update a network. Note that there is no saveConfig operation.
692     * This function is retained for compatibility with the public
693     * API. The more powerful saveNetwork() is used by the
694     * state machine
695     *
696     * @param config wifi configuration to add/update
697     * @return network Id
698     */
699    int addOrUpdateNetwork(WifiConfiguration config) {
700        if (VDBG) localLog("addOrUpdateNetwork id=", config.networkId);
701        //adding unconditional message to chase b/15111865
702        Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
703                + " uid=" + Integer.toString(config.creatorUid)
704                + "/" + Integer.toString(config.lastUpdateUid));
705        NetworkUpdateResult result = addOrUpdateNetworkNative(config);
706        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
707            WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
708            if (conf != null) {
709                sendConfiguredNetworksChangedBroadcast(conf,
710                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
711                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
712            }
713        }
714        return result.getNetworkId();
715    }
716
717    /**
718     * Remove a network. Note that there is no saveConfig operation.
719     * This function is retained for compatibility with the public
720     * API. The more powerful forgetNetwork() is used by the
721     * state machine for network removal
722     *
723     * @param netId network to be removed
724     * @return {@code true} if it succeeds, {@code false} otherwise
725     */
726    boolean removeNetwork(int netId) {
727        if (VDBG) localLog("removeNetwork", netId);
728        boolean ret = mWifiNative.removeNetwork(netId);
729        if (ret) {
730            removeConfigAndSendBroadcastIfNeeded(netId);
731        }
732        return ret;
733    }
734
735    private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
736        boolean remove = true;
737        WifiConfiguration config = mConfiguredNetworks.get(netId);
738        if (config != null) {
739            if (VDBG) {
740                loge("removeNetwork " + Integer.toString(netId) + " key=" +
741                        config.configKey() + " config.id=" + Integer.toString(config.networkId));
742            }
743
744            // cancel the last user choice
745            if (config.configKey().equals(lastSelectedConfiguration)) {
746                lastSelectedConfiguration = null;
747            }
748
749            // Remove any associated keys
750            if (config.enterpriseConfig != null) {
751                removeKeys(config.enterpriseConfig);
752            }
753
754            if (config.didSelfAdd) {
755                if (config.peerWifiConfiguration != null) {
756                    for (WifiConfiguration peer : mConfiguredNetworks.values()) {
757                        if (config.peerWifiConfiguration.equals(peer.configKey())) {
758                            /* the configuration that trigger the add is still there */
759                            remove = false;
760                        }
761                    }
762                } else {
763                    loge("removeNetwork " + Integer.toString(netId)
764                            + " key=" + config.configKey()
765                            + " config.id=" + Integer.toString(config.networkId)
766                            + " didSelfAdd and null peerWifiConfiguration"
767                            + " -> dont remove");
768                    remove = false;
769                }
770            }
771
772            if (remove) {
773                mConfiguredNetworks.remove(netId);
774                mNetworkIds.remove(configKey(config));
775            } else {
776                /* we can't directly remove the configuration since we added it ourselves, because
777                 * that could cause the system to re-add it right away.
778                 * Instead black list it. It will be unblacklisted only thru a new add.
779                 */
780                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DELETED);
781                mWifiNative.disableNetwork(config.networkId);
782            }
783
784            writeIpAndProxyConfigurations();
785            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
786            writeKnownNetworkHistory();
787        }
788        return remove;
789    }
790
791    /**
792     * Enable a network. Note that there is no saveConfig operation.
793     * This function is retained for compatibility with the public
794     * API. The more powerful selectNetwork()/saveNetwork() is used by the
795     * state machine for connecting to a network
796     *
797     * @param netId network to be enabled
798     * @return {@code true} if it succeeds, {@code false} otherwise
799     */
800    boolean enableNetwork(int netId, boolean disableOthers) {
801        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
802        if (disableOthers) {
803            if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
804            sendConfiguredNetworksChangedBroadcast();
805        } else {
806            if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
807            WifiConfiguration enabledNetwork = null;
808            synchronized(mConfiguredNetworks) {
809                enabledNetwork = mConfiguredNetworks.get(netId);
810            }
811            // check just in case the network was removed by someone else.
812            if (enabledNetwork != null) {
813                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
814                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
815            }
816        }
817        return ret;
818    }
819
820    boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
821        boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
822
823        WifiConfiguration config = mConfiguredNetworks.get(netId);
824        if (config != null) config.status = Status.ENABLED;
825
826        if (disableOthers) {
827            markAllNetworksDisabledExcept(netId);
828        }
829        return ret;
830    }
831
832    void disableAllNetworks() {
833        if (VDBG) localLog("disableAllNetworks");
834        boolean networkDisabled = false;
835        for(WifiConfiguration config : mConfiguredNetworks.values()) {
836            if(config != null && config.status != Status.DISABLED) {
837                if(mWifiNative.disableNetwork(config.networkId)) {
838                    networkDisabled = true;
839                    config.status = Status.DISABLED;
840                } else {
841                    loge("Disable network failed on " + config.networkId);
842                }
843            }
844        }
845
846        if (networkDisabled) {
847            sendConfiguredNetworksChangedBroadcast();
848        }
849    }
850    /**
851     * Disable a network. Note that there is no saveConfig operation.
852     * @param netId network to be disabled
853     * @return {@code true} if it succeeds, {@code false} otherwise
854     */
855    boolean disableNetwork(int netId) {
856        return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
857    }
858
859    /**
860     * Disable a network. Note that there is no saveConfig operation.
861     * @param netId network to be disabled
862     * @param reason reason code network was disabled
863     * @return {@code true} if it succeeds, {@code false} otherwise
864     */
865    boolean disableNetwork(int netId, int reason) {
866        if (VDBG) localLog("disableNetwork", netId);
867        boolean ret = mWifiNative.disableNetwork(netId);
868        WifiConfiguration network = null;
869        WifiConfiguration config = mConfiguredNetworks.get(netId);
870
871        if (VDBG) {
872            if (config != null) {
873                loge("disableNetwork netId=" + Integer.toString(netId)
874                        + " SSID=" + config.SSID
875                        + " disabled=" + (config.status == Status.DISABLED)
876                        + " reason=" + Integer.toString(config.disableReason));
877            }
878        }
879        /* Only change the reason if the network was not previously disabled */
880        if (config != null && config.status != Status.DISABLED) {
881            config.status = Status.DISABLED;
882            config.disableReason = reason;
883            network = config;
884        }
885        if (network != null) {
886            sendConfiguredNetworksChangedBroadcast(network,
887                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
888        }
889        return ret;
890    }
891
892    /**
893     * Save the configured networks in supplicant to disk
894     * @return {@code true} if it succeeds, {@code false} otherwise
895     */
896    boolean saveConfig() {
897        return mWifiNative.saveConfig();
898    }
899
900    /**
901     * Start WPS pin method configuration with pin obtained
902     * from the access point
903     * @param config WPS configuration
904     * @return Wps result containing status and pin
905     */
906    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
907        WpsResult result = new WpsResult();
908        if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
909            /* WPS leaves all networks disabled */
910            markAllNetworksDisabled();
911            result.status = WpsResult.Status.SUCCESS;
912        } else {
913            loge("Failed to start WPS pin method configuration");
914            result.status = WpsResult.Status.FAILURE;
915        }
916        return result;
917    }
918
919    /**
920     * Start WPS pin method configuration with pin obtained
921     * from the device
922     * @return WpsResult indicating status and pin
923     */
924    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
925        WpsResult result = new WpsResult();
926        result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
927        /* WPS leaves all networks disabled */
928        if (!TextUtils.isEmpty(result.pin)) {
929            markAllNetworksDisabled();
930            result.status = WpsResult.Status.SUCCESS;
931        } else {
932            loge("Failed to start WPS pin method configuration");
933            result.status = WpsResult.Status.FAILURE;
934        }
935        return result;
936    }
937
938    /**
939     * Start WPS push button configuration
940     * @param config WPS configuration
941     * @return WpsResult indicating status and pin
942     */
943    WpsResult startWpsPbc(WpsInfo config) {
944        WpsResult result = new WpsResult();
945        if (mWifiNative.startWpsPbc(config.BSSID)) {
946            /* WPS leaves all networks disabled */
947            markAllNetworksDisabled();
948            result.status = WpsResult.Status.SUCCESS;
949        } else {
950            loge("Failed to start WPS push button configuration");
951            result.status = WpsResult.Status.FAILURE;
952        }
953        return result;
954    }
955
956    /**
957     * Fetch the link properties for a given network id
958     *
959     * @return LinkProperties for the given network id
960     */
961    LinkProperties getLinkProperties(int netId) {
962        WifiConfiguration config = mConfiguredNetworks.get(netId);
963        if (config != null) return new LinkProperties(config.getLinkProperties());
964        return null;
965    }
966
967    /**
968     * set IP configuration for a given network id
969     */
970    void setLinkProperties(int netId, LinkProperties linkProperties) {
971        WifiConfiguration config = mConfiguredNetworks.get(netId);
972        if (config != null) {
973            // add old proxy details - TODO - is this still needed?
974            if(config.getLinkProperties() != null) {
975                linkProperties.setHttpProxy(config.getLinkProperties().getHttpProxy());
976            }
977            config.setLinkProperties(linkProperties);
978        }
979    }
980
981    /**
982     * set default GW MAC address
983     */
984    void setDefaultGwMacAddress(int netId, String macAddress) {
985        WifiConfiguration config = mConfiguredNetworks.get(netId);
986        if (config != null) {
987            //update defaultGwMacAddress
988            config.defaultGwMacAddress = macAddress;
989        }
990    }
991
992
993    /**
994     * clear IP configuration for a given network id
995     * @param network id
996     */
997    void clearLinkProperties(int netId) {
998        WifiConfiguration config = mConfiguredNetworks.get(netId);
999        if (config != null && config.getLinkProperties() != null) {
1000            // Clear everything except proxy
1001            ProxyInfo proxy = config.getLinkProperties().getHttpProxy();
1002            config.getLinkProperties().clear();
1003            config.getLinkProperties().setHttpProxy(proxy);
1004        }
1005    }
1006
1007
1008    /**
1009     * Fetch the proxy properties for a given network id
1010     * @param network id
1011     * @return ProxyInfo for the network id
1012     */
1013    ProxyInfo getProxyProperties(int netId) {
1014        LinkProperties linkProperties = getLinkProperties(netId);
1015        if (linkProperties != null) {
1016            return new ProxyInfo(linkProperties.getHttpProxy());
1017        }
1018        return null;
1019    }
1020
1021    /**
1022     * Return if the specified network is using static IP
1023     * @param network id
1024     * @return {@code true} if using static ip for netId
1025     */
1026    boolean isUsingStaticIp(int netId) {
1027        WifiConfiguration config = mConfiguredNetworks.get(netId);
1028        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
1029            return true;
1030        }
1031        return false;
1032    }
1033
1034    /**
1035     * Should be called when a single network configuration is made.
1036     * @param network The network configuration that changed.
1037     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
1038     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
1039     */
1040    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
1041            int reason) {
1042        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1043        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1044        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
1045        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
1046        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
1047        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1048    }
1049
1050    /**
1051     * Should be called when multiple network configuration changes are made.
1052     */
1053    private void sendConfiguredNetworksChangedBroadcast() {
1054        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1055        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1056        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
1057        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1058    }
1059
1060    void loadConfiguredNetworks() {
1061        String listStr = mWifiNative.listNetworks();
1062        mLastPriority = 0;
1063
1064        mConfiguredNetworks.clear();
1065        mNetworkIds.clear();
1066
1067        if (listStr == null)
1068            return;
1069
1070        String[] lines = listStr.split("\n");
1071
1072        if (VDBG) {
1073            loge("loadConfiguredNetworks: found " + Integer.toString(lines.length)
1074                    + " networks", true);
1075        }
1076
1077        // Skip the first line, which is a header
1078        for (int i = 1; i < lines.length; i++) {
1079            String[] result = lines[i].split("\t");
1080            // network-id | ssid | bssid | flags
1081            WifiConfiguration config = new WifiConfiguration();
1082            try {
1083                config.networkId = Integer.parseInt(result[0]);
1084            } catch(NumberFormatException e) {
1085                loge("Failed to read network-id '" + result[0] + "'");
1086                continue;
1087            }
1088            if (result.length > 3) {
1089                if (result[3].indexOf("[CURRENT]") != -1)
1090                    config.status = WifiConfiguration.Status.CURRENT;
1091                else if (result[3].indexOf("[DISABLED]") != -1)
1092                    config.status = WifiConfiguration.Status.DISABLED;
1093                else
1094                    config.status = WifiConfiguration.Status.ENABLED;
1095            } else {
1096                config.status = WifiConfiguration.Status.ENABLED;
1097            }
1098            readNetworkVariables(config);
1099            if (config.priority > mLastPriority) {
1100                mLastPriority = config.priority;
1101            }
1102
1103            config.setIpAssignment(IpAssignment.DHCP);
1104            config.setProxySettings(ProxySettings.NONE);
1105
1106            if (mNetworkIds.containsKey(configKey(config))) {
1107                // That SSID is already known, just ignore this duplicate entry
1108                if (VDBG) localLog("discarded duplicate network ", config.networkId);
1109            } else if(config.isValid()){
1110                mConfiguredNetworks.put(config.networkId, config);
1111                mNetworkIds.put(configKey(config), config.networkId);
1112                if (VDBG) localLog("loaded configured network", config.networkId);
1113            } else {
1114                if (DBG) log("Ignoring loaded configured for network " + config.networkId
1115                    + " because config are not valid");
1116            }
1117        }
1118
1119        readIpAndProxyConfigurations();
1120        readNetworkHistory();
1121        readAutoJoinConfig();
1122
1123        sendConfiguredNetworksChangedBroadcast();
1124
1125        if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
1126
1127        if (mNetworkIds.size() == 0) {
1128            // no networks? Lets log if the wpa_supplicant.conf file contents
1129            BufferedReader reader = null;
1130            try {
1131                reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1132                if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---");
1133                for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1134                    if (VDBG) localLog(line);
1135                }
1136                if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---");
1137            } catch (FileNotFoundException e) {
1138                if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1139            } catch (IOException e) {
1140                if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1141            } finally {
1142                try {
1143                    if (reader != null) {
1144                        reader.close();
1145                    }
1146                } catch (IOException e) {
1147                    // Just ignore the fact that we couldn't close
1148                }
1149            }
1150        }
1151    }
1152
1153    private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
1154        Map<String, String> result = new HashMap<>();
1155        BufferedReader reader = null;
1156        if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key);
1157
1158        try {
1159            reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1160            boolean found = false;
1161            String networkSsid = null;
1162            String value = null;
1163
1164            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1165                if (VDBG) loge(line);
1166
1167                if (line.matches("[ \\t]*network=\\{")) {
1168                    found = true;
1169                    networkSsid = null;
1170                    value = null;
1171                } else if (line.matches("[ \\t]*\\{")) {
1172                    found = false;
1173                    networkSsid = null;
1174                    value = null;
1175                }
1176
1177                if (found) {
1178                    int index;
1179                    if ((index = line.indexOf("ssid=")) >= 0) {
1180                        networkSsid = line.substring(index + 5);
1181                    } else if ((index = line.indexOf(key + "=")) >= 0) {
1182                        value = line.substring(index + key.length() + 1);
1183                    }
1184
1185                    if (networkSsid != null && value != null) {
1186                        result.put(networkSsid, value);
1187                    }
1188                }
1189            }
1190        } catch (FileNotFoundException e) {
1191            if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1192        } catch (IOException e) {
1193            if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1194        } finally {
1195            try {
1196                if (reader != null) {
1197                    reader.close();
1198                }
1199            } catch (IOException e) {
1200                // Just ignore the fact that we couldn't close
1201            }
1202        }
1203
1204        return result;
1205    }
1206
1207    private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
1208        Map<String, String> data = readNetworkVariablesFromSupplicantFile(key);
1209        if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key);
1210        return data.get(ssid);
1211    }
1212
1213    /* Mark all networks except specified netId as disabled */
1214    private void markAllNetworksDisabledExcept(int netId) {
1215        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1216            if(config != null && config.networkId != netId) {
1217                if (config.status != Status.DISABLED) {
1218                    config.status = Status.DISABLED;
1219                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
1220                }
1221            }
1222        }
1223    }
1224
1225    private void markAllNetworksDisabled() {
1226        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
1227    }
1228
1229    boolean needsUnlockedKeyStore() {
1230
1231        // Any network using certificates to authenticate access requires
1232        // unlocked key store; unless the certificates can be stored with
1233        // hardware encryption
1234
1235        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1236
1237            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
1238                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1239
1240                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
1241                    return true;
1242                }
1243            }
1244        }
1245
1246        return false;
1247    }
1248
1249    public void writeKnownNetworkHistory() {
1250        if (VDBG) {
1251            loge(" writeKnownNetworkHistory() num networks:" +
1252                    Integer.toString(mConfiguredNetworks.size()), true);
1253        }
1254
1255        /* Make a copy */
1256        final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
1257        for (WifiConfiguration config : mConfiguredNetworks.values()) {
1258            networks.add(new WifiConfiguration(config));
1259        }
1260
1261        mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() {
1262            public void onWriteCalled(DataOutputStream out) throws IOException {
1263                for (WifiConfiguration config : networks) {
1264                    //loge("onWriteCalled write SSID: " + config.SSID);
1265                   /* if (config.getLinkProperties() != null)
1266                        loge(" lp " + config.getLinkProperties().toString());
1267                    else
1268                        loge("attempt config w/o lp");
1269                    */
1270
1271                    if (VDBG) {
1272                        int num = 0;
1273                        if (config.connectChoices != null) {
1274                            num = config.connectChoices.size();
1275                        }
1276                        loge("saving network history: " + config.configKey()  + " gw: " +
1277                                config.defaultGwMacAddress + " autojoin status: " +
1278                                config.autoJoinStatus + " ephemeral=" + config.ephemeral
1279                                + " choices:" + Integer.toString(num));
1280                    }
1281                    if (config.ephemeral == true)
1282                        continue;
1283
1284                    if (config.isValid() == false)
1285                        continue;
1286
1287                    if (config.SSID == null) {
1288                        if (VDBG) {
1289                            loge("writeKnownNetworkHistory trying to write config with null SSID");
1290                        }
1291                        continue;
1292                    }
1293
1294                    out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
1295
1296                    out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
1297                    out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY);
1298
1299                    out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
1300                    out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
1301                            + SEPARATOR_KEY);
1302                    out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
1303                            + SEPARATOR_KEY);
1304                    out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
1305                            + Integer.toString(config.disableReason)
1306                            + SEPARATOR_KEY);
1307                    out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
1308                            + SEPARATOR_KEY);
1309                    out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
1310                            + SEPARATOR_KEY);
1311                    out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
1312                            + SEPARATOR_KEY);
1313                    if (config.peerWifiConfiguration != null) {
1314                        out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
1315                                + SEPARATOR_KEY);
1316                    }
1317                    out.writeUTF(NUM_CONNECTION_FAILURES_KEY
1318                            + Integer.toString(config.numConnectionFailures)
1319                            + SEPARATOR_KEY);
1320                    out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride)
1321                            + SEPARATOR_KEY);
1322                    out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY
1323                            + Integer.toString(config.numScorerOverrideAndSwitchedNetwork)
1324                            + SEPARATOR_KEY);
1325                    out.writeUTF(NUM_ASSOCIATION_KEY
1326                            + Integer.toString(config.numAssociation)
1327                            + SEPARATOR_KEY);
1328                    out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
1329                            + SEPARATOR_KEY);
1330                    out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
1331                            + SEPARATOR_KEY);
1332                    out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
1333                            + SEPARATOR_KEY);
1334                    out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
1335                            + SEPARATOR_KEY);
1336                    String allowedKeyManagementString =
1337                            makeString(config.allowedKeyManagement,
1338                                    WifiConfiguration.KeyMgmt.strings);
1339                    out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
1340
1341                    if (config.connectChoices != null) {
1342                        for (String key : config.connectChoices.keySet()) {
1343                            Integer choice = config.connectChoices.get(key);
1344                            out.writeUTF(CHOICE_KEY + key + "="
1345                                    + choice.toString() + SEPARATOR_KEY);
1346                        }
1347                    }
1348                    if (config.linkedConfigurations != null) {
1349                        for (String key : config.linkedConfigurations.keySet()) {
1350                            out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
1351                        }
1352                    }
1353
1354                    if (config.getLinkProperties() != null) {
1355                        String macAddress = config.defaultGwMacAddress;
1356                        if (macAddress != null) {
1357                            out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
1358                        }
1359                    }
1360
1361                    if (config.scanResultCache != null) {
1362                        for (ScanResult result : config.scanResultCache.values()) {
1363                            out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
1364
1365                            out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
1366                                    + SEPARATOR_KEY);
1367
1368                            out.writeUTF(RSSI_KEY + Integer.toString(result.level)
1369                                    + SEPARATOR_KEY);
1370
1371                            out.writeUTF(BSSID_STATUS_KEY + Integer.toString(result.status)
1372                                    + SEPARATOR_KEY);
1373
1374                            if (result.seen != 0) {
1375                                out.writeUTF(MILLI_KEY + Long.toString(result.seen)
1376                                        + SEPARATOR_KEY);
1377                            }
1378                            out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
1379                        }
1380                    }
1381                    if (config.lastFailure != null) {
1382                        out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
1383                    }
1384                    out.writeUTF(SEPARATOR_KEY);
1385                }
1386            }
1387
1388        });
1389    }
1390
1391    public void setLastSelectedConfiguration(int netId) {
1392        if (DBG) {
1393            loge("setLastSelectedConfiguration " + Integer.toString(netId));
1394        }
1395        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
1396            lastSelectedConfiguration = null;
1397        } else {
1398            WifiConfiguration selected = getWifiConfiguration(netId);
1399            if (selected == null) {
1400                lastSelectedConfiguration = null;
1401            } else {
1402                lastSelectedConfiguration = selected.configKey();
1403                if (VDBG) {
1404                    loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
1405                }
1406            }
1407        }
1408    }
1409
1410    public String getLastSelectedConfiguration() {
1411        return lastSelectedConfiguration;
1412    }
1413
1414    private void readNetworkHistory() {
1415        if (VDBG) {
1416            loge("will readNetworkHistory path:" + networkHistoryConfigFile, true);
1417        }
1418        DataInputStream in = null;
1419        try {
1420            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
1421                    networkHistoryConfigFile)));
1422            WifiConfiguration config = null;
1423            while (true) {
1424                int id = -1;
1425                String key = in.readUTF();
1426                String bssid = null;
1427                String ssid = null;
1428
1429                int freq = 0;
1430                int status = 0;
1431                long seen = 0;
1432                int rssi = WifiConfiguration.INVALID_RSSI;
1433                String caps = null;
1434                if (key.startsWith(CONFIG_KEY)) {
1435
1436                    if (config != null) {
1437                        config = null;
1438                    }
1439                    String configKey = key.replace(CONFIG_KEY, "");
1440                    configKey = configKey.replace(SEPARATOR_KEY, "");
1441                    // get the networkId for that config Key
1442                    Integer n = mNetworkIds.get(configKey.hashCode());
1443                    // skip reading that configuration data
1444                    // since we don't have a corresponding network ID
1445                    if (n == null) {
1446                        loge("readNetworkHistory didnt find netid for hash="
1447                                + Integer.toString(configKey.hashCode())
1448                                + " key: " + configKey);
1449                        continue;
1450                    }
1451                    config = mConfiguredNetworks.get(n);
1452                    if (config == null) {
1453                        loge("readNetworkHistory didnt find config for netid="
1454                                + n.toString()
1455                                + " key: " + configKey);
1456                    }
1457                    status = 0;
1458                    ssid = null;
1459                    bssid = null;
1460                    freq = 0;
1461                    seen = 0;
1462                    rssi = WifiConfiguration.INVALID_RSSI;
1463                    caps = null;
1464
1465                } else if (config != null) {
1466                    if (key.startsWith(SSID_KEY)) {
1467                        ssid = key.replace(SSID_KEY, "");
1468                        ssid = ssid.replace(SEPARATOR_KEY, "");
1469                        if (config.SSID != null && !config.SSID.equals(ssid)) {
1470                            loge("Error parsing network history file, mismatched SSIDs");
1471                            config = null; //error
1472                            ssid = null;
1473                        } else {
1474                            config.SSID = ssid;
1475                        }
1476                    }
1477
1478                    if (key.startsWith(FQDN_KEY)) {
1479                        String fqdn = key.replace(FQDN_KEY, "");
1480                        fqdn = fqdn.replace(SEPARATOR_KEY, "");
1481                        config.FQDN = fqdn;
1482                    }
1483
1484                    if (key.startsWith(DEFAULT_GW_KEY)) {
1485                        String gateway = key.replace(DEFAULT_GW_KEY, "");
1486                        gateway = gateway.replace(SEPARATOR_KEY, "");
1487                        config.defaultGwMacAddress = gateway;
1488                    }
1489
1490                    if (key.startsWith(STATUS_KEY)) {
1491                        String st = key.replace(STATUS_KEY, "");
1492                        st = st.replace(SEPARATOR_KEY, "");
1493                        config.autoJoinStatus = Integer.parseInt(st);
1494                    }
1495
1496                    /*
1497                    if (key.startsWith(SUPPLICANT_STATUS_KEY)) {
1498                        String status = key.replace(SUPPLICANT_STATUS_KEY, "");
1499                        status = status.replace(SEPARATOR_KEY, "");
1500                        config.status = Integer.parseInt(status);
1501                    }
1502
1503                    if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
1504                        String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
1505                        reason = reason.replace(SEPARATOR_KEY, "");
1506                        config.disableReason = Integer.parseInt(reason);
1507                    }*/
1508
1509                    if (key.startsWith(SELF_ADDED_KEY)) {
1510                        String selfAdded = key.replace(SELF_ADDED_KEY, "");
1511                        selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
1512                        config.selfAdded = Boolean.parseBoolean(selfAdded);
1513                    }
1514
1515                    if (key.startsWith(DID_SELF_ADD_KEY)) {
1516                        String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
1517                        didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
1518                        config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
1519                    }
1520
1521                    if (key.startsWith(CREATOR_UID_KEY)) {
1522                        String uid = key.replace(CREATOR_UID_KEY, "");
1523                        uid = uid.replace(SEPARATOR_KEY, "");
1524                        config.creatorUid = Integer.parseInt(uid);
1525                    }
1526
1527                    if (key.startsWith(BLACKLIST_MILLI_KEY)) {
1528                        String milli = key.replace(BLACKLIST_MILLI_KEY, "");
1529                        milli = milli.replace(SEPARATOR_KEY, "");
1530                        config.blackListTimestamp = Long.parseLong(milli);
1531                    }
1532
1533                    if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) {
1534                        String num = key.replace(NUM_CONNECTION_FAILURES_KEY, "");
1535                        num = num.replace(SEPARATOR_KEY, "");
1536                        config.numConnectionFailures = Integer.parseInt(num);
1537                    }
1538
1539                    if (key.startsWith(SCORER_OVERRIDE_KEY)) {
1540                        String num = key.replace(SCORER_OVERRIDE_KEY, "");
1541                        num = num.replace(SEPARATOR_KEY, "");
1542                        config.numScorerOverride = Integer.parseInt(num);
1543                    }
1544
1545                    if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) {
1546                        String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, "");
1547                        num = num.replace(SEPARATOR_KEY, "");
1548                        config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num);
1549                    }
1550
1551                    if (key.startsWith(NUM_ASSOCIATION_KEY)) {
1552                        String num = key.replace(NUM_ASSOCIATION_KEY, "");
1553                        num = num.replace(SEPARATOR_KEY, "");
1554                        config.numAssociation = Integer.parseInt(num);
1555                    }
1556
1557                    if (key.startsWith(CONNECT_UID_KEY)) {
1558                        String uid = key.replace(CONNECT_UID_KEY, "");
1559                        uid = uid.replace(SEPARATOR_KEY, "");
1560                        config.lastConnectUid = Integer.parseInt(uid);
1561                    }
1562
1563                    if (key.startsWith(UPDATE_UID_KEY)) {
1564                        String uid = key.replace(UPDATE_UID_KEY, "");
1565                        uid = uid.replace(SEPARATOR_KEY, "");
1566                        config.lastUpdateUid = Integer.parseInt(uid);
1567                    }
1568
1569                    if (key.startsWith(FAILURE_KEY)) {
1570                        config.lastFailure = key.replace(FAILURE_KEY, "");
1571                        config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
1572                    }
1573
1574                    if (key.startsWith(PEER_CONFIGURATION_KEY)) {
1575                        config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
1576                        config.peerWifiConfiguration =
1577                                config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
1578                    }
1579
1580                    if (key.startsWith(CHOICE_KEY)) {
1581                        String choiceStr = key.replace(CHOICE_KEY, "");
1582                        choiceStr = choiceStr.replace(SEPARATOR_KEY, "");
1583                        String configKey = "";
1584                        int choice = 0;
1585                        Matcher match = mConnectChoice.matcher(choiceStr);
1586                        if (!match.find()) {
1587                            if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
1588                                    " Couldnt match pattern : " + choiceStr);
1589                        } else {
1590                            configKey = match.group(1);
1591                            try {
1592                                choice = Integer.parseInt(match.group(2));
1593                            } catch (NumberFormatException e) {
1594                                choice = 0;
1595                            }
1596                            if (choice > 0) {
1597                                if (config.connectChoices == null) {
1598                                    config.connectChoices = new HashMap<String, Integer>();
1599                                }
1600                                config.connectChoices.put(configKey, choice);
1601                            }
1602                        }
1603                    }
1604
1605                    if (key.startsWith(LINK_KEY)) {
1606                        String configKey = key.replace(LINK_KEY, "");
1607                        configKey = configKey.replace(SEPARATOR_KEY, "");
1608                        if (config.linkedConfigurations == null) {
1609                            config.linkedConfigurations = new HashMap<String, Integer>();
1610                        }
1611                        if (config.linkedConfigurations != null) {
1612                            config.linkedConfigurations.put(configKey, -1);
1613                        }
1614                    }
1615
1616                    if (key.startsWith(BSSID_KEY)) {
1617                        if (key.startsWith(BSSID_KEY)) {
1618                            bssid = key.replace(BSSID_KEY, "");
1619                            bssid = bssid.replace(SEPARATOR_KEY, "");
1620                            freq = 0;
1621                            seen = 0;
1622                            rssi = WifiConfiguration.INVALID_RSSI;
1623                            caps = "";
1624                            status = 0;
1625                        }
1626
1627                        if (key.startsWith(RSSI_KEY)) {
1628                            String lvl = key.replace(RSSI_KEY, "");
1629                            lvl = lvl.replace(SEPARATOR_KEY, "");
1630                            rssi = Integer.parseInt(lvl);
1631                        }
1632
1633                        if (key.startsWith(BSSID_STATUS_KEY)) {
1634                            String st = key.replace(BSSID_STATUS_KEY, "");
1635                            st = st.replace(SEPARATOR_KEY, "");
1636                            status = Integer.parseInt(st);
1637                        }
1638
1639                        if (key.startsWith(FREQ_KEY)) {
1640                            String channel = key.replace(FREQ_KEY, "");
1641                            channel = channel.replace(SEPARATOR_KEY, "");
1642                            freq = Integer.parseInt(channel);
1643                        }
1644
1645                        if (key.startsWith(DATE_KEY)) {
1646                        /*
1647                         * when reading the configuration from file we don't update the date
1648                         * so as to avoid reading back stale or non-sensical data that would
1649                         * depend on network time.
1650                         * The date of a WifiConfiguration should only come from actual scan result.
1651                         *
1652                        String s = key.replace(FREQ_KEY, "");
1653                        seen = Integer.getInteger(s);
1654                        */
1655                        }
1656
1657                        if (key.startsWith(BSSID_KEY_END)) {
1658
1659                            if ((bssid != null) && (ssid != null)) {
1660
1661                                if (config.scanResultCache == null) {
1662                                    config.scanResultCache = new HashMap<String, ScanResult>();
1663                                }
1664                                WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
1665                                ScanResult result = new ScanResult(wssid, bssid,
1666                                        caps, rssi, freq, (long) 0);
1667                                result.seen = seen;
1668                                config.scanResultCache.put(bssid, result);
1669                                result.status = status;
1670                            }
1671                        }
1672                    }
1673                }
1674            }
1675        } catch (EOFException ignore) {
1676            if (in != null) {
1677                try {
1678                    in.close();
1679                } catch (Exception e) {
1680                    loge("readNetworkHistory: Error reading file" + e);
1681                }
1682            }
1683        } catch (IOException e) {
1684            loge("readNetworkHistory: No config file, revert to default" + e);
1685        }
1686
1687        if(in!=null) {
1688            try {
1689                in.close();
1690            } catch (Exception e) {
1691                loge("readNetworkHistory: Error closing file" + e);
1692            }
1693        }
1694    }
1695
1696    private void readAutoJoinConfig() {
1697        BufferedReader reader = null;
1698        try {
1699
1700            reader = new BufferedReader(new FileReader(autoJoinConfigFile));
1701
1702            for (String key = reader.readLine(); key != null; key = reader.readLine()) {
1703                if (key != null) {
1704                    Log.d(TAG, "readAutoJoinConfig line: " + key);
1705                }
1706                if (key.startsWith(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY)) {
1707                    String st = key.replace(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY, "");
1708                    st = st.replace(SEPARATOR_KEY, "");
1709                    try {
1710                        enableAutoJoinWhileAssociated = Integer.parseInt(st) != 0;
1711                        Log.d(TAG,"readAutoJoinConfig: enabled = " + enableAutoJoinWhileAssociated);
1712                    } catch (NumberFormatException e) {
1713                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1714                    }
1715                }
1716
1717                if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY)) {
1718                    String st =
1719                            key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, "");
1720                    st = st.replace(SEPARATOR_KEY, "");
1721                    try {
1722                        thresholdInitialAutoJoinAttemptMin5RSSI = Integer.parseInt(st);
1723                        Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin5RSSI = "
1724                                + Integer.toString(thresholdInitialAutoJoinAttemptMin5RSSI));
1725                    } catch (NumberFormatException e) {
1726                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1727                    }
1728                }
1729
1730                if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY)) {
1731                    String st =
1732                            key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, "");
1733                    st = st.replace(SEPARATOR_KEY, "");
1734                    try {
1735                        thresholdInitialAutoJoinAttemptMin24RSSI = Integer.parseInt(st);
1736                        Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin24RSSI = "
1737                                + Integer.toString(thresholdInitialAutoJoinAttemptMin24RSSI));
1738                    } catch (NumberFormatException e) {
1739                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1740                    }
1741                }
1742
1743                if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_5G_KEY)) {
1744                    String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, "");
1745                    st = st.replace(SEPARATOR_KEY, "");
1746                    try {
1747                        thresholdUnblacklistThreshold5Hard = Integer.parseInt(st);
1748                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Hard = "
1749                            + Integer.toString(thresholdUnblacklistThreshold5Hard));
1750                    } catch (NumberFormatException e) {
1751                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1752                    }
1753                }
1754                if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY)) {
1755                    String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, "");
1756                    st = st.replace(SEPARATOR_KEY, "");
1757                    try {
1758                        thresholdUnblacklistThreshold5Soft = Integer.parseInt(st);
1759                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Soft = "
1760                            + Integer.toString(thresholdUnblacklistThreshold5Soft));
1761                    } catch (NumberFormatException e) {
1762                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1763                    }
1764                }
1765                if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_24G_KEY)) {
1766                    String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, "");
1767                    st = st.replace(SEPARATOR_KEY, "");
1768                    try {
1769                        thresholdUnblacklistThreshold24Hard = Integer.parseInt(st);
1770                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Hard = "
1771                            + Integer.toString(thresholdUnblacklistThreshold24Hard));
1772                    } catch (NumberFormatException e) {
1773                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1774                    }
1775                }
1776                if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY)) {
1777                    String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, "");
1778                    st = st.replace(SEPARATOR_KEY, "");
1779                    try {
1780                        thresholdUnblacklistThreshold24Soft = Integer.parseInt(st);
1781                        Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Soft = "
1782                            + Integer.toString(thresholdUnblacklistThreshold24Soft));
1783                    } catch (NumberFormatException e) {
1784                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1785                    }
1786                }
1787
1788                if (key.startsWith(THRESHOLD_GOOD_RSSI_5_KEY)) {
1789                    String st = key.replace(THRESHOLD_GOOD_RSSI_5_KEY, "");
1790                    st = st.replace(SEPARATOR_KEY, "");
1791                    try {
1792                        thresholdGoodRssi5 = Integer.parseInt(st);
1793                        Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi5 = "
1794                            + Integer.toString(thresholdGoodRssi5));
1795                    } catch (NumberFormatException e) {
1796                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1797                    }
1798                }
1799                if (key.startsWith(THRESHOLD_LOW_RSSI_5_KEY)) {
1800                    String st = key.replace(THRESHOLD_LOW_RSSI_5_KEY, "");
1801                    st = st.replace(SEPARATOR_KEY, "");
1802                    try {
1803                        thresholdLowRssi5 = Integer.parseInt(st);
1804                        Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi5 = "
1805                            + Integer.toString(thresholdLowRssi5));
1806                    } catch (NumberFormatException e) {
1807                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1808                    }
1809                }
1810                if (key.startsWith(THRESHOLD_BAD_RSSI_5_KEY)) {
1811                    String st = key.replace(THRESHOLD_BAD_RSSI_5_KEY, "");
1812                    st = st.replace(SEPARATOR_KEY, "");
1813                    try {
1814                        thresholdBadRssi5 = Integer.parseInt(st);
1815                        Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi5 = "
1816                            + Integer.toString(thresholdBadRssi5));
1817                    } catch (NumberFormatException e) {
1818                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1819                    }
1820                }
1821
1822                if (key.startsWith(THRESHOLD_GOOD_RSSI_24_KEY)) {
1823                    String st = key.replace(THRESHOLD_GOOD_RSSI_24_KEY, "");
1824                    st = st.replace(SEPARATOR_KEY, "");
1825                    try {
1826                        thresholdGoodRssi24 = Integer.parseInt(st);
1827                        Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi24 = "
1828                            + Integer.toString(thresholdGoodRssi24));
1829                    } catch (NumberFormatException e) {
1830                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1831                    }
1832                }
1833                if (key.startsWith(THRESHOLD_LOW_RSSI_24_KEY)) {
1834                    String st = key.replace(THRESHOLD_LOW_RSSI_24_KEY, "");
1835                    st = st.replace(SEPARATOR_KEY, "");
1836                    try {
1837                        thresholdLowRssi24 = Integer.parseInt(st);
1838                        Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi24 = "
1839                            + Integer.toString(thresholdLowRssi24));
1840                    } catch (NumberFormatException e) {
1841                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1842                    }
1843                }
1844                if (key.startsWith(THRESHOLD_BAD_RSSI_24_KEY)) {
1845                    String st = key.replace(THRESHOLD_BAD_RSSI_24_KEY, "");
1846                    st = st.replace(SEPARATOR_KEY, "");
1847                    try {
1848                        thresholdBadRssi24 = Integer.parseInt(st);
1849                        Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi24 = "
1850                            + Integer.toString(thresholdBadRssi24));
1851                    } catch (NumberFormatException e) {
1852                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1853                    }
1854                }
1855
1856                if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
1857                    String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
1858                    st = st.replace(SEPARATOR_KEY, "");
1859                    try {
1860                        maxTxPacketForNetworkSwitching = Integer.parseInt(st);
1861                        Log.d(TAG,"readAutoJoinConfig: maxTxPacketForNetworkSwitching = "
1862                            + Integer.toString(maxTxPacketForNetworkSwitching));
1863                    } catch (NumberFormatException e) {
1864                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1865                    }
1866                }
1867                if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
1868                    String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
1869                    st = st.replace(SEPARATOR_KEY, "");
1870                    try {
1871                        maxRxPacketForNetworkSwitching = Integer.parseInt(st);
1872                        Log.d(TAG,"readAutoJoinConfig: maxRxPacketForNetworkSwitching = "
1873                            + Integer.toString(maxRxPacketForNetworkSwitching));
1874                    } catch (NumberFormatException e) {
1875                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1876                    }
1877                }
1878
1879                if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY)) {
1880                    String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY, "");
1881                    st = st.replace(SEPARATOR_KEY, "");
1882                    try {
1883                        thresholdBandPreferenceLowRssi5 = Integer.parseInt(st);
1884                        Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceLowRssi5 = "
1885                            + Integer.toString(thresholdBandPreferenceLowRssi5));
1886                    } catch (NumberFormatException e) {
1887                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1888                    }
1889                }
1890                if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
1891                    String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
1892                    st = st.replace(SEPARATOR_KEY, "");
1893                    try {
1894                        thresholdBandPreferenceRssi5 = Integer.parseInt(st);
1895                        Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi5 = "
1896                            + Integer.toString(thresholdBandPreferenceRssi5));
1897                    } catch (NumberFormatException e) {
1898                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1899                    }
1900                }
1901                if (key.startsWith(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
1902                    String st = key.replace(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
1903                    st = st.replace(SEPARATOR_KEY, "");
1904                    try {
1905                        thresholdBandPreferenceRssi24 = Integer.parseInt(st);
1906                        Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi24 = "
1907                            + Integer.toString(thresholdBandPreferenceRssi24));
1908                    } catch (NumberFormatException e) {
1909                        Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
1910                    }
1911                }
1912            }
1913        } catch (EOFException ignore) {
1914            if (reader != null) {
1915                try {
1916                    reader.close();
1917                    reader = null;
1918                } catch (Exception e) {
1919                    loge("readAutoJoinStatus: Error closing file" + e);
1920                }
1921            }
1922        } catch (IOException e) {
1923            loge("readAutoJoinStatus: Error parsing configuration" + e);
1924        }
1925
1926        if (reader!=null) {
1927           try {
1928               reader.close();
1929           } catch (Exception e) {
1930               loge("readAutoJoinStatus: Error closing file" + e);
1931           }
1932        }
1933    }
1934
1935
1936    private void writeIpAndProxyConfigurations() {
1937        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
1938        for(WifiConfiguration config : mConfiguredNetworks.values()) {
1939            if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
1940                networks.put(configKey(config), config.getIpConfiguration());
1941            }
1942        }
1943
1944        super.writeIpAndProxyConfigurations(ipConfigFile, networks);
1945    }
1946
1947    private void readIpAndProxyConfigurations() {
1948        SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
1949
1950        if (networks.size() == 0) {
1951            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
1952            return;
1953        }
1954
1955        for (int i = 0; i < networks.size(); i++) {
1956            int id = networks.keyAt(i);
1957            WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
1958
1959
1960            if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
1961                loge("configuration found for missing network, nid=" + id
1962                        +", ignored, networks.size=" + Integer.toString(networks.size()));
1963            } else {
1964                config.setIpConfiguration(networks.valueAt(i));
1965            }
1966        }
1967    }
1968
1969    /*
1970     * Convert string to Hexadecimal before passing to wifi native layer
1971     * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
1972     * conversion to hex is required because SSIDs can have space characters in them;
1973     * and that can confuses the supplicant because it uses space charaters as delimiters
1974     */
1975
1976    private String encodeSSID(String str){
1977        String tmp = removeDoubleQuotes(str);
1978        return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
1979    }
1980
1981    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
1982        /*
1983         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1984         * network configuration. Otherwise, the networkId should
1985         * refer to an existing configuration.
1986         */
1987
1988        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
1989
1990        int netId = config.networkId;
1991        boolean newNetwork = false;
1992        // networkId of INVALID_NETWORK_ID means we want to create a new network
1993        if (netId == INVALID_NETWORK_ID) {
1994            Integer savedNetId = mNetworkIds.get(configKey(config));
1995            //paranoia: check if either we have a network Id or a WifiConfiguration
1996            //matching the one we are trying to add.
1997            if (savedNetId == null) {
1998                for (WifiConfiguration test : mConfiguredNetworks.values()) {
1999                    if (test.configKey().equals(config.configKey())) {
2000                        savedNetId = test.networkId;
2001                        loge("addOrUpdateNetworkNative " + config.configKey()
2002                                + " was found, but no network Id");
2003                        break;
2004                    }
2005                }
2006            }
2007            if (savedNetId != null) {
2008                netId = savedNetId;
2009            } else {
2010                newNetwork = true;
2011                netId = mWifiNative.addNetwork();
2012                if (netId < 0) {
2013                    loge("Failed to add a network!");
2014                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
2015                } else {
2016                    loge("addOrUpdateNetworkNative created netId=" + netId);
2017                }
2018            }
2019        }
2020
2021        boolean updateFailed = true;
2022
2023        setVariables: {
2024
2025            if (config.SSID != null &&
2026                    !mWifiNative.setNetworkVariable(
2027                        netId,
2028                        WifiConfiguration.ssidVarName,
2029                        encodeSSID(config.SSID))) {
2030                loge("failed to set SSID: "+config.SSID);
2031                break setVariables;
2032            }
2033
2034            if (config.BSSID != null) {
2035                loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
2036                if (!mWifiNative.setNetworkVariable(
2037                        netId,
2038                        WifiConfiguration.bssidVarName,
2039                        config.BSSID)) {
2040                    loge("failed to set BSSID: " + config.BSSID);
2041                    break setVariables;
2042                }
2043            }
2044
2045            String allowedKeyManagementString =
2046                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
2047            if (config.allowedKeyManagement.cardinality() != 0 &&
2048                    !mWifiNative.setNetworkVariable(
2049                        netId,
2050                        WifiConfiguration.KeyMgmt.varName,
2051                        allowedKeyManagementString)) {
2052                loge("failed to set key_mgmt: "+
2053                        allowedKeyManagementString);
2054                break setVariables;
2055            }
2056
2057            String allowedProtocolsString =
2058                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
2059            if (config.allowedProtocols.cardinality() != 0 &&
2060                    !mWifiNative.setNetworkVariable(
2061                        netId,
2062                        WifiConfiguration.Protocol.varName,
2063                        allowedProtocolsString)) {
2064                loge("failed to set proto: "+
2065                        allowedProtocolsString);
2066                break setVariables;
2067            }
2068
2069            String allowedAuthAlgorithmsString =
2070                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
2071            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
2072                    !mWifiNative.setNetworkVariable(
2073                        netId,
2074                        WifiConfiguration.AuthAlgorithm.varName,
2075                        allowedAuthAlgorithmsString)) {
2076                loge("failed to set auth_alg: "+
2077                        allowedAuthAlgorithmsString);
2078                break setVariables;
2079            }
2080
2081            String allowedPairwiseCiphersString =
2082                    makeString(config.allowedPairwiseCiphers,
2083                    WifiConfiguration.PairwiseCipher.strings);
2084            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
2085                    !mWifiNative.setNetworkVariable(
2086                        netId,
2087                        WifiConfiguration.PairwiseCipher.varName,
2088                        allowedPairwiseCiphersString)) {
2089                loge("failed to set pairwise: "+
2090                        allowedPairwiseCiphersString);
2091                break setVariables;
2092            }
2093
2094            String allowedGroupCiphersString =
2095                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
2096            if (config.allowedGroupCiphers.cardinality() != 0 &&
2097                    !mWifiNative.setNetworkVariable(
2098                        netId,
2099                        WifiConfiguration.GroupCipher.varName,
2100                        allowedGroupCiphersString)) {
2101                loge("failed to set group: "+
2102                        allowedGroupCiphersString);
2103                break setVariables;
2104            }
2105
2106            // Prevent client screw-up by passing in a WifiConfiguration we gave it
2107            // by preventing "*" as a key.
2108            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
2109                    !mWifiNative.setNetworkVariable(
2110                        netId,
2111                        WifiConfiguration.pskVarName,
2112                        config.preSharedKey)) {
2113                loge("failed to set psk");
2114                break setVariables;
2115            }
2116
2117            boolean hasSetKey = false;
2118            if (config.wepKeys != null) {
2119                for (int i = 0; i < config.wepKeys.length; i++) {
2120                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
2121                    // by preventing "*" as a key.
2122                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
2123                        if (!mWifiNative.setNetworkVariable(
2124                                    netId,
2125                                    WifiConfiguration.wepKeyVarNames[i],
2126                                    config.wepKeys[i])) {
2127                            loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
2128                            break setVariables;
2129                        }
2130                        hasSetKey = true;
2131                    }
2132                }
2133            }
2134
2135            if (hasSetKey) {
2136                if (!mWifiNative.setNetworkVariable(
2137                            netId,
2138                            WifiConfiguration.wepTxKeyIdxVarName,
2139                            Integer.toString(config.wepTxKeyIndex))) {
2140                    loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
2141                    break setVariables;
2142                }
2143            }
2144
2145            if (!mWifiNative.setNetworkVariable(
2146                        netId,
2147                        WifiConfiguration.priorityVarName,
2148                        Integer.toString(config.priority))) {
2149                loge(config.SSID + ": failed to set priority: "
2150                        +config.priority);
2151                break setVariables;
2152            }
2153
2154            if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
2155                        netId,
2156                        WifiConfiguration.hiddenSSIDVarName,
2157                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
2158                loge(config.SSID + ": failed to set hiddenSSID: "+
2159                        config.hiddenSSID);
2160                break setVariables;
2161            }
2162
2163            if (config.requirePMF && !mWifiNative.setNetworkVariable(
2164                        netId,
2165                        WifiConfiguration.pmfVarName,
2166                        "2")) {
2167                loge(config.SSID + ": failed to set requirePMF: "+
2168                        config.requirePMF);
2169                break setVariables;
2170            }
2171
2172            if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
2173                    netId,
2174                    WifiConfiguration.updateIdentiferVarName,
2175                    config.updateIdentifier)) {
2176                loge(config.SSID + ": failed to set updateIdentifier: "+
2177                        config.updateIdentifier);
2178                break setVariables;
2179            }
2180
2181            if (config.enterpriseConfig != null &&
2182                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
2183
2184                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
2185
2186                if (needsKeyStore(enterpriseConfig)) {
2187                    /**
2188                     * Keyguard settings may eventually be controlled by device policy.
2189                     * We check here if keystore is unlocked before installing
2190                     * credentials.
2191                     * TODO: Do we need a dialog here ?
2192                     */
2193                    if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
2194                        loge(config.SSID + ": key store is locked");
2195                        break setVariables;
2196                    }
2197
2198                    try {
2199                        /* config passed may include only fields being updated.
2200                         * In order to generate the key id, fetch uninitialized
2201                         * fields from the currently tracked configuration
2202                         */
2203                        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
2204                        String keyId = config.getKeyIdForCredentials(currentConfig);
2205
2206                        if (!installKeys(enterpriseConfig, keyId)) {
2207                            loge(config.SSID + ": failed to install keys");
2208                            break setVariables;
2209                        }
2210                    } catch (IllegalStateException e) {
2211                        loge(config.SSID + " invalid config for key installation");
2212                        break setVariables;
2213                    }
2214                }
2215
2216                HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
2217                for (String key : enterpriseFields.keySet()) {
2218                        String value = enterpriseFields.get(key);
2219                        if (key.equals("password") && value != null && value.equals("*")) {
2220                            //no need to try to set an obfuscated password, which will fail
2221                            continue;
2222                        }
2223                        if (!mWifiNative.setNetworkVariable(
2224                                    netId,
2225                                    key,
2226                                    value)) {
2227                            removeKeys(enterpriseConfig);
2228                            loge(config.SSID + ": failed to set " + key +
2229                                    ": " + value);
2230                            break setVariables;
2231                        }
2232                }
2233            }
2234            updateFailed = false;
2235        } //end of setVariables
2236
2237        if (updateFailed) {
2238            if (newNetwork) {
2239                mWifiNative.removeNetwork(netId);
2240                loge("Failed to set a network variable, removed network: " + netId);
2241            }
2242            return new NetworkUpdateResult(INVALID_NETWORK_ID);
2243        }
2244
2245        /* An update of the network variables requires reading them
2246         * back from the supplicant to update mConfiguredNetworks.
2247         * This is because some of the variables (SSID, wep keys &
2248         * passphrases) reflect different values when read back than
2249         * when written. For example, wep key is stored as * irrespective
2250         * of the value sent to the supplicant
2251         */
2252        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
2253        if (currentConfig == null) {
2254            currentConfig = new WifiConfiguration();
2255            currentConfig.setIpAssignment(IpAssignment.DHCP);
2256            currentConfig.setProxySettings(ProxySettings.NONE);
2257            currentConfig.networkId = netId;
2258            if (config != null) {
2259                //carry over the creation parameters
2260                currentConfig.selfAdded = config.selfAdded;
2261                currentConfig.didSelfAdd = config.didSelfAdd;
2262                currentConfig.lastConnectUid = config.lastConnectUid;
2263                currentConfig.lastUpdateUid = config.lastUpdateUid;
2264                currentConfig.creatorUid = config.creatorUid;
2265                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
2266            }
2267            if (DBG) {
2268                loge("created new config netId=" + Integer.toString(netId)
2269                        + " uid=" + Integer.toString(currentConfig.creatorUid));
2270            }
2271        }
2272
2273        if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
2274            //make sure the configuration is not deleted anymore since we just
2275            //added or modified it.
2276            currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
2277            currentConfig.selfAdded = false;
2278            currentConfig.didSelfAdd = false;
2279        }
2280
2281        if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
2282
2283        readNetworkVariables(currentConfig);
2284
2285        mConfiguredNetworks.put(netId, currentConfig);
2286        mNetworkIds.put(configKey(currentConfig), netId);
2287
2288        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
2289        result.setIsNewNetwork(newNetwork);
2290        result.setNetworkId(netId);
2291        return result;
2292    }
2293
2294
2295    public void linkConfiguration(WifiConfiguration config) {
2296        if (config.scanResultCache != null && config.scanResultCache.size() > 6) {
2297            // Ignore configurations with large number of BSSIDs
2298            return;
2299        }
2300        if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
2301            // Only link WPA_PSK config
2302            return;
2303        }
2304        for (WifiConfiguration link : mConfiguredNetworks.values()) {
2305            boolean doLink = false;
2306
2307            if (link.configKey().equals(config.configKey())) {
2308                continue;
2309            }
2310
2311            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
2312                continue;
2313            }
2314
2315            // Autojoin will be allowed to dynamically jump from a linked configuration
2316            // to another, hence only link configurations that have equivalent level of security
2317            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
2318                continue;
2319            }
2320
2321            if (link.scanResultCache != null && link.scanResultCache.size() > 6) {
2322                // Ignore configurations with large number of BSSIDs
2323                continue;
2324            }
2325
2326            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
2327                // If both default GW are known, compare based on RSSI only if the GW is equal
2328                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
2329                    if (VDBG) {
2330                        loge("linkConfiguration link due to same gw" + link.SSID +
2331                                " and " + config.SSID + " GW " + config.defaultGwMacAddress);
2332                    }
2333                    doLink = true;
2334                }
2335            } else {
2336                // We do not know BOTH default gateways hence we will try to link
2337                // hoping that WifiConfigurations are indeed behind the same gateway.
2338                // once both WifiConfiguration have been tried and thus once both efault gateways
2339                // are known we will revisit the choice of linking them
2340                if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
2341                        && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
2342                    for (String abssid : config.scanResultCache.keySet()) {
2343                        for (String bbssid : link.scanResultCache.keySet()) {
2344                            if (VDBG) {
2345                                loge("linkConfiguration try to link due to DBDC BSSID match "
2346                                        + link.SSID +
2347                                        " and " + config.SSID + " bssida " + abssid
2348                                        + " bssidb " + bbssid);
2349                            }
2350                            if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
2351                                // If first 16 ascii characters of BSSID matches,
2352                                // we assume this is a DBDC
2353                                doLink = true;
2354                            }
2355                        }
2356                    }
2357                }
2358            }
2359
2360            if (doLink) {
2361                if (VDBG) {
2362                    loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID);
2363                }
2364                if (link.linkedConfigurations == null) {
2365                    link.linkedConfigurations = new HashMap<String, Integer>();
2366                }
2367                if (config.linkedConfigurations == null) {
2368                    config.linkedConfigurations = new HashMap<String, Integer>();
2369                }
2370                link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
2371                config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
2372            } else {
2373                //todo if they are linked, break the link
2374            }
2375        }
2376    }
2377
2378    /*
2379     * We try to link a scan result with a WifiConfiguration for which SSID and
2380     * key management dont match,
2381     * for instance, we try identify the 5GHz SSID of a DBDC AP,
2382     * even though we know only of the 2.4GHz
2383     *
2384     * Obviously, this function is not optimal since it is used to compare every scan
2385     * result with every Saved WifiConfiguration, with a string.equals operation.
2386     * As a speed up, might be better to implement the mConfiguredNetworks store as a
2387     * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
2388     * so as to speed this up. Also to prevent the tiny probability of hash collision.
2389     *
2390     */
2391    public WifiConfiguration associateWithConfiguration(ScanResult result) {
2392        String configKey = WifiConfiguration.configKey(result);
2393        if (configKey == null) {
2394            if (DBG) loge("associateWithConfiguration(): no config key " );
2395            return null;
2396        }
2397
2398        //need to compare with quoted string
2399        String SSID = "\"" + result.SSID + "\"";
2400
2401        WifiConfiguration config = null;
2402        for (WifiConfiguration link : mConfiguredNetworks.values()) {
2403            boolean doLink = false;
2404
2405            if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) {
2406                //make sure we dont associate the scan result to a deleted config
2407                continue;
2408            }
2409
2410            if (configKey.equals(link.configKey())) {
2411                if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
2412                return link; //found it exactly
2413            }
2414
2415            if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) {
2416                String bssid = "";
2417                for (String key : link.scanResultCache.keySet()) {
2418                    bssid = key;
2419                }
2420
2421                if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
2422                        && SSID.regionMatches(false, 0, link.SSID, 0, 3)) {
2423                    // if first 16 ascii characters of BSSID matches, and first 3
2424                    // characters of SSID match, we assume this is a home setup
2425                    // and thus we will try to transfer the password from the known
2426                    // BSSID/SSID to the recently found BSSID/SSID
2427
2428                    //if (VDBG)
2429                    //    loge("associateWithConfiguration OK " );
2430                    doLink = true;
2431                }
2432            }
2433
2434            if (doLink) {
2435                //try to make a non verified WifiConfiguration, but only if the original
2436                //configuration was not self already added
2437                if (VDBG) {
2438                    loge("associateWithConfiguration: will create " +
2439                            result.SSID + " and associate it with: " + link.SSID);
2440                }
2441                config = wifiConfigurationFromScanResult(result);
2442                if (config != null) {
2443                    config.selfAdded = true;
2444                    config.didSelfAdd = true;
2445                    config.peerWifiConfiguration = link.configKey();
2446                    if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
2447                            config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
2448                        //transfer the credentials from the configuration we are linking from
2449                        String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
2450                        if (psk != null) {
2451                            config.preSharedKey = psk;
2452                            if (VDBG) {
2453                                if (config.preSharedKey != null)
2454                                    loge(" transfer PSK : " + config.preSharedKey);
2455                            }
2456
2457                            //link configurations
2458                            if (link.linkedConfigurations == null) {
2459                                link.linkedConfigurations = new HashMap<String, Integer>();
2460                            }
2461                            if (config.linkedConfigurations == null) {
2462                                config.linkedConfigurations = new HashMap<String, Integer>();
2463                            }
2464                            link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
2465                            config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
2466                        } else {
2467                            config = null;
2468                        }
2469                    } else {
2470                        config = null;
2471                    }
2472                }
2473            } else {
2474                //todo if they are linked, break the link
2475            }
2476        }
2477        return config;
2478    }
2479
2480    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
2481        if (config == null)
2482            return null;
2483        long now_ms = System.currentTimeMillis();
2484
2485        HashSet<Integer> channels = new HashSet<Integer>();
2486
2487        //get channels for this configuration, if there are at least 2 BSSIDs
2488        if (config.scanResultCache == null && config.linkedConfigurations == null) {
2489            return null;
2490        }
2491
2492        if (VDBG) {
2493            StringBuilder dbg = new StringBuilder();
2494            dbg.append("makeChannelList age=" + Integer.toString(age)
2495                    + " for " + config.configKey());
2496            if (config.scanResultCache != null) {
2497                dbg.append(" bssids=" + config.scanResultCache.size());
2498            }
2499            if (config.linkedConfigurations != null) {
2500                dbg.append(" linked=" + config.linkedConfigurations.size());
2501            }
2502            loge(dbg.toString());
2503        }
2504
2505        if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
2506            for (ScanResult result : config.scanResultCache.values()) {
2507                if (VDBG) {
2508                    boolean test = (now_ms - result.seen) < age;
2509                    loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
2510                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
2511                }
2512                if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2513                    channels.add(result.frequency);
2514                }
2515            }
2516        }
2517
2518        //get channels for linked configurations
2519        if (config.linkedConfigurations != null) {
2520            for (String key : config.linkedConfigurations.keySet()) {
2521                WifiConfiguration linked = getWifiConfiguration(key);
2522                if (linked == null)
2523                    continue;
2524                if (linked.scanResultCache == null) {
2525                    continue;
2526                }
2527                for (ScanResult result : linked.scanResultCache.values()) {
2528                    if (VDBG) {
2529                        loge("has link: " + result.BSSID
2530                                + " freq=" + Integer.toString(result.frequency)
2531                                + " age=" + Long.toString(now_ms - result.seen));
2532                    }
2533                    if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2534                        channels.add(result.frequency);
2535                    }
2536                }
2537            }
2538        }
2539        return channels;
2540    }
2541
2542    public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) {
2543        WifiConfiguration found = null;
2544        if (scanResult == null)
2545            return found;
2546
2547        //first step, look for this scan Result by SSID + Key Management
2548        String key = WifiConfiguration.configKey(scanResult);
2549        int hash = key.hashCode();
2550
2551        Integer netId = mNetworkIds.get(hash);
2552        if (netId == null) return null;
2553        WifiConfiguration config = mConfiguredNetworks.get(netId);
2554        if (config != null) {
2555           if (config.scanResultCache == null) {
2556                config.scanResultCache = new HashMap<String, ScanResult>();
2557           }
2558           if (config.scanResultCache == null) {
2559                return null;
2560           }
2561           //add the scan result to this WifiConfiguration
2562           config.scanResultCache.put(scanResult.BSSID, scanResult);
2563           mConfiguredNetworks.put(netId, config);
2564           linkConfiguration(config);
2565           found = config;
2566        }
2567
2568        if (VDBG) {
2569            config = mConfiguredNetworks.get(netId);
2570            if (config != null) {
2571                if (config.scanResultCache != null) {
2572                    String status = "";
2573                    if (scanResult.status > 0) {
2574                        status = " status=" + Integer.toString(scanResult.status);
2575                    }
2576                    loge("                    got known scan result " +
2577                            scanResult.BSSID + " key : " + key + " num: " +
2578                            Integer.toString(config.scanResultCache.size())
2579                            + " rssi=" + Integer.toString(scanResult.level)
2580                            + " freq=" + Integer.toString(scanResult.frequency)
2581                            + status);
2582                } else {
2583                    loge("                    got known scan result and no cache" +
2584                            scanResult.BSSID + " key : " + key);
2585                }
2586            }
2587        }
2588        return found;
2589
2590    }
2591
2592    /* Compare current and new configuration and write to file on change */
2593    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
2594            WifiConfiguration currentConfig,
2595            WifiConfiguration newConfig) {
2596        boolean ipChanged = false;
2597        boolean proxyChanged = false;
2598        LinkProperties linkProperties = null;
2599
2600        if (VDBG) {
2601            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
2602                    newConfig.SSID + " path: " + ipConfigFile);
2603        }
2604
2605
2606        switch (newConfig.getIpAssignment()) {
2607            case STATIC:
2608                Collection<LinkAddress> currentLinkAddresses = currentConfig.getLinkProperties()
2609                        .getLinkAddresses();
2610                Collection<LinkAddress> newLinkAddresses = newConfig.getLinkProperties()
2611                        .getLinkAddresses();
2612                Collection<InetAddress> currentDnses =
2613                        currentConfig.getLinkProperties().getDnsServers();
2614                Collection<InetAddress> newDnses = newConfig.getLinkProperties().getDnsServers();
2615                Collection<RouteInfo> currentRoutes = currentConfig.getLinkProperties().getRoutes();
2616                Collection<RouteInfo> newRoutes = newConfig.getLinkProperties().getRoutes();
2617
2618                boolean linkAddressesDiffer =
2619                        (currentLinkAddresses.size() != newLinkAddresses.size()) ||
2620                        !currentLinkAddresses.containsAll(newLinkAddresses);
2621                boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
2622                        !currentDnses.containsAll(newDnses);
2623                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
2624                        !currentRoutes.containsAll(newRoutes);
2625
2626                if ((currentConfig.getIpAssignment() != newConfig.getIpAssignment()) ||
2627                        linkAddressesDiffer ||
2628                        dnsesDiffer ||
2629                        routesDiffer) {
2630                    ipChanged = true;
2631                }
2632                break;
2633            case DHCP:
2634                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2635                    ipChanged = true;
2636                }
2637                break;
2638            case UNASSIGNED:
2639                /* Ignore */
2640                break;
2641            default:
2642                loge("Ignore invalid ip assignment during write");
2643                break;
2644        }
2645
2646        switch (newConfig.getProxySettings()) {
2647            case STATIC:
2648            case PAC:
2649                ProxyInfo newHttpProxy = newConfig.getLinkProperties().getHttpProxy();
2650                ProxyInfo currentHttpProxy = currentConfig.getLinkProperties().getHttpProxy();
2651
2652                if (newHttpProxy != null) {
2653                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
2654                } else {
2655                    proxyChanged = (currentHttpProxy != null);
2656                }
2657                break;
2658            case NONE:
2659                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
2660                    proxyChanged = true;
2661                }
2662                break;
2663            case UNASSIGNED:
2664                /* Ignore */
2665                break;
2666            default:
2667                loge("Ignore invalid proxy configuration during write");
2668                break;
2669        }
2670
2671        if (!ipChanged) {
2672            linkProperties = copyIpSettingsFromConfig(currentConfig);
2673        } else {
2674            currentConfig.setIpAssignment(newConfig.getIpAssignment());
2675            linkProperties = copyIpSettingsFromConfig(newConfig);
2676            log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
2677                    linkProperties.toString());
2678        }
2679
2680
2681        if (!proxyChanged) {
2682            linkProperties.setHttpProxy(currentConfig.getLinkProperties().getHttpProxy());
2683        } else {
2684            currentConfig.setProxySettings(newConfig.getProxySettings());
2685            linkProperties.setHttpProxy(newConfig.getLinkProperties().getHttpProxy());
2686            log("proxy changed SSID = " + currentConfig.SSID);
2687            if (linkProperties.getHttpProxy() != null) {
2688                log(" proxyProperties: " + linkProperties.getHttpProxy().toString());
2689            }
2690        }
2691
2692        if (ipChanged || proxyChanged) {
2693            currentConfig.setLinkProperties(linkProperties);
2694            writeIpAndProxyConfigurations();
2695            sendConfiguredNetworksChangedBroadcast(currentConfig,
2696                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
2697        }
2698        return new NetworkUpdateResult(ipChanged, proxyChanged);
2699    }
2700
2701    private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
2702        LinkProperties linkProperties = new LinkProperties();
2703        linkProperties.setInterfaceName(config.getLinkProperties().getInterfaceName());
2704        for (LinkAddress linkAddr : config.getLinkProperties().getLinkAddresses()) {
2705            linkProperties.addLinkAddress(linkAddr);
2706        }
2707        for (RouteInfo route : config.getLinkProperties().getRoutes()) {
2708            linkProperties.addRoute(route);
2709        }
2710        for (InetAddress dns : config.getLinkProperties().getDnsServers()) {
2711            linkProperties.addDnsServer(dns);
2712        }
2713        return linkProperties;
2714    }
2715
2716    /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */
2717    private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
2718        switch (key) {
2719            case WifiEnterpriseConfig.EAP_KEY:
2720            case WifiEnterpriseConfig.ENGINE_KEY:
2721                return false;
2722            default:
2723                return true;
2724        }
2725    }
2726
2727    /**
2728     * Read the variables from the supplicant daemon that are needed to
2729     * fill in the WifiConfiguration object.
2730     *
2731     * @param config the {@link WifiConfiguration} object to be filled in.
2732     */
2733    private void readNetworkVariables(WifiConfiguration config) {
2734
2735        int netId = config.networkId;
2736        if (netId < 0)
2737            return;
2738
2739        /*
2740         * TODO: maybe should have a native method that takes an array of
2741         * variable names and returns an array of values. But we'd still
2742         * be doing a round trip to the supplicant daemon for each variable.
2743         */
2744        String value;
2745
2746        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
2747        if (!TextUtils.isEmpty(value)) {
2748            if (value.charAt(0) != '"') {
2749                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
2750                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
2751                //supplicant string
2752            } else {
2753                config.SSID = value;
2754            }
2755        } else {
2756            config.SSID = null;
2757        }
2758
2759        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
2760        if (!TextUtils.isEmpty(value)) {
2761            config.BSSID = value;
2762        } else {
2763            config.BSSID = null;
2764        }
2765
2766        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
2767        config.priority = -1;
2768        if (!TextUtils.isEmpty(value)) {
2769            try {
2770                config.priority = Integer.parseInt(value);
2771            } catch (NumberFormatException ignore) {
2772            }
2773        }
2774
2775        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
2776        config.hiddenSSID = false;
2777        if (!TextUtils.isEmpty(value)) {
2778            try {
2779                config.hiddenSSID = Integer.parseInt(value) != 0;
2780            } catch (NumberFormatException ignore) {
2781            }
2782        }
2783
2784        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
2785        config.wepTxKeyIndex = -1;
2786        if (!TextUtils.isEmpty(value)) {
2787            try {
2788                config.wepTxKeyIndex = Integer.parseInt(value);
2789            } catch (NumberFormatException ignore) {
2790            }
2791        }
2792
2793        for (int i = 0; i < 4; i++) {
2794            value = mWifiNative.getNetworkVariable(netId,
2795                    WifiConfiguration.wepKeyVarNames[i]);
2796            if (!TextUtils.isEmpty(value)) {
2797                config.wepKeys[i] = value;
2798            } else {
2799                config.wepKeys[i] = null;
2800            }
2801        }
2802
2803        value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
2804        if (!TextUtils.isEmpty(value)) {
2805            config.preSharedKey = value;
2806        } else {
2807            config.preSharedKey = null;
2808        }
2809
2810        value = mWifiNative.getNetworkVariable(config.networkId,
2811                WifiConfiguration.Protocol.varName);
2812        if (!TextUtils.isEmpty(value)) {
2813            String vals[] = value.split(" ");
2814            for (String val : vals) {
2815                int index =
2816                    lookupString(val, WifiConfiguration.Protocol.strings);
2817                if (0 <= index) {
2818                    config.allowedProtocols.set(index);
2819                }
2820            }
2821        }
2822
2823        value = mWifiNative.getNetworkVariable(config.networkId,
2824                WifiConfiguration.KeyMgmt.varName);
2825        if (!TextUtils.isEmpty(value)) {
2826            String vals[] = value.split(" ");
2827            for (String val : vals) {
2828                int index =
2829                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
2830                if (0 <= index) {
2831                    config.allowedKeyManagement.set(index);
2832                }
2833            }
2834        }
2835
2836        value = mWifiNative.getNetworkVariable(config.networkId,
2837                WifiConfiguration.AuthAlgorithm.varName);
2838        if (!TextUtils.isEmpty(value)) {
2839            String vals[] = value.split(" ");
2840            for (String val : vals) {
2841                int index =
2842                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
2843                if (0 <= index) {
2844                    config.allowedAuthAlgorithms.set(index);
2845                }
2846            }
2847        }
2848
2849        value = mWifiNative.getNetworkVariable(config.networkId,
2850                WifiConfiguration.PairwiseCipher.varName);
2851        if (!TextUtils.isEmpty(value)) {
2852            String vals[] = value.split(" ");
2853            for (String val : vals) {
2854                int index =
2855                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
2856                if (0 <= index) {
2857                    config.allowedPairwiseCiphers.set(index);
2858                }
2859            }
2860        }
2861
2862        value = mWifiNative.getNetworkVariable(config.networkId,
2863                WifiConfiguration.GroupCipher.varName);
2864        if (!TextUtils.isEmpty(value)) {
2865            String vals[] = value.split(" ");
2866            for (String val : vals) {
2867                int index =
2868                    lookupString(val, WifiConfiguration.GroupCipher.strings);
2869                if (0 <= index) {
2870                    config.allowedGroupCiphers.set(index);
2871                }
2872            }
2873        }
2874
2875        if (config.enterpriseConfig == null) {
2876            config.enterpriseConfig = new WifiEnterpriseConfig();
2877        }
2878        HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
2879        for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
2880            value = mWifiNative.getNetworkVariable(netId, key);
2881            if (!TextUtils.isEmpty(value)) {
2882                if (!enterpriseConfigKeyShouldBeQuoted(key)) {
2883                    value = removeDoubleQuotes(value);
2884                }
2885                enterpriseFields.put(key, value);
2886            } else {
2887                enterpriseFields.put(key, EMPTY_VALUE);
2888            }
2889        }
2890
2891        if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
2892            saveConfig();
2893        }
2894
2895        migrateCerts(config.enterpriseConfig);
2896        // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
2897    }
2898
2899    private static String removeDoubleQuotes(String string) {
2900        int length = string.length();
2901        if ((length > 1) && (string.charAt(0) == '"')
2902                && (string.charAt(length - 1) == '"')) {
2903            return string.substring(1, length - 1);
2904        }
2905        return string;
2906    }
2907
2908    private static String makeString(BitSet set, String[] strings) {
2909        StringBuffer buf = new StringBuffer();
2910        int nextSetBit = -1;
2911
2912        /* Make sure all set bits are in [0, strings.length) to avoid
2913         * going out of bounds on strings.  (Shouldn't happen, but...) */
2914        set = set.get(0, strings.length);
2915
2916        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
2917            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
2918        }
2919
2920        // remove trailing space
2921        if (set.cardinality() > 0) {
2922            buf.setLength(buf.length() - 1);
2923        }
2924
2925        return buf.toString();
2926    }
2927
2928    private int lookupString(String string, String[] strings) {
2929        int size = strings.length;
2930
2931        string = string.replace('-', '_');
2932
2933        for (int i = 0; i < size; i++)
2934            if (string.equals(strings[i]))
2935                return i;
2936
2937        // if we ever get here, we should probably add the
2938        // value to WifiConfiguration to reflect that it's
2939        // supported by the WPA supplicant
2940        loge("Failed to look-up a string: " + string);
2941
2942        return -1;
2943    }
2944
2945    /* return the allowed key management based on a scan result */
2946
2947    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
2948        WifiConfiguration config = new WifiConfiguration();
2949
2950        config.SSID = "\"" + result.SSID + "\"";
2951
2952        if (VDBG) {
2953            loge("WifiConfiguration from scan results " +
2954                    config.SSID + " cap " + result.capabilities);
2955        }
2956        if (result.capabilities.contains("WEP")) {
2957            config.allowedKeyManagement.set(KeyMgmt.NONE);
2958            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
2959            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
2960        }
2961
2962        if (result.capabilities.contains("PSK")) {
2963            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
2964        }
2965
2966        if (result.capabilities.contains("EAP")) {
2967            //this is probably wrong, as we don't have a way to enter the enterprise config
2968            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
2969            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
2970        }
2971
2972        config.scanResultCache = new HashMap<String, ScanResult>();
2973        if (config.scanResultCache == null)
2974            return null;
2975        config.scanResultCache.put(result.BSSID, result);
2976
2977        return config;
2978    }
2979
2980
2981    /* Returns a unique for a given configuration */
2982    private static int configKey(WifiConfiguration config) {
2983        String key = config.configKey();
2984        return key.hashCode();
2985    }
2986
2987    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2988        pw.println("WifiConfigStore");
2989        pw.println("mLastPriority " + mLastPriority);
2990        pw.println("Configured networks");
2991        for (WifiConfiguration conf : getConfiguredNetworks()) {
2992            pw.println(conf);
2993        }
2994        pw.println();
2995
2996        if (mLocalLog != null) {
2997            pw.println("WifiConfigStore - Log Begin ----");
2998            mLocalLog.dump(fd, pw, args);
2999            pw.println("WifiConfigStore - Log End ----");
3000        }
3001    }
3002
3003    public String getConfigFile() {
3004        return ipConfigFile;
3005    }
3006
3007    protected void loge(String s) {
3008        loge(s, false);
3009    }
3010
3011    protected void loge(String s, boolean stack) {
3012        if (stack) {
3013            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3014                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
3015                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
3016                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
3017        } else {
3018            Log.e(TAG, s);
3019        }
3020    }
3021
3022    protected void log(String s) {
3023        Log.d(TAG, s);
3024    }
3025
3026    private void localLog(String s) {
3027        if (mLocalLog != null) {
3028            mLocalLog.log(s);
3029        }
3030    }
3031
3032    private void localLog(String s, int netId) {
3033        if (mLocalLog == null) {
3034            return;
3035        }
3036
3037        WifiConfiguration config;
3038        synchronized(mConfiguredNetworks) {
3039            config = mConfiguredNetworks.get(netId);
3040        }
3041
3042        if (config != null) {
3043            mLocalLog.log(s + " " + config.getPrintableSsid());
3044        } else {
3045            mLocalLog.log(s + " " + netId);
3046        }
3047    }
3048
3049    // Certificate and private key management for EnterpriseConfig
3050    static boolean needsKeyStore(WifiEnterpriseConfig config) {
3051        // Has no keys to be installed
3052        if (config.getClientCertificate() == null && config.getCaCertificate() == null)
3053            return false;
3054        return true;
3055    }
3056
3057    static boolean isHardwareBackedKey(PrivateKey key) {
3058        return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
3059    }
3060
3061    static boolean hasHardwareBackedKey(Certificate certificate) {
3062        return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
3063    }
3064
3065    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
3066        String client = config.getClientCertificateAlias();
3067        if (!TextUtils.isEmpty(client)) {
3068            // a valid client certificate is configured
3069
3070            // BUGBUG: keyStore.get() never returns certBytes; because it is not
3071            // taking WIFI_UID as a parameter. It always looks for certificate
3072            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
3073            // all certificates need software keystore until we get the get() API
3074            // fixed.
3075
3076            return true;
3077        }
3078
3079        /*
3080        try {
3081
3082            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
3083                    .USER_CERTIFICATE + client);
3084
3085            CertificateFactory factory = CertificateFactory.getInstance("X.509");
3086            if (factory == null) {
3087                Slog.e(TAG, "Error getting certificate factory");
3088                return;
3089            }
3090
3091            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
3092            if (certBytes != null) {
3093                Certificate cert = (X509Certificate) factory.generateCertificate(
3094                        new ByteArrayInputStream(certBytes));
3095
3096                if (cert != null) {
3097                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
3098
3099                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
3100                            .USER_CERTIFICATE + client);
3101                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
3102                            "does not need" ) + " software key store");
3103                } else {
3104                    Slog.d(TAG, "could not generate certificate");
3105                }
3106            } else {
3107                Slog.e(TAG, "Could not load client certificate " + Credentials
3108                        .USER_CERTIFICATE + client);
3109                mNeedsSoftwareKeystore = true;
3110            }
3111
3112        } catch(CertificateException e) {
3113            Slog.e(TAG, "Could not read certificates");
3114            mCaCert = null;
3115            mClientCertificate = null;
3116        }
3117        */
3118
3119        return false;
3120    }
3121
3122    /** called when CS ask WiFistateMachine to disconnect the current network
3123     * because the score is bad.
3124     */
3125    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
3126        /* TODO verify the bad network is current */
3127        WifiConfiguration config = mConfiguredNetworks.get(netId);
3128        if (config != null) {
3129            if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
3130                    && info.is24GHz()) || (info.getRssi() <
3131                            WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
3132                //we got disconnected and RSSI was bad, so disable light
3133                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
3134                        + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
3135                loge("handleBadNetworkDisconnectReport (+4) "
3136                        + Integer.toString(netId) + " " + info);
3137            } else {
3138                //we got disabled but RSSI is good, so disable hard
3139                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
3140                        + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
3141                loge("handleBadNetworkDisconnectReport (+8) "
3142                        + Integer.toString(netId) + " " + info);
3143            }
3144        }
3145    }
3146
3147    boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) {
3148        boolean found = false;
3149        if (BSSID == null)
3150            return found;
3151
3152        // Look for the BSSID in our config store
3153        for (WifiConfiguration config : mConfiguredNetworks.values()) {
3154            if (config.scanResultCache != null) {
3155                for (ScanResult result: config.scanResultCache.values()) {
3156                    if (result.BSSID.equals(BSSID)) {
3157                        if (enable) {
3158                            result.status = ScanResult.ENABLED;
3159                        } else {
3160                            // Black list the BSSID we we were trying to join
3161                            // so as the Roam state machine
3162                            // doesn't pick it up over and over
3163                            result.status = ScanResult.AUTO_ROAM_DISABLED;
3164                            found = true;
3165                        }
3166                    }
3167                }
3168            }
3169        }
3170        return found;
3171    }
3172
3173    int getMaxDhcpRetries() {
3174        return Settings.Global.getInt(mContext.getContentResolver(),
3175                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
3176                DEFAULT_MAX_DHCP_RETRIES);
3177    }
3178
3179    void handleSSIDStateChange(int netId, boolean enabled, String message) {
3180        WifiConfiguration config = mConfiguredNetworks.get(netId);
3181        if (config != null) {
3182            if (enabled) {
3183                loge("SSID re-enabled for  " + config.configKey() +
3184                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
3185                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
3186                //TODO: http://b/16381983 Fix Wifi Network Blacklisting
3187                //TODO: really I don't know if re-enabling is right but we
3188                //TODO: should err on the side of trying to connect
3189                //TODO: even if the attempt will fail
3190                if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
3191                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
3192                }
3193            } else {
3194                loge("SSID temp disabled for  " + config.configKey() +
3195                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
3196                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
3197                if (message != null) {
3198                    loge(" message=" + message);
3199                }
3200                if (config.selfAdded && config.lastConnected == 0) {
3201                    // This is a network we self added, and we never succeeded,
3202                    // the user did not create this network and never entered its credentials,
3203                    // so we want to be very aggressive in disabling it completely.
3204                    disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE);
3205                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
3206                    config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE;
3207                } else {
3208                    if (message != null) {
3209                        if (message.contains("WRONG_KEY")
3210                                || message.contains("AUTH_FAILED")) {
3211                            // This configuration has received an auth failure, so disable it
3212                            // temporarily because we don't want auto-join to try it out.
3213                            // this network may be re-enabled by the "usual"
3214                            // enableAllNetwork function
3215                            //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE:
3216                            //TODO: if we could count on the wrong_ley or auth_failure
3217                            //TODO: message to be correct
3218                            //TODO: then we could just mark the configuration as
3219                            //TODO: DISABLED_ON_AUTH_FAILURE
3220                            //TODO: and the configuration will stay there until
3221                            //TODO: user enter new credentials
3222                            //TODO: It is not the case however, so instead  of disabling, let's
3223                            //TODO: start blacklisting hard
3224                            //TODO: http://b/16381983 Fix Wifi Network Blacklisting
3225                            if (config.autoJoinStatus <=
3226                                    WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
3227                                // 4 auth failure will reach 128 and disable permanently
3228                                // autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128
3229                                config.setAutoJoinStatus(4 + config.autoJoinStatus * 4);
3230                                if (config.autoJoinStatus >
3231                                        WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)
3232                                    config.setAutoJoinStatus
3233                                            (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
3234                            }
3235                            if (DBG) {
3236                                loge("blacklisted " + config.configKey() + " to "
3237                                        + Integer.toString(config.autoJoinStatus));
3238                            }
3239                        } else if (message.contains("DHCP FAILURE")) {
3240                            config.numConnectionFailures++;
3241                            config.lastConnectionFailure = System.currentTimeMillis();
3242                            int maxRetries = getMaxDhcpRetries();
3243                            // maxRetries == 0 means keep trying forever
3244                            if (maxRetries > 0 && config.numConnectionFailures > maxRetries) {
3245                                /**
3246                                 * If we've exceeded the maximum number of retries for DHCP
3247                                 * to a given network, disable the network
3248                                 */
3249                                config.setAutoJoinStatus
3250                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
3251                                disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
3252                            }
3253                            if (DBG) {
3254                                loge("blacklisted " + config.configKey() + " to "
3255                                        + config.autoJoinStatus
3256                                        + " due to DHCP failure, count="
3257                                        + config.numConnectionFailures);
3258                            }
3259                        }
3260                        message.replace("\n", "");
3261                        message.replace("\r", "");
3262                        config.lastFailure = message;
3263                    }
3264                }
3265            }
3266        }
3267    }
3268
3269    boolean installKeys(WifiEnterpriseConfig config, String name) {
3270        boolean ret = true;
3271        String privKeyName = Credentials.USER_PRIVATE_KEY + name;
3272        String userCertName = Credentials.USER_CERTIFICATE + name;
3273        String caCertName = Credentials.CA_CERTIFICATE + name;
3274        if (config.getClientCertificate() != null) {
3275            byte[] privKeyData = config.getClientPrivateKey().getEncoded();
3276            if (isHardwareBackedKey(config.getClientPrivateKey())) {
3277                // Hardware backed key store is secure enough to store keys un-encrypted, this
3278                // removes the need for user to punch a PIN to get access to these keys
3279                if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
3280                ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
3281                        KeyStore.FLAG_NONE);
3282            } else {
3283                // Software backed key store is NOT secure enough to store keys un-encrypted.
3284                // Save keys encrypted so they are protected with user's PIN. User will
3285                // have to unlock phone before being able to use these keys and connect to
3286                // networks.
3287                if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
3288                ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
3289                        KeyStore.FLAG_ENCRYPTED);
3290            }
3291            if (ret == false) {
3292                return ret;
3293            }
3294
3295            ret = putCertInKeyStore(userCertName, config.getClientCertificate());
3296            if (ret == false) {
3297                // Remove private key installed
3298                mKeyStore.delKey(privKeyName, Process.WIFI_UID);
3299                return ret;
3300            }
3301        }
3302
3303        if (config.getCaCertificate() != null) {
3304            ret = putCertInKeyStore(caCertName, config.getCaCertificate());
3305            if (ret == false) {
3306                if (config.getClientCertificate() != null) {
3307                    // Remove client key+cert
3308                    mKeyStore.delKey(privKeyName, Process.WIFI_UID);
3309                    mKeyStore.delete(userCertName, Process.WIFI_UID);
3310                }
3311                return ret;
3312            }
3313        }
3314
3315        // Set alias names
3316        if (config.getClientCertificate() != null) {
3317            config.setClientCertificateAlias(name);
3318            config.resetClientKeyEntry();
3319        }
3320
3321        if (config.getCaCertificate() != null) {
3322            config.setCaCertificateAlias(name);
3323            config.resetCaCertificate();
3324        }
3325
3326        return ret;
3327    }
3328
3329    private boolean putCertInKeyStore(String name, Certificate cert) {
3330        try {
3331            byte[] certData = Credentials.convertToPem(cert);
3332            if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
3333            return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
3334
3335        } catch (IOException e1) {
3336            return false;
3337        } catch (CertificateException e2) {
3338            return false;
3339        }
3340    }
3341
3342    void removeKeys(WifiEnterpriseConfig config) {
3343        String client = config.getClientCertificateAlias();
3344        // a valid client certificate is configured
3345        if (!TextUtils.isEmpty(client)) {
3346            if (DBG) Log.d(TAG, "removing client private key and user cert");
3347            mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
3348            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
3349        }
3350
3351        String ca = config.getCaCertificateAlias();
3352        // a valid ca certificate is configured
3353        if (!TextUtils.isEmpty(ca)) {
3354            if (DBG) Log.d(TAG, "removing CA cert");
3355            mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
3356        }
3357    }
3358
3359
3360    /** Migrates the old style TLS config to the new config style. This should only be used
3361     * when restoring an old wpa_supplicant.conf or upgrading from a previous
3362     * platform version.
3363     * @return true if the config was updated
3364     * @hide
3365     */
3366    boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
3367        String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
3368        /*
3369         * If the old configuration value is not present, then there is nothing
3370         * to do.
3371         */
3372        if (TextUtils.isEmpty(oldPrivateKey)) {
3373            return false;
3374        } else {
3375            // Also ignore it if it's empty quotes.
3376            oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
3377            if (TextUtils.isEmpty(oldPrivateKey)) {
3378                return false;
3379            }
3380        }
3381
3382        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
3383        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
3384                WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
3385
3386        /*
3387        * The old key started with the keystore:// URI prefix, but we don't
3388        * need that anymore. Trim it off if it exists.
3389        */
3390        final String keyName;
3391        if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
3392            keyName = new String(
3393                    oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
3394        } else {
3395            keyName = oldPrivateKey;
3396        }
3397        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
3398
3399        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
3400                config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
3401
3402        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
3403                config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
3404
3405        mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
3406                config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
3407
3408        // Remove old private_key string so we don't run this again.
3409        mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
3410
3411        return true;
3412    }
3413
3414    /** Migrate certs from global pool to wifi UID if not already done */
3415    void migrateCerts(WifiEnterpriseConfig config) {
3416        String client = config.getClientCertificateAlias();
3417        // a valid client certificate is configured
3418        if (!TextUtils.isEmpty(client)) {
3419            if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
3420                mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
3421                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
3422                mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
3423                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
3424            }
3425        }
3426
3427        String ca = config.getCaCertificateAlias();
3428        // a valid ca certificate is configured
3429        if (!TextUtils.isEmpty(ca)) {
3430            if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
3431                mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
3432                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
3433            }
3434        }
3435    }
3436
3437}
3438