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