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