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