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