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