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