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