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