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