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