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