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