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