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