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