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