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