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