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