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