WifiConfigManager.java revision 8c0c14b2c98bc26e6a09f66cff1244f0a30f44cb
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.WifiScanner;
43import android.net.wifi.WpsInfo;
44import android.net.wifi.WpsResult;
45import android.os.Environment;
46import android.os.RemoteException;
47import android.os.SystemClock;
48import android.os.UserHandle;
49import android.os.UserManager;
50import android.provider.Settings;
51import android.security.KeyStore;
52import android.text.TextUtils;
53import android.util.LocalLog;
54import android.util.Log;
55import android.util.SparseArray;
56
57import com.android.internal.R;
58import com.android.server.LocalServices;
59import com.android.server.net.DelayedDiskWrite;
60import com.android.server.net.IpConfigStore;
61import com.android.server.wifi.anqp.ANQPElement;
62import com.android.server.wifi.anqp.ANQPFactory;
63import com.android.server.wifi.anqp.Constants;
64import com.android.server.wifi.hotspot2.ANQPData;
65import com.android.server.wifi.hotspot2.AnqpCache;
66import com.android.server.wifi.hotspot2.IconEvent;
67import com.android.server.wifi.hotspot2.NetworkDetail;
68import com.android.server.wifi.hotspot2.PasspointMatch;
69import com.android.server.wifi.hotspot2.SupplicantBridge;
70import com.android.server.wifi.hotspot2.Utils;
71import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
72import com.android.server.wifi.hotspot2.pps.Credential;
73import com.android.server.wifi.hotspot2.pps.HomeSP;
74
75import org.xml.sax.SAXException;
76
77import java.io.BufferedReader;
78import java.io.DataOutputStream;
79import java.io.File;
80import java.io.FileDescriptor;
81import java.io.FileNotFoundException;
82import java.io.FileReader;
83import java.io.IOException;
84import java.io.PrintWriter;
85import java.text.DateFormat;
86import java.util.ArrayList;
87import java.util.Calendar;
88import java.util.Collection;
89import java.util.Collections;
90import java.util.Comparator;
91import java.util.Date;
92import java.util.HashMap;
93import java.util.HashSet;
94import java.util.List;
95import java.util.Map;
96import java.util.Objects;
97import java.util.Set;
98import java.util.concurrent.ConcurrentHashMap;
99import java.util.concurrent.atomic.AtomicBoolean;
100import java.util.concurrent.atomic.AtomicInteger;
101import java.util.regex.Pattern;
102import java.util.zip.CRC32;
103import java.util.zip.Checksum;
104
105
106/**
107 * This class provides the API to manage configured
108 * wifi networks. The API is not thread safe is being
109 * used only from WifiStateMachine.
110 *
111 * It deals with the following
112 * - Add/update/remove a WifiConfiguration
113 *   The configuration contains two types of information.
114 *     = IP and proxy configuration that is handled by WifiConfigManager and
115 *       is saved to disk on any change.
116 *
117 *       The format of configuration file is as follows:
118 *       <version>
119 *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
120 *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
121 *       ..
122 *
123 *       (key, value) pairs for a given network are grouped together and can
124 *       be in any order. A EOS at the end of a set of (key, value) pairs
125 *       indicates that the next set of (key, value) pairs are for a new
126 *       network. A network is identified by a unique ID_KEY. If there is no
127 *       ID_KEY in the (key, value) pairs, the data is discarded.
128 *
129 *       An invalid version on read would result in discarding the contents of
130 *       the file. On the next write, the latest version is written to file.
131 *
132 *       Any failures during read or write to the configuration file are ignored
133 *       without reporting to the user since the likelihood of these errors are
134 *       low and the impact on connectivity is low.
135 *
136 *     = SSID & security details that is pushed to the supplicant.
137 *       supplicant saves these details to the disk on calling
138 *       saveConfigCommand().
139 *
140 *       We have two kinds of APIs exposed:
141 *        > public API calls that provide fine grained control
142 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
143 *          removeNetwork(). For these calls, the config is not persisted
144 *          to the disk. (TODO: deprecate these calls in WifiManager)
145 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
146 *          These calls persist the supplicant config to disk.
147 *
148 * - Maintain a list of configured networks for quick access
149 *
150 */
151public class WifiConfigManager {
152
153    private Context mContext;
154    public static final String TAG = "WifiConfigManager";
155    private static final boolean DBG = true;
156    private static boolean VDBG = false;
157    private static boolean VVDBG = false;
158
159    private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
160
161    /* configured networks with network id as the key */
162    private final ConfigurationMap mConfiguredNetworks;
163
164    /* A network id is a unique identifier for a network configured in the
165     * supplicant. Network ids are generated when the supplicant reads
166     * the configuration file at start and can thus change for networks.
167     * We store the IP configuration for networks along with a unique id
168     * that is generated from SSID and security type of the network. A mapping
169     * from the generated unique id to network id of the network is needed to
170     * map supplicant config to IP configuration. */
171
172    /* Stores a map of NetworkId to ScanCache */
173    private ConcurrentHashMap<Integer, ScanDetailCache> mScanDetailCaches;
174
175    /**
176     * Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user,
177     * so as, framework knows not to re-add those SSIDs automatically to the Saved networks
178     */
179    private Set<Long> mDeletedSSIDs = new HashSet<Long>();
180
181    /**
182     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
183     * so as, framework knows not to autojoin again those SSIDs based on scorer input.
184     * The list is never cleared up.
185     *
186     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
187     */
188    public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
189
190    /* Tracks the highest priority of configured networks */
191    private int mLastPriority = -1;
192
193    private static final String ipConfigFile = Environment.getDataDirectory() +
194            "/misc/wifi/ipconfig.txt";
195
196    // The Wifi verbose log is provided as a way to persist the verbose logging settings
197    // for testing purpose.
198    // It is not intended for normal use.
199    private static final String WIFI_VERBOSE_LOGS_KEY
200            = "WIFI_VERBOSE_LOGS";
201
202    // As we keep deleted PSK WifiConfiguration for a while, the PSK of
203    // those deleted WifiConfiguration is set to this random unused PSK
204    private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
205
206    /**
207     * The threshold for each kind of error. If a network continuously encounter the same error more
208     * than the threshold times, this network will be disabled. -1 means unavailable.
209     */
210    private static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
211            -1, //  threshold for NETWORK_SELECTION_ENABLE
212            1,  //  threshold for DISABLED_BAD_LINK
213            5,  //  threshold for DISABLED_ASSOCIATION_REJECTION
214            5,  //  threshold for DISABLED_AUTHENTICATION_FAILURE
215            5,  //  threshold for DISABLED_DHCP_FAILURE
216            5,  //  threshold for DISABLED_DNS_FAILURE
217            6,  //  threshold for DISABLED_TLS_VERSION_MISMATCH
218            1,  //  threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
219            1,  //  threshold for DISABLED_NO_INTERNET
220            1   //  threshold for DISABLED_BY_WIFI_MANAGER
221    };
222
223    /**
224     * Timeout for each kind of error. After the timeout minutes, unblock the network again.
225     */
226    private static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT = {
227            Integer.MAX_VALUE,  // threshold for NETWORK_SELECTION_ENABLE
228            15,                 // threshold for DISABLED_BAD_LINK
229            5,                  // threshold for DISABLED_ASSOCIATION_REJECTION
230            5,                  // threshold for DISABLED_AUTHENTICATION_FAILURE
231            5,                  // threshold for DISABLED_DHCP_FAILURE
232            5,                  // threshold for DISABLED_DNS_FAILURE
233            Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
234            Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
235            Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET
236            Integer.MAX_VALUE   // threshold for DISABLED_BY_WIFI_MANAGER
237    };
238
239    public int maxTxPacketForFullScans = 8;
240    public int maxRxPacketForFullScans = 16;
241
242    public int maxTxPacketForPartialScans = 40;
243    public int maxRxPacketForPartialScans = 80;
244
245    public int associatedFullScanMaxIntervalMilli = 300000;
246
247    // Sane value for roam blacklisting (not switching to a network if already associated)
248    // 2 days
249    public int networkSwitchingBlackListPeriodMilli = 2 * 24 * 60 * 60 * 1000;
250
251    public int badLinkSpeed24 = 6;
252    public int badLinkSpeed5 = 12;
253    public int goodLinkSpeed24 = 24;
254    public int goodLinkSpeed5 = 36;
255
256    public int maxAuthErrorsToBlacklist = 4;
257    public int maxConnectionErrorsToBlacklist = 4;
258    public int wifiConfigBlacklistMinTimeMilli = 1000 * 60 * 5;
259
260    // How long a disconnected config remain considered as the last user selection
261    public int wifiConfigLastSelectionHysteresis = 1000 * 60 * 3;
262
263    // Boost RSSI values of associated networks
264    public int associatedHysteresisHigh = +14;
265    public int associatedHysteresisLow = +8;
266
267    boolean showNetworks = true; // TODO set this back to false, used for debugging 17516271
268
269    public boolean roamOnAny = false;
270    public boolean onlyLinkSameCredentialConfigurations = true;
271
272    public boolean enableLinkDebouncing = true;
273    public boolean enable5GHzPreference = true;
274    public boolean enableWifiCellularHandoverUserTriggeredAdjustment = true;
275
276    public static final int maxNumScanCacheEntries = 128;
277
278    public final AtomicBoolean enableHalBasedPno = new AtomicBoolean(false);
279    public final AtomicBoolean enableSsidWhitelist = new AtomicBoolean(false);
280    public final AtomicBoolean enableAutoJoinWhenAssociated = new AtomicBoolean(true);
281    public final AtomicBoolean enableFullBandScanWhenAssociated = new AtomicBoolean(true);
282    public final AtomicBoolean enableChipWakeUpWhenAssociated = new AtomicBoolean(true);
283    public final AtomicBoolean enableRssiPollWhenAssociated = new AtomicBoolean(true);
284    public AtomicInteger thresholdSaturatedRssi5 = new AtomicInteger(
285            WifiQualifiedNetworkSelector.RSSI_SATURATION_5G_BAND);
286    public AtomicInteger thresholdQualifiedRssi5 = new AtomicInteger(
287            WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND);
288    public AtomicInteger thresholdMinimumRssi5 = new AtomicInteger(
289            WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI);
290    public AtomicInteger thresholdSaturatedRssi24 = new AtomicInteger(
291            WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND);
292    public AtomicInteger thresholdQualifiedRssi24 = new AtomicInteger(
293            WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND);
294    public AtomicInteger thresholdMinimumRssi24 = new AtomicInteger(
295            WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI);
296    public final AtomicInteger maxTxPacketForNetworkSwitching = new AtomicInteger(40);
297    public final AtomicInteger maxRxPacketForNetworkSwitching = new AtomicInteger(80);
298    public final AtomicInteger enableVerboseLogging = new AtomicInteger(0);
299    public final AtomicInteger associatedFullScanBackoff =
300            new AtomicInteger(12); // Will be divided by 8 by WifiStateMachine
301    public final AtomicInteger alwaysEnableScansWhileAssociated = new AtomicInteger(0);
302    public final AtomicInteger maxNumPassiveChannelsForPartialScans = new AtomicInteger(2);
303    public final AtomicInteger maxNumActiveChannelsForPartialScans = new AtomicInteger(6);
304    public final AtomicInteger wifiDisconnectedShortScanIntervalMilli = new AtomicInteger(15000);
305    public final AtomicInteger wifiDisconnectedLongScanIntervalMilli = new AtomicInteger(120000);
306    public final AtomicInteger wifiAssociatedShortScanIntervalMilli = new AtomicInteger(20000);
307    public final AtomicInteger wifiAssociatedLongScanIntervalMilli = new AtomicInteger(180000);
308    public AtomicInteger currentNetworkBoost = new AtomicInteger(
309            WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD);
310    public AtomicInteger bandAward5Ghz = new AtomicInteger(
311            WifiQualifiedNetworkSelector.BAND_AWARD_5GHz);
312
313    /**
314     * Regex pattern for extracting a connect choice.
315     * Matches a strings like the following:
316     * <configKey>=([0:9]+)
317     */
318    private static Pattern mConnectChoice =
319            Pattern.compile("(.*)=([0-9]+)");
320
321
322    /* Enterprise configuration keys */
323    /**
324     * In old configurations, the "private_key" field was used. However, newer
325     * configurations use the key_id field with the engine_id set to "keystore".
326     * If this field is found in the configuration, the migration code is
327     * triggered.
328     */
329    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
330
331    /**
332     * This represents an empty value of an enterprise field.
333     * NULL is used at wpa_supplicant to indicate an empty value
334     */
335    static final String EMPTY_VALUE = "NULL";
336
337    /**
338     * If Connectivity Service has triggered an unwanted network disconnect
339     */
340    public long lastUnwantedNetworkDisconnectTimestamp = 0;
341
342    /**
343     * The maximum number of times we will retry a connection to an access point
344     * for which we have failed in acquiring an IP address from DHCP. A value of
345     * N means that we will make N+1 connection attempts in all.
346     * <p>
347     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
348     * value if a Settings value is not present.
349     */
350    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
351
352
353    private final LocalLog mLocalLog;
354
355    private final KeyStore mKeyStore;
356
357    private IpConfigStore mIpconfigStore;
358    private DelayedDiskWrite mWriter;
359
360    private final WifiNetworkHistory mWifiNetworkHistory;
361    private final WifiConfigStore mWifiConfigStore;
362
363    /**
364     * The lastSelectedConfiguration is used to remember which network
365     * was selected last by the user.
366     * The connection to this network may not be successful, as well
367     * the selection (i.e. network priority) might not be persisted.
368     * WiFi state machine is the only object that sets this variable.
369     */
370    private String lastSelectedConfiguration = null;
371    private long mLastSelectedTimeStamp =
372            WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
373
374    /*
375     * BSSID blacklist, i.e. list of BSSID we want to avoid
376     */
377    HashSet<String> mBssidBlacklist = new HashSet<String>();
378
379    /*
380     * Lost config list, whenever we read a config from networkHistory.txt that was not in
381     * wpa_supplicant.conf
382     */
383    HashSet<String> mLostConfigsDbg = new HashSet<String>();
384
385    private final AnqpCache mAnqpCache;
386    private final SupplicantBridge mSupplicantBridge;
387    private final SupplicantBridgeCallbacks mSupplicantBridgeCallbacks;
388
389    private final PasspointManagementObjectManager mMOManager;
390    private final boolean mEnableOsuQueries;
391    private final SIMAccessor mSIMAccessor;
392
393    private WifiStateMachine mWifiStateMachine;
394    private FrameworkFacade mFacade;
395    private Clock mClock;
396
397    private class SupplicantBridgeCallbacks implements SupplicantBridge.SupplicantBridgeCallbacks {
398        @Override
399        public void notifyANQPResponse(ScanDetail scanDetail,
400                                       Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
401            updateAnqpCache(scanDetail, anqpElements);
402            if (anqpElements == null || anqpElements.isEmpty()) {
403                return;
404            }
405            scanDetail.propagateANQPInfo(anqpElements);
406
407            Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
408            Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() + " pass 2 matches: "
409                    + toMatchString(matches));
410
411            cacheScanResultForPasspointConfigs(scanDetail, matches, null);
412        }
413        @Override
414        public void notifyIconFailed(long bssid) {
415            Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION);
416            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
417            intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, bssid);
418            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
419        }
420
421    }
422
423    WifiConfigManager(Context context,  WifiStateMachine wifiStateMachine, WifiNative wifiNative,
424            FrameworkFacade facade, Clock clock, UserManager userManager, KeyStore keyStore) {
425        mContext = context;
426        mFacade = facade;
427        mClock = clock;
428        mWifiStateMachine = wifiStateMachine;
429        mKeyStore = keyStore;
430
431        if (showNetworks) {
432            mLocalLog = wifiNative.getLocalLog();
433        } else {
434            mLocalLog = null;
435        }
436
437        wifiAssociatedShortScanIntervalMilli.set(mContext.getResources().getInteger(
438                R.integer.config_wifi_associated_short_scan_interval));
439        wifiAssociatedLongScanIntervalMilli.set(mContext.getResources().getInteger(
440                R.integer.config_wifi_associated_short_scan_interval));
441        wifiDisconnectedShortScanIntervalMilli.set(mContext.getResources().getInteger(
442                R.integer.config_wifi_disconnected_short_scan_interval));
443        wifiDisconnectedLongScanIntervalMilli.set(mContext.getResources().getInteger(
444                R.integer.config_wifi_disconnected_long_scan_interval));
445
446        onlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
447                R.bool.config_wifi_only_link_same_credential_configurations);
448        maxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
449                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
450        maxNumPassiveChannelsForPartialScans.set(mContext.getResources().getInteger(
451                R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels));
452        associatedFullScanMaxIntervalMilli = mContext.getResources().getInteger(
453                R.integer.config_wifi_framework_associated_full_scan_max_interval);
454        associatedFullScanBackoff.set(mContext.getResources().getInteger(
455                R.integer.config_wifi_framework_associated_full_scan_backoff));
456        enableLinkDebouncing = mContext.getResources().getBoolean(
457                R.bool.config_wifi_enable_disconnection_debounce);
458
459        enable5GHzPreference = mContext.getResources().getBoolean(
460                R.bool.config_wifi_enable_5GHz_preference);
461
462        bandAward5Ghz.set(mContext.getResources().getInteger(
463                R.integer.config_wifi_framework_5GHz_preference_boost_factor));
464
465        associatedHysteresisHigh = mContext.getResources().getInteger(
466                R.integer.config_wifi_framework_current_association_hysteresis_high);
467        associatedHysteresisLow = mContext.getResources().getInteger(
468                R.integer.config_wifi_framework_current_association_hysteresis_low);
469
470        thresholdMinimumRssi5.set(mContext.getResources().getInteger(
471                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
472        thresholdQualifiedRssi5.set(mContext.getResources().getInteger(
473                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
474        thresholdSaturatedRssi5.set(mContext.getResources().getInteger(
475                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
476        thresholdMinimumRssi24.set(mContext.getResources().getInteger(
477                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
478        thresholdQualifiedRssi24.set(mContext.getResources().getInteger(
479                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
480        thresholdSaturatedRssi24.set(mContext.getResources().getInteger(
481                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
482
483        enableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
484                R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
485
486        badLinkSpeed24 = mContext.getResources().getInteger(
487                R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
488        badLinkSpeed5 = mContext.getResources().getInteger(
489                R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
490        goodLinkSpeed24 = mContext.getResources().getInteger(
491                R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
492        goodLinkSpeed5 = mContext.getResources().getInteger(
493                R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
494
495        maxAuthErrorsToBlacklist = mContext.getResources().getInteger(
496                R.integer.config_wifi_framework_max_auth_errors_to_blacklist);
497        maxConnectionErrorsToBlacklist = mContext.getResources().getInteger(
498                R.integer.config_wifi_framework_max_connection_errors_to_blacklist);
499        wifiConfigBlacklistMinTimeMilli = mContext.getResources().getInteger(
500                R.integer.config_wifi_framework_network_black_list_min_time_milli);
501
502        enableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
503                R.bool.config_wifi_framework_enable_associated_network_selection));
504
505        currentNetworkBoost.set(mContext.getResources().getInteger(
506                R.integer.config_wifi_framework_current_network_boost));
507        networkSwitchingBlackListPeriodMilli = mContext.getResources().getInteger(
508                R.integer.config_wifi_network_switching_blacklist_time);
509
510        enableHalBasedPno.set(mContext.getResources().getBoolean(
511                        R.bool.config_wifi_hal_pno_enable));
512
513        enableSsidWhitelist.set(mContext.getResources().getBoolean(
514                R.bool.config_wifi_ssid_white_list_enable));
515        if (!enableHalBasedPno.get() && enableSsidWhitelist.get()) {
516            enableSsidWhitelist.set(false);
517        }
518
519        boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled);
520        Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled"));
521
522        mConfiguredNetworks = new ConfigurationMap(userManager);
523        mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on);
524        mEnableOsuQueries = true;
525        mAnqpCache = new AnqpCache(mClock);
526        mSupplicantBridgeCallbacks = new SupplicantBridgeCallbacks();
527        mSupplicantBridge = new SupplicantBridge(wifiNative, mSupplicantBridgeCallbacks);
528        mScanDetailCaches = new ConcurrentHashMap<>(16, 0.75f, 2);
529        mSIMAccessor = new SIMAccessor(mContext);
530        mWriter = new DelayedDiskWrite();
531        mIpconfigStore = new IpConfigStore(mWriter);
532        mWifiNetworkHistory = new WifiNetworkHistory(context, mLocalLog, mWriter);
533        mWifiConfigStore =
534                new WifiConfigStore(wifiNative, mKeyStore, mLocalLog, showNetworks, true);
535    }
536
537    public void trimANQPCache(boolean all) {
538        mAnqpCache.clear(all, DBG);
539    }
540
541    void enableVerboseLogging(int verbose) {
542        enableVerboseLogging.set(verbose);
543        if (verbose > 0) {
544            VDBG = true;
545            showNetworks = true;
546        } else {
547            VDBG = false;
548        }
549        if (verbose > 1) {
550            VVDBG = true;
551        } else {
552            VVDBG = false;
553        }
554    }
555
556    /**
557     * Fetch the list of configured networks
558     * and enable all stored networks in supplicant.
559     */
560    void loadAndEnableAllNetworks() {
561        if (DBG) log("Loading config and enabling all networks ");
562        loadConfiguredNetworks();
563        enableAllNetworks();
564    }
565
566    int getConfiguredNetworksSize() {
567        return mConfiguredNetworks.sizeForCurrentUser();
568    }
569
570    private List<WifiConfiguration>
571    getConfiguredNetworks(Map<String, String> pskMap) {
572        List<WifiConfiguration> networks = new ArrayList<>();
573        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
574            WifiConfiguration newConfig = new WifiConfiguration(config);
575            // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to
576            // correctly handle updating existing configs that are filtered out here.
577            if (config.ephemeral) {
578                // Do not enumerate and return this configuration to any one,
579                // for instance WiFi Picker.
580                // instead treat it as unknown. the configuration can still be retrieved
581                // directly by the key or networkId
582                continue;
583            }
584
585            if (pskMap != null && config.allowedKeyManagement != null
586                    && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
587                    && pskMap.containsKey(config.SSID)) {
588                newConfig.preSharedKey = pskMap.get(config.SSID);
589            }
590            networks.add(newConfig);
591        }
592        return networks;
593    }
594
595    /**
596     * This function returns all configuration, and is used for cebug and creating bug reports.
597     */
598    private List<WifiConfiguration>
599    getAllConfiguredNetworks() {
600        List<WifiConfiguration> networks = new ArrayList<>();
601        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
602            WifiConfiguration newConfig = new WifiConfiguration(config);
603            networks.add(newConfig);
604        }
605        return networks;
606    }
607
608    /**
609     * Fetch the list of currently configured networks
610     * @return List of networks
611     */
612    public List<WifiConfiguration> getConfiguredNetworks() {
613        return getConfiguredNetworks(null);
614    }
615
616    /**
617     * Fetch the list of currently configured networks, filled with real preSharedKeys
618     * @return List of networks
619     */
620    List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
621        Map<String, String> pskMap = getCredentialsBySsidMap();
622        List<WifiConfiguration> configurations = getConfiguredNetworks(pskMap);
623        for (WifiConfiguration configuration : configurations) {
624            try {
625                configuration
626                        .setPasspointManagementObjectTree(mMOManager.getMOTree(configuration.FQDN));
627            } catch (IOException ioe) {
628                Log.w(TAG, "Failed to parse MO from " + configuration.FQDN + ": " + ioe);
629            }
630        }
631        return configurations;
632    }
633
634    /**
635     * Find matching network for this scanResult
636     */
637    WifiConfiguration getMatchingConfig(ScanResult scanResult) {
638
639        for (Map.Entry entry : mScanDetailCaches.entrySet()) {
640            Integer netId = (Integer) entry.getKey();
641            ScanDetailCache cache = (ScanDetailCache) entry.getValue();
642            WifiConfiguration config = getWifiConfiguration(netId);
643            if (config == null)
644                continue;
645            if (cache.get(scanResult.BSSID) != null) {
646                return config;
647            }
648        }
649
650        return null;
651    }
652
653    /**
654     * Fetch the preSharedKeys for all networks.
655     * @return a map from Ssid to preSharedKey.
656     */
657    private Map<String, String> getCredentialsBySsidMap() {
658        return readNetworkVariablesFromSupplicantFile("psk");
659    }
660
661    /**
662     * Fetch the list of currently configured networks that were recently seen
663     *
664     * @return List of networks
665     */
666    List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
667        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
668
669        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
670            if (config.ephemeral) {
671                // Do not enumerate and return this configuration to any one,
672                // instead treat it as unknown. the configuration can still be retrieved
673                // directly by the key or networkId
674                continue;
675            }
676
677            // Calculate the RSSI for scan results that are more recent than milli
678            ScanDetailCache cache = getScanDetailCache(config);
679            if (cache == null) {
680                continue;
681            }
682            config.setVisibility(cache.getVisibility(milli));
683            if (config.visibility == null) {
684                continue;
685            }
686            if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
687                    config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
688                continue;
689            }
690            if (copy) {
691                networks.add(new WifiConfiguration(config));
692            } else {
693                networks.add(config);
694            }
695        }
696        return networks;
697    }
698
699    /**
700     *  Update the configuration and BSSID with latest RSSI value.
701     */
702    void updateConfiguration(WifiInfo info) {
703        WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
704        if (config != null && getScanDetailCache(config) != null) {
705            ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
706            if (scanDetail != null) {
707                ScanResult result = scanDetail.getScanResult();
708                long previousSeen = result.seen;
709                int previousRssi = result.level;
710
711                // Update the scan result
712                scanDetail.setSeen();
713                result.level = info.getRssi();
714
715                // Average the RSSI value
716                result.averageRssi(previousRssi, previousSeen,
717                        WifiQualifiedNetworkSelector.SCAN_RESULT_MAXIMUNM_AGE);
718                if (VDBG) {
719                    loge("updateConfiguration freq=" + result.frequency
720                        + " BSSID=" + result.BSSID
721                        + " RSSI=" + result.level
722                        + " " + config.configKey());
723                }
724            }
725        }
726    }
727
728    /**
729     * get the Wificonfiguration for this netId
730     *
731     * @return Wificonfiguration
732     */
733    public WifiConfiguration getWifiConfiguration(int netId) {
734        return mConfiguredNetworks.getForCurrentUser(netId);
735    }
736
737    /**
738     * Get the Wificonfiguration for this key
739     * @return Wificonfiguration
740     */
741    public WifiConfiguration getWifiConfiguration(String key) {
742        return mConfiguredNetworks.getByConfigKeyForCurrentUser(key);
743    }
744
745    /**
746     * Enable all networks (if disabled time expire) and save config. This will be a no-op if the
747     * list of configured networks indicates all networks as being enabled
748     */
749    void enableAllNetworks() {
750        boolean networkEnabledStateChanged = false;
751
752        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
753            if (config != null && !config.ephemeral
754                    && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
755                if (tryEnableQualifiedNetwork(config)) {
756                    networkEnabledStateChanged = true;
757                }
758            }
759        }
760
761        if (networkEnabledStateChanged) {
762            saveConfig();
763            sendConfiguredNetworksChangedBroadcast();
764        }
765    }
766
767    /**
768     * Enable a network in wpa_supplicant.
769     */
770    boolean enableNetworkNative(WifiConfiguration config) {
771        return mWifiConfigStore.enableNetwork(config);
772    }
773
774    /**
775     * Enable all networks in wpa_supplicant.
776     */
777    void enableAllNetworksNative() {
778        if (mWifiConfigStore.enableAllNetworks(mConfiguredNetworks.valuesForCurrentUser())) {
779            sendConfiguredNetworksChangedBroadcast();
780        }
781    }
782
783    private boolean setNetworkPriorityNative(WifiConfiguration config, int priority) {
784        return mWifiConfigStore.setNetworkPriority(config, priority);
785    }
786
787    private boolean setSSIDNative(WifiConfiguration config, String ssid) {
788        return mWifiConfigStore.setNetworkSSID(config, ssid);
789    }
790
791    public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
792        if (config != null) {
793            if (config.lastConnectUid != uid) {
794                config.lastConnectUid = uid;
795                return true;
796            }
797        }
798        return false;
799    }
800
801    /**
802     * Selects the specified network for connection. This involves
803     * updating the priority of all the networks and enabling the given
804     * network while disabling others.
805     *
806     * Selecting a network will leave the other networks disabled and
807     * a call to enableAllNetworks() needs to be issued upon a connection
808     * or a failure event from supplicant
809     *
810     * @param config network to select for connection
811     * @param updatePriorities makes config highest priority network
812     * @return false if the network id is invalid
813     */
814    boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
815        if (VDBG) localLogNetwork("selectNetwork", config.networkId);
816        if (config.networkId == INVALID_NETWORK_ID) return false;
817        if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
818                mWifiStateMachine.getCurrentUserProfiles())) {
819            loge("selectNetwork " + Integer.toString(config.networkId) + ": Network config is not "
820                    + "visible to current user.");
821            return false;
822        }
823
824        // Reset the priority of each network at start or if it goes too high.
825        if (mLastPriority == -1 || mLastPriority > 1000000) {
826            if (updatePriorities) {
827                for (WifiConfiguration config2 : mConfiguredNetworks.valuesForCurrentUser()) {
828                    if (config2.networkId != INVALID_NETWORK_ID) {
829                        setNetworkPriorityNative(config2, 0);
830                    }
831                }
832            }
833            mLastPriority = 0;
834        }
835
836        // Set to the highest priority and save the configuration.
837        if (updatePriorities) {
838            setNetworkPriorityNative(config, ++mLastPriority);
839        }
840
841        if (config.isPasspoint()) {
842            /* need to slap on the SSID of selected bssid to work */
843            if (getScanDetailCache(config).size() != 0) {
844                ScanDetail result = getScanDetailCache(config).getFirst();
845                if (result == null) {
846                    loge("Could not find scan result for " + config.BSSID);
847                } else {
848                    log("Setting SSID for " + config.networkId + " to" + result.getSSID());
849                    setSSIDNative(config, result.getSSID());
850                }
851
852            } else {
853                loge("Could not find bssid for " + config);
854            }
855        }
856
857        mWifiConfigStore.enableHS20(config.isPasspoint());
858
859        if (updatePriorities)
860            saveConfig();
861
862        updateLastConnectUid(config, uid);
863
864        writeKnownNetworkHistory();
865
866        /* Enable the given network while disabling all other networks */
867        selectNetworkWithoutBroadcast(config.networkId);
868
869       /* Avoid saving the config & sending a broadcast to prevent settings
870        * from displaying a disabled list of networks */
871        return true;
872    }
873
874    /**
875     * Add/update the specified configuration and save config
876     *
877     * @param config WifiConfiguration to be saved
878     * @return network update result
879     */
880    NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
881        WifiConfiguration conf;
882
883        // A new network cannot have null SSID
884        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
885                config.SSID == null)) {
886            return new NetworkUpdateResult(INVALID_NETWORK_ID);
887        }
888
889        if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
890                mWifiStateMachine.getCurrentUserProfiles())) {
891            return new NetworkUpdateResult(INVALID_NETWORK_ID);
892        }
893
894        if (VDBG) localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId);
895        if (VDBG) {
896            logd("WifiConfigManager saveNetwork,"
897                    + " size=" + Integer.toString(mConfiguredNetworks.sizeForAllUsers())
898                    + " (for all users)"
899                    + " SSID=" + config.SSID
900                    + " Uid=" + Integer.toString(config.creatorUid)
901                    + "/" + Integer.toString(config.lastUpdateUid));
902        }
903
904        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
905            if (VDBG) {
906                loge("WifiConfigManager: removed from ephemeral blacklist: " + config.SSID);
907            }
908            // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
909            // below, since we're creating/modifying a config.
910        }
911
912        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
913        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
914        int netId = result.getNetworkId();
915
916        if (VDBG) localLogNetwork("WifiConfigManager: saveNetwork got it back netId=", netId);
917
918        conf = mConfiguredNetworks.getForCurrentUser(netId);
919        if (conf != null) {
920            if (!conf.getNetworkSelectionStatus().isNetworkEnabled()) {
921                if (VDBG) localLog("WifiConfigManager: re-enabling: " + conf.SSID);
922
923                // reenable autojoin, since new information has been provided
924                updateNetworkSelectionStatus(netId,
925                        WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
926            }
927            if (VDBG) {
928                loge("WifiConfigManager: saveNetwork got config back netId="
929                        + Integer.toString(netId)
930                        + " uid=" + Integer.toString(config.creatorUid));
931            }
932        }
933
934        saveConfig();
935        sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?
936                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
937        return result;
938    }
939
940    void noteRoamingFailure(WifiConfiguration config, int reason) {
941        if (config == null) return;
942        config.lastRoamingFailure = System.currentTimeMillis();
943        config.roamingFailureBlackListTimeMilli
944                = 2 * (config.roamingFailureBlackListTimeMilli + 1000);
945        if (config.roamingFailureBlackListTimeMilli
946                > networkSwitchingBlackListPeriodMilli) {
947            config.roamingFailureBlackListTimeMilli =
948                    networkSwitchingBlackListPeriodMilli;
949        }
950        config.lastRoamingFailureReason = reason;
951    }
952
953    void saveWifiConfigBSSID(WifiConfiguration config, String bssid) {
954        mWifiConfigStore.setNetworkBSSID(config, bssid);
955    }
956
957
958    void updateStatus(int netId, DetailedState state) {
959        if (netId != INVALID_NETWORK_ID) {
960            WifiConfiguration config = mConfiguredNetworks.getForAllUsers(netId);
961            if (config == null) return;
962            switch (state) {
963                case CONNECTED:
964                    config.status = Status.CURRENT;
965                    //we successfully connected, hence remove the blacklist
966                    updateNetworkSelectionStatus(netId,
967                            WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
968                    break;
969                case DISCONNECTED:
970                    //If network is already disabled, keep the status
971                    if (config.status == Status.CURRENT) {
972                        config.status = Status.ENABLED;
973                    }
974                    break;
975                default:
976                    //do nothing, retain the existing state
977                    break;
978            }
979        }
980    }
981
982
983    /**
984     * Disable an ephemeral SSID for the purpose of auto-joining thru scored.
985     * This SSID will never be scored anymore.
986     * The only way to "un-disable it" is if the user create a network for that SSID and then
987     * forget it.
988     *
989     * @param SSID caller must ensure that the SSID passed thru this API match
990     *            the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
991     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
992     *         disconnect if this is the current network.
993     */
994    WifiConfiguration disableEphemeralNetwork(String SSID) {
995        if (SSID == null) {
996            return null;
997        }
998
999        WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeralForCurrentUser(SSID);
1000
1001        mDeletedEphemeralSSIDs.add(SSID);
1002        loge("Forget ephemeral SSID " + SSID + " num=" + mDeletedEphemeralSSIDs.size());
1003
1004        if (foundConfig != null) {
1005            loge("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId);
1006        }
1007
1008        writeKnownNetworkHistory();
1009        return foundConfig;
1010    }
1011
1012    /**
1013     * Forget the specified network and save config
1014     *
1015     * @param netId network to forget
1016     * @return {@code true} if it succeeds, {@code false} otherwise
1017     */
1018    boolean forgetNetwork(int netId) {
1019        if (showNetworks) localLogNetwork("forgetNetwork", netId);
1020        if (!removeNetwork(netId)) {
1021            loge("Failed to forget network " + netId);
1022            return false;
1023        }
1024        saveConfig();
1025        writeKnownNetworkHistory();
1026        return true;
1027    }
1028
1029    /**
1030     * Add/update a network. Note that there is no saveConfig operation.
1031     * This function is retained for compatibility with the public
1032     * API. The more powerful saveNetwork() is used by the
1033     * state machine
1034     *
1035     * @param config wifi configuration to add/update
1036     * @return network Id
1037     */
1038    int addOrUpdateNetwork(WifiConfiguration config, int uid) {
1039        if (config == null || !WifiConfigurationUtil.isVisibleToAnyProfile(config,
1040                mWifiStateMachine.getCurrentUserProfiles())) {
1041            return WifiConfiguration.INVALID_NETWORK_ID;
1042        }
1043
1044        if (showNetworks) localLogNetwork("addOrUpdateNetwork id=", config.networkId);
1045        if (config.isPasspoint()) {
1046            /* create a temporary SSID with providerFriendlyName */
1047            Long csum = getChecksum(config.FQDN);
1048            config.SSID = csum.toString();
1049            config.enterpriseConfig.setDomainSuffixMatch(config.FQDN);
1050        }
1051
1052        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
1053        if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
1054            WifiConfiguration conf = mConfiguredNetworks.getForCurrentUser(result.getNetworkId());
1055            if (conf != null) {
1056                sendConfiguredNetworksChangedBroadcast(conf,
1057                    result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
1058                            WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1059            }
1060        }
1061
1062        return result.getNetworkId();
1063    }
1064
1065    public int addPasspointManagementObject(String managementObject) {
1066        try {
1067            mMOManager.addSP(managementObject);
1068            return 0;
1069        } catch (IOException | SAXException e) {
1070            return -1;
1071        }
1072    }
1073
1074    public int modifyPasspointMo(String fqdn, List<PasspointManagementObjectDefinition> mos) {
1075        try {
1076            return mMOManager.modifySP(fqdn, mos);
1077        } catch (IOException | SAXException e) {
1078            return -1;
1079        }
1080    }
1081
1082    public boolean queryPasspointIcon(long bssid, String fileName) {
1083        return mSupplicantBridge.doIconQuery(bssid, fileName);
1084    }
1085
1086    public int matchProviderWithCurrentNetwork(String fqdn) {
1087        ScanDetail scanDetail = mWifiStateMachine.getActiveScanDetail();
1088        if (scanDetail == null) {
1089            return PasspointMatch.None.ordinal();
1090        }
1091        HomeSP homeSP = mMOManager.getHomeSP(fqdn);
1092        if (homeSP == null) {
1093            return PasspointMatch.None.ordinal();
1094        }
1095
1096        ANQPData anqpData = mAnqpCache.getEntry(scanDetail.getNetworkDetail());
1097
1098        Map<Constants.ANQPElementType, ANQPElement> anqpElements =
1099                anqpData != null ? anqpData.getANQPElements() : null;
1100
1101        return homeSP.match(scanDetail.getNetworkDetail(), anqpElements, mSIMAccessor).ordinal();
1102    }
1103
1104    /**
1105     * General PnoNetwork list sorting algorithm:
1106     * 1, Place the fully enabled networks first. Among the fully enabled networks,
1107     * sort them in the oder determined by the return of |compareConfigurations| method
1108     * implementation.
1109     * 2. Next place all the temporarily disabled networks. Among the temporarily disabled
1110     * networks, sort them in the order determined by the return of |compareConfigurations| method
1111     * implementation.
1112     * 3. Place the permanently disabled networks last. The order among permanently disabled
1113     * networks doesn't matter.
1114     */
1115    private static class PnoListComparator implements Comparator<WifiConfiguration> {
1116
1117        public final int ENABLED_NETWORK_SCORE = 3;
1118        public final int TEMPORARY_DISABLED_NETWORK_SCORE = 2;
1119        public final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1;
1120
1121        @Override
1122        public int compare(WifiConfiguration a, WifiConfiguration b) {
1123            int configAScore = getPnoNetworkSortScore(a);
1124            int configBScore = getPnoNetworkSortScore(b);
1125            if (configAScore == configBScore) {
1126                return compareConfigurations(a, b);
1127            } else {
1128                return Integer.compare(configBScore, configAScore);
1129            }
1130        }
1131
1132        // This needs to be implemented by the connected/disconnected PNO list comparator.
1133        public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
1134            return 0;
1135        }
1136
1137        /**
1138         * Returns an integer representing a score for each configuration. The scores are assigned
1139         * based on the status of the configuration. The scores are assigned according to the order:
1140         * Fully enabled network > Temporarily disabled network > Permanently disabled network.
1141         */
1142        private int getPnoNetworkSortScore(WifiConfiguration config) {
1143            if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
1144                return ENABLED_NETWORK_SCORE;
1145            } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
1146                return TEMPORARY_DISABLED_NETWORK_SCORE;
1147            } else {
1148                return PERMANENTLY_DISABLED_NETWORK_SCORE;
1149            }
1150        }
1151    }
1152
1153    /**
1154     * Disconnected PnoNetwork list sorting algorithm:
1155     * Place the configurations in descending order of their |numAssociation| values. If networks
1156     * have the same |numAssociation|, then sort them in descending order of their |priority|
1157     * values.
1158     */
1159    private static final PnoListComparator sDisconnectedPnoListComparator =
1160            new PnoListComparator() {
1161                @Override
1162                public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
1163                    if (a.numAssociation != b.numAssociation) {
1164                        return Long.compare(b.numAssociation, a.numAssociation);
1165                    } else {
1166                        return Integer.compare(b.priority, a.priority);
1167                    }
1168                }
1169            };
1170
1171    /**
1172     * Retrieves an updated list of priorities for all the saved networks before
1173     * enabling disconnected PNO (wpa_supplicant based PNO).
1174     *
1175     * @return list of networks with updated priorities.
1176     */
1177    public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveDisconnectedPnoNetworkList() {
1178        return retrievePnoNetworkList(true, sDisconnectedPnoListComparator);
1179    }
1180
1181    /**
1182     * Retrieves an updated list of priorities for all the saved networks before
1183     * enabling/disabling disconnected PNO (wpa_supplicant based PNO).
1184     *
1185     * wpa_supplicant uses the priority of networks to build the list of SSID's to monitor
1186     * during PNO. If there are a lot of saved networks, this list will be truncated and we
1187     * might end up not connecting to the networks we use most frequently. So, We want the networks
1188     * to be re-sorted based on the relative |numAssociation| values.
1189     *
1190     * @param enablePno boolean indicating whether PNO is being enabled or disabled.
1191     * @return list of networks with updated priorities.
1192     */
1193    public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveDisconnectedPnoNetworkList(
1194            boolean enablePno) {
1195        return retrievePnoNetworkList(enablePno, sDisconnectedPnoListComparator);
1196    }
1197
1198    /**
1199     * Connected PnoNetwork list sorting algorithm:
1200     * Place the configurations with |lastSeenInQualifiedNetworkSelection| set first. If networks
1201     * have the same value, then sort them in descending order of their |numAssociation|
1202     * values.
1203     */
1204    private static final PnoListComparator sConnectedPnoListComparator =
1205            new PnoListComparator() {
1206                @Override
1207                public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
1208                    boolean isConfigALastSeen =
1209                            a.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection();
1210                    boolean isConfigBLastSeen =
1211                            b.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection();
1212                    if (isConfigALastSeen != isConfigBLastSeen) {
1213                        return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
1214                    } else {
1215                        return Long.compare(b.numAssociation, a.numAssociation);
1216                    }
1217                }
1218            };
1219
1220    /**
1221     * Retrieves an updated list of priorities for all the saved networks before
1222     * enabling connected PNO (HAL based ePno).
1223     *
1224     * @return list of networks with updated priorities.
1225     */
1226    public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveConnectedPnoNetworkList() {
1227        return retrievePnoNetworkList(true, sConnectedPnoListComparator);
1228    }
1229
1230    /**
1231     * Create a PnoNetwork object from the provided WifiConfiguration.
1232     * @param config Configuration corresponding to the network.
1233     * @param newPriority New priority to be assigned to the network.
1234     */
1235    private static WifiScanner.PnoSettings.PnoNetwork createPnoNetworkFromWifiConfiguration(
1236            WifiConfiguration config, int newPriority) {
1237        WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
1238                new WifiScanner.PnoSettings.PnoNetwork(config.SSID);
1239        pnoNetwork.networkId = config.networkId;
1240        pnoNetwork.priority = newPriority;
1241        if (config.hiddenSSID) {
1242            pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN;
1243        }
1244        pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND;
1245        pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND;
1246        if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
1247            pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK;
1248        } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
1249                || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
1250            pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL;
1251        } else {
1252            pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN;
1253        }
1254        return pnoNetwork;
1255    }
1256
1257    /**
1258     * Retrieves an updated list of priorities for all the saved networks before
1259     * enabling/disabling PNO.
1260     *
1261     * @param enablePno boolean indicating whether PNO is being enabled or disabled.
1262     * @return list of networks with updated priorities.
1263     */
1264    private ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList(
1265            boolean enablePno, PnoListComparator pnoListComparator) {
1266        ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
1267        ArrayList<WifiConfiguration> wifiConfigurations =
1268                new ArrayList<>(mConfiguredNetworks.valuesForCurrentUser());
1269        if (enablePno) {
1270            Collections.sort(wifiConfigurations, pnoListComparator);
1271            // Let's use the network list size as the highest priority and then go down from there.
1272            // So, the most frequently connected network has the highest priority now.
1273            int priority = wifiConfigurations.size();
1274            if (DBG) {
1275                Log.d(TAG, "Retrieve network priorities before PNO. Max priority: " + priority);
1276            }
1277            for (WifiConfiguration config : wifiConfigurations) {
1278                pnoList.add(createPnoNetworkFromWifiConfiguration(config, priority));
1279                priority--;
1280            }
1281        } else {
1282            // Revert the priorities back to the saved config values after PNO.
1283            if (DBG) Log.d(TAG, "Retrieve network priorities after PNO.");
1284            for (WifiConfiguration config : wifiConfigurations) {
1285                pnoList.add(createPnoNetworkFromWifiConfiguration(config, config.priority));
1286            }
1287        }
1288        return pnoList;
1289    }
1290
1291    /**
1292     * Remove a network. Note that there is no saveConfig operation.
1293     * This function is retained for compatibility with the public
1294     * API. The more powerful forgetNetwork() is used by the
1295     * state machine for network removal
1296     *
1297     * @param netId network to be removed
1298     * @return {@code true} if it succeeds, {@code false} otherwise
1299     */
1300    boolean removeNetwork(int netId) {
1301        if (showNetworks) localLogNetwork("removeNetwork", netId);
1302        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1303        if (!removeConfigAndSendBroadcastIfNeeded(config)) {
1304            return false;
1305        }
1306        if (config.isPasspoint()) {
1307            writePasspointConfigs(config.FQDN, null);
1308        }
1309        return true;
1310    }
1311
1312    static private Long getChecksum(String source) {
1313        Checksum csum = new CRC32();
1314        csum.update(source.getBytes(), 0, source.getBytes().length);
1315        return csum.getValue();
1316    }
1317
1318    private boolean removeConfigWithoutBroadcast(WifiConfiguration config) {
1319        if (config == null) {
1320            return false;
1321        }
1322        if (!mWifiConfigStore.removeNetwork(config)) {
1323            loge("Failed to remove network " + config.networkId);
1324            return false;
1325        }
1326        if (config.configKey().equals(lastSelectedConfiguration)) {
1327            lastSelectedConfiguration = null;
1328        }
1329        mConfiguredNetworks.remove(config.networkId);
1330        mScanDetailCaches.remove(config.networkId);
1331        return true;
1332    }
1333
1334    private boolean removeConfigAndSendBroadcastIfNeeded(WifiConfiguration config) {
1335        if (!removeConfigWithoutBroadcast(config)) {
1336            return false;
1337        }
1338        String key = config.configKey();
1339        if (VDBG) {
1340            logd("removeNetwork " + " key=" + key + " config.id=" + config.networkId);
1341        }
1342        if (config.selfAdded || config.linkedConfigurations != null
1343                || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1344            if (!TextUtils.isEmpty(config.SSID)) {
1345                    /* Remember that we deleted this PSK SSID */
1346                if (config.SSID != null) {
1347                    Long csum = getChecksum(config.SSID);
1348                    mDeletedSSIDs.add(csum);
1349                    logd("removeNetwork "
1350                            + " key=" + key
1351                            + " config.id=" + config.networkId
1352                            + "  crc=" + csum);
1353                } else {
1354                    logd("removeNetwork "
1355                            + " key=" + key
1356                            + " config.id=" + config.networkId);
1357                }
1358            }
1359        }
1360        writeIpAndProxyConfigurations();
1361        sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
1362        if (!config.ephemeral) {
1363            removeUserSelectionPreference(key);
1364        }
1365        writeKnownNetworkHistory();
1366        return true;
1367    }
1368
1369    private void removeUserSelectionPreference(String configKey) {
1370        if (DBG) {
1371            Log.d(TAG, "removeUserSelectionPreference: key is " + configKey);
1372        }
1373        if (configKey == null) {
1374            return;
1375        }
1376        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1377            WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1378            String connectChoice = status.getConnectChoice();
1379            if (connectChoice != null && connectChoice.equals(configKey)) {
1380                Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
1381                        + " : " + config.networkId);
1382                status.setConnectChoice(null);
1383                status.setConnectChoiceTimestamp(WifiConfiguration.NetworkSelectionStatus
1384                            .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1385            }
1386        }
1387    }
1388
1389    /*
1390     * Remove all networks associated with an application
1391     *
1392     * @param packageName name of the package of networks to remove
1393     * @return {@code true} if all networks removed successfully, {@code false} otherwise
1394     */
1395    boolean removeNetworksForApp(ApplicationInfo app) {
1396        if (app == null || app.packageName == null) {
1397            return false;
1398        }
1399
1400        boolean success = true;
1401
1402        WifiConfiguration [] copiedConfigs =
1403                mConfiguredNetworks.valuesForCurrentUser().toArray(new WifiConfiguration[0]);
1404        for (WifiConfiguration config : copiedConfigs) {
1405            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
1406                continue;
1407            }
1408            if (showNetworks) {
1409                localLog("Removing network " + config.SSID
1410                         + ", application \"" + app.packageName + "\" uninstalled"
1411                         + " from user " + UserHandle.getUserId(app.uid));
1412            }
1413            success &= removeNetwork(config.networkId);
1414        }
1415
1416        saveConfig();
1417
1418        return success;
1419    }
1420
1421    boolean removeNetworksForUser(int userId) {
1422        boolean success = true;
1423
1424        WifiConfiguration[] copiedConfigs =
1425                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1426        for (WifiConfiguration config : copiedConfigs) {
1427            if (userId != UserHandle.getUserId(config.creatorUid)) {
1428                continue;
1429            }
1430            success &= removeNetwork(config.networkId);
1431            if (showNetworks) {
1432                localLog("Removing network " + config.SSID
1433                        + ", user " + userId + " removed");
1434            }
1435        }
1436
1437        return success;
1438    }
1439
1440    /**
1441     * Enable a network. Note that there is no saveConfig operation.
1442     * This function is retained for compatibility with the public
1443     * API. The more powerful selectNetwork()/saveNetwork() is used by the
1444     * state machine for connecting to a network
1445     *
1446     * @param netId network to be enabled
1447     * @return {@code true} if it succeeds, {@code false} otherwise
1448     */
1449    boolean enableNetwork(int netId, boolean disableOthers, int uid) {
1450        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1451        if (config == null) {
1452            return false;
1453        }
1454        boolean ret = true;
1455        if (disableOthers) {
1456            ret = selectNetworkWithoutBroadcast(netId);
1457            if (VDBG) localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId);
1458            updateLastConnectUid(getWifiConfiguration(netId), uid);
1459
1460            writeKnownNetworkHistory();
1461            sendConfiguredNetworksChangedBroadcast();
1462        } else {
1463            if (VDBG) localLogNetwork("enableNetwork(disableOthers=false) ", netId);
1464            WifiConfiguration enabledNetwork;
1465            synchronized(mConfiguredNetworks) {                     // !!! Useless synchronization!
1466                enabledNetwork = mConfiguredNetworks.getForCurrentUser(netId);
1467            }
1468            // check just in case the network was removed by someone else.
1469            if (enabledNetwork != null) {
1470                sendConfiguredNetworksChangedBroadcast(enabledNetwork,
1471                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1472            }
1473        }
1474        return ret;
1475    }
1476
1477    boolean selectNetworkWithoutBroadcast(int netId) {
1478        return mWifiConfigStore.selectNetwork(
1479                mConfiguredNetworks.getForCurrentUser(netId),
1480                mConfiguredNetworks.valuesForCurrentUser());
1481    }
1482
1483    /**
1484     * Disable a network in wpa_supplicant.
1485     */
1486    boolean disableNetworkNative(WifiConfiguration config) {
1487        return mWifiConfigStore.disableNetwork(config);
1488    }
1489
1490    /**
1491     * Disable all networks in wpa_supplicant.
1492     */
1493    void disableAllNetworksNative() {
1494        mWifiConfigStore.disableAllNetworks(mConfiguredNetworks.valuesForCurrentUser());
1495    }
1496
1497    /**
1498     * Disable a network. Note that there is no saveConfig operation.
1499     * @param netId network to be disabled
1500     * @return {@code true} if it succeeds, {@code false} otherwise
1501     */
1502    boolean disableNetwork(int netId) {
1503        return mWifiConfigStore.disableNetwork(mConfiguredNetworks.getForCurrentUser(netId));
1504    }
1505
1506    /**
1507     * Update a network according to the update reason and its current state
1508     * @param netId The network ID of the network need update
1509     * @param reason The reason to update the network
1510     * @return false if no change made to the input configure file, can due to error or need not
1511     *         true the input config file has been changed
1512     */
1513    boolean updateNetworkSelectionStatus(int netId, int reason) {
1514        WifiConfiguration config = getWifiConfiguration(netId);
1515        return updateNetworkSelectionStatus(config, reason);
1516    }
1517
1518    /**
1519     * Update a network according to the update reason and its current state
1520     * @param config the network need update
1521     * @param reason The reason to update the network
1522     * @return false if no change made to the input configure file, can due to error or need not
1523     *         true the input config file has been changed
1524     */
1525    boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1526        if (config == null) {
1527            return false;
1528        }
1529
1530        WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1531        if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1532            updateNetworkStatus(config, WifiConfiguration.NetworkSelectionStatus
1533                    .NETWORK_SELECTION_ENABLE);
1534            localLog("Enable network:" + config.configKey());
1535            return true;
1536        }
1537
1538        networkStatus.incrementDisableReasonCounter(reason);
1539        if (DBG) {
1540            localLog("Network:" + config.SSID + "disable counter of "
1541                    + WifiConfiguration.NetworkSelectionStatus.getNetworkDisableReasonString(reason)
1542                    + " is: " + networkStatus.getDisableReasonCounter(reason) + "and threshold is: "
1543                    + NETWORK_SELECTION_DISABLE_THRESHOLD[reason]);
1544        }
1545
1546        if (networkStatus.getDisableReasonCounter(reason)
1547                >= NETWORK_SELECTION_DISABLE_THRESHOLD[reason]) {
1548            return updateNetworkStatus(config, reason);
1549        }
1550        return true;
1551    }
1552
1553    /**
1554     * Check the config. If it is temporarily disabled, check the disable time is expired or not, If
1555     * expired, enabled it again for qualified network selection.
1556     * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1557     * @return true if network status has been changed
1558     *         false network status is not changed
1559     */
1560    boolean tryEnableQualifiedNetwork(int networkId) {
1561        WifiConfiguration config = getWifiConfiguration(networkId);
1562        if (config == null) {
1563            localLog("updateQualifiedNetworkstatus invalid network.");
1564            return false;
1565        }
1566        return tryEnableQualifiedNetwork(config);
1567    }
1568
1569    /**
1570     * Check the config. If it is temporarily disabled, check the disable is expired or not, If
1571     * expired, enabled it again for qualified network selection.
1572     * @param config network to be checked for possible unblock (due to timeout)
1573     * @return true if network status has been changed
1574     *         false network status is not changed
1575     */
1576    boolean tryEnableQualifiedNetwork(WifiConfiguration config) {
1577        WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1578        if (networkStatus.isNetworkTemporaryDisabled()) {
1579            //time difference in minutes
1580            long timeDifference = (System.currentTimeMillis()
1581                    - networkStatus.getDisableTime()) / 1000 / 60;
1582            if (timeDifference < 0 || timeDifference
1583                    >= NETWORK_SELECTION_DISABLE_TIMEOUT[
1584                    networkStatus.getNetworkSelectionDisableReason()]) {
1585                updateNetworkSelectionStatus(config.networkId,
1586                        networkStatus.NETWORK_SELECTION_ENABLE);
1587                return true;
1588            }
1589        }
1590        return false;
1591    }
1592
1593    /**
1594     * Update a network's status. Note that there is no saveConfig operation.
1595     * @param config network to be updated
1596     * @param reason reason code for updated
1597     * @return false if no change made to the input configure file, can due to error or need not
1598     *         true the input config file has been changed
1599     */
1600    boolean updateNetworkStatus(WifiConfiguration config, int reason) {
1601        localLog("updateNetworkStatus:" + (config == null ? null : config.SSID));
1602        if (config == null) {
1603            return false;
1604        }
1605
1606        WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1607        if (reason < 0 || reason >= WifiConfiguration.NetworkSelectionStatus
1608                .NETWORK_SELECTION_DISABLED_MAX) {
1609            localLog("Invalid Network disable reason:" + reason);
1610            return false;
1611        }
1612
1613        if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1614            if (networkStatus.isNetworkEnabled()) {
1615                if (DBG) {
1616                    localLog("Need not change Qualified network Selection status since"
1617                            + " already enabled");
1618                }
1619                return false;
1620            }
1621            networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
1622                    .NETWORK_SELECTION_ENABLED);
1623            networkStatus.setNetworkSelectionDisableReason(reason);
1624            networkStatus.setDisableTime(
1625                    WifiConfiguration.NetworkSelectionStatus
1626                    .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1627            networkStatus.clearDisableReasonCounter();
1628            String disableTime = DateFormat.getDateTimeInstance().format(new Date());
1629            if (DBG) {
1630                localLog("Re-enable network: " + config.SSID + " at " + disableTime);
1631            }
1632            sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1633        } else {
1634            //disable the network
1635            if (networkStatus.isNetworkPermanentlyDisabled()) {
1636                //alreay permanent disable
1637                if (DBG) {
1638                    localLog("Do nothing. Alreay permanent disabled! "
1639                            + WifiConfiguration.NetworkSelectionStatus
1640                            .getNetworkDisableReasonString(reason));
1641                }
1642                return false;
1643            } else if (networkStatus.isNetworkTemporaryDisabled()
1644                    && reason < WifiConfiguration.NetworkSelectionStatus
1645                    .DISABLED_TLS_VERSION_MISMATCH) {
1646                //alreay temporarily disable
1647                if (DBG) {
1648                    localLog("Do nothing. Already temporarily disabled! "
1649                            + WifiConfiguration.NetworkSelectionStatus
1650                            .getNetworkDisableReasonString(reason));
1651                }
1652                return false;
1653            }
1654
1655            if (networkStatus.isNetworkEnabled()) {
1656                disableNetworkNative(config);
1657                sendConfiguredNetworksChangedBroadcast(config,
1658                        WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1659                localLog("Disable network " + config.SSID + " reason:"
1660                        + WifiConfiguration.NetworkSelectionStatus
1661                        .getNetworkDisableReasonString(reason));
1662            }
1663            if (reason < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
1664                networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
1665                        .NETWORK_SELECTION_TEMPORARY_DISABLED);
1666                networkStatus.setDisableTime(System.currentTimeMillis());
1667            } else {
1668                networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
1669                        .NETWORK_SELECTION_PERMANENTLY_DISABLED);
1670            }
1671            networkStatus.setNetworkSelectionDisableReason(reason);
1672            if (DBG) {
1673                String disableTime = DateFormat.getDateTimeInstance().format(new Date());
1674                localLog("Network:" + config.SSID + "Configure new status:"
1675                        + networkStatus.getNetworkStatusString() + " with reason:"
1676                        + networkStatus.getNetworkDisableReasonString() + " at: " + disableTime);
1677            }
1678        }
1679        return true;
1680    }
1681
1682    /**
1683     * Save the configured networks in supplicant to disk
1684     * @return {@code true} if it succeeds, {@code false} otherwise
1685     */
1686    boolean saveConfig() {
1687        return mWifiConfigStore.saveConfig();
1688    }
1689
1690    /**
1691     * Start WPS pin method configuration with pin obtained
1692     * from the access point
1693     * @param config WPS configuration
1694     * @return Wps result containing status and pin
1695     */
1696    WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
1697        return mWifiConfigStore.startWpsWithPinFromAccessPoint(
1698                config, mConfiguredNetworks.valuesForCurrentUser());
1699    }
1700
1701    /**
1702     * Start WPS pin method configuration with obtained
1703     * from the device
1704     * @return WpsResult indicating status and pin
1705     */
1706    WpsResult startWpsWithPinFromDevice(WpsInfo config) {
1707        return mWifiConfigStore.startWpsWithPinFromDevice(
1708            config, mConfiguredNetworks.valuesForCurrentUser());
1709    }
1710
1711    /**
1712     * Start WPS push button configuration
1713     * @param config WPS configuration
1714     * @return WpsResult indicating status and pin
1715     */
1716    WpsResult startWpsPbc(WpsInfo config) {
1717        return mWifiConfigStore.startWpsPbc(
1718            config, mConfiguredNetworks.valuesForCurrentUser());
1719    }
1720
1721    /**
1722     * Fetch the static IP configuration for a given network id
1723     */
1724    StaticIpConfiguration getStaticIpConfiguration(int netId) {
1725        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1726        if (config != null) {
1727            return config.getStaticIpConfiguration();
1728        }
1729        return null;
1730    }
1731
1732    /**
1733     * Set the static IP configuration for a given network id
1734     */
1735    void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
1736        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1737        if (config != null) {
1738            config.setStaticIpConfiguration(staticIpConfiguration);
1739        }
1740    }
1741
1742    /**
1743     * set default GW MAC address
1744     */
1745    void setDefaultGwMacAddress(int netId, String macAddress) {
1746        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1747        if (config != null) {
1748            //update defaultGwMacAddress
1749            config.defaultGwMacAddress = macAddress;
1750        }
1751    }
1752
1753
1754    /**
1755     * Fetch the proxy properties for a given network id
1756     * @param netId id
1757     * @return ProxyInfo for the network id
1758     */
1759    ProxyInfo getProxyProperties(int netId) {
1760        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1761        if (config != null) {
1762            return config.getHttpProxy();
1763        }
1764        return null;
1765    }
1766
1767    /**
1768     * Return if the specified network is using static IP
1769     * @param netId id
1770     * @return {@code true} if using static ip for netId
1771     */
1772    boolean isUsingStaticIp(int netId) {
1773        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1774        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
1775            return true;
1776        }
1777        return false;
1778    }
1779
1780    boolean isEphemeral(int netId) {
1781        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
1782        return config != null && config.ephemeral;
1783    }
1784
1785    /**
1786     * Should be called when a single network configuration is made.
1787     * @param network The network configuration that changed.
1788     * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
1789     * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
1790     */
1791    private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
1792            int reason) {
1793        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1794        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1795        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
1796        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
1797        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
1798        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1799    }
1800
1801    /**
1802     * Should be called when multiple network configuration changes are made.
1803     */
1804    private void sendConfiguredNetworksChangedBroadcast() {
1805        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
1806        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1807        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
1808        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1809    }
1810
1811    void loadConfiguredNetworks() {
1812
1813        final Map<String, WifiConfiguration> configs = new HashMap<>();
1814        final SparseArray<Map<String, String>> networkExtras = new SparseArray<>();
1815        mLastPriority = mWifiConfigStore.loadNetworks(configs, networkExtras);
1816
1817        readNetworkHistory(configs);
1818        readPasspointConfig(configs, networkExtras);
1819
1820        // We are only now updating mConfiguredNetworks for two reasons:
1821        // 1) The information required to compute configKeys is spread across wpa_supplicant.conf
1822        //    and networkHistory.txt. Thus, we had to load both files first.
1823        // 2) mConfiguredNetworks caches a Passpoint network's FQDN the moment the network is added.
1824        //    Thus, we had to load the FQDNs first.
1825        mConfiguredNetworks.clear();
1826        for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) {
1827            final String configKey = entry.getKey();
1828            final WifiConfiguration config = entry.getValue();
1829            if (!configKey.equals(config.configKey())) {
1830                if (showNetworks) {
1831                    log("Ignoring network " + config.networkId + " because the configKey loaded "
1832                            + "from wpa_supplicant.conf is not valid.");
1833                }
1834                mWifiConfigStore.removeNetwork(config);
1835                continue;
1836            }
1837            mConfiguredNetworks.put(config);
1838        }
1839
1840        readIpAndProxyConfigurations();
1841
1842        sendConfiguredNetworksChangedBroadcast();
1843
1844        if (showNetworks) {
1845            localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.sizeForAllUsers()
1846                    + " networks (for all users)");
1847        }
1848
1849        if (mConfiguredNetworks.sizeForAllUsers() == 0) {
1850            // no networks? Lets log if the file contents
1851            logKernelTime();
1852            logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE);
1853            logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE_BACKUP);
1854            logContents(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE);
1855        }
1856    }
1857
1858    private void logContents(String file) {
1859        localLogAndLogcat("--- Begin " + file + " ---");
1860        BufferedReader reader = null;
1861        try {
1862            reader = new BufferedReader(new FileReader(file));
1863            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1864                localLogAndLogcat(line);
1865            }
1866        } catch (FileNotFoundException e) {
1867            localLog("Could not open " + file + ", " + e);
1868            Log.w(TAG, "Could not open " + file + ", " + e);
1869        } catch (IOException e) {
1870            localLog("Could not read " + file + ", " + e);
1871            Log.w(TAG, "Could not read " + file + ", " + e);
1872        } finally {
1873            try {
1874                if (reader != null) {
1875                    reader.close();
1876                }
1877            } catch (IOException e) {
1878                // Just ignore the fact that we couldn't close
1879            }
1880        }
1881        localLogAndLogcat("--- End " + file + " Contents ---");
1882    }
1883
1884    private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
1885        return mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
1886    }
1887
1888    private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
1889        return mWifiConfigStore.readNetworkVariableFromSupplicantFile(ssid, key);
1890    }
1891
1892    boolean needsUnlockedKeyStore() {
1893
1894        // Any network using certificates to authenticate access requires
1895        // unlocked key store; unless the certificates can be stored with
1896        // hardware encryption
1897
1898        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1899
1900            if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
1901                    && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1902
1903                if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
1904                    return true;
1905                }
1906            }
1907        }
1908
1909        return false;
1910    }
1911
1912    void readPasspointConfig(Map<String, WifiConfiguration> configs,
1913            SparseArray<Map<String, String>> networkExtras) {
1914        List<HomeSP> homeSPs;
1915        try {
1916            homeSPs = mMOManager.loadAllSPs();
1917        } catch (IOException e) {
1918            loge("Could not read " + PPS_FILE + " : " + e);
1919            return;
1920        }
1921
1922        int matchedConfigs = 0;
1923        for (HomeSP homeSp : homeSPs) {
1924            String fqdn = homeSp.getFQDN();
1925            Log.d(TAG, "Looking for " + fqdn);
1926            for (WifiConfiguration config : configs.values()) {
1927                Log.d(TAG, "Testing " + config.SSID);
1928
1929                if (config.enterpriseConfig == null) {
1930                    continue;
1931                }
1932                final String configFqdn =
1933                        networkExtras.get(config.networkId).get(WifiConfigStore.ID_STRING_KEY_FQDN);
1934                if (configFqdn != null && configFqdn.equals(fqdn)) {
1935                    Log.d(TAG, "Matched " + configFqdn + " with " + config.networkId);
1936                    ++matchedConfigs;
1937                    config.FQDN = fqdn;
1938                    config.providerFriendlyName = homeSp.getFriendlyName();
1939
1940                    HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums();
1941                    config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
1942                    int i = 0;
1943                    for (long id : roamingConsortiumIds) {
1944                        config.roamingConsortiumIds[i] = id;
1945                        i++;
1946                    }
1947                    IMSIParameter imsiParameter = homeSp.getCredential().getImsi();
1948                    config.enterpriseConfig.setPlmn(
1949                            imsiParameter != null ? imsiParameter.toString() : null);
1950                    config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm());
1951                }
1952            }
1953        }
1954
1955        Log.d(TAG, "loaded " + matchedConfigs + " passpoint configs");
1956    }
1957
1958    public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) {
1959        mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
1960            @Override
1961            public void onWriteCalled(DataOutputStream out) throws IOException {
1962                try {
1963                    if (homeSP != null) {
1964                        mMOManager.addSP(homeSP);
1965                    } else {
1966                        mMOManager.removeSP(fqdn);
1967                    }
1968                } catch (IOException e) {
1969                    loge("Could not write " + PPS_FILE + " : " + e);
1970                }
1971            }
1972        }, false);
1973    }
1974
1975    /**
1976     *  Write network history, WifiConfigurations and mScanDetailCaches to file.
1977     */
1978    private void readNetworkHistory(Map<String, WifiConfiguration> configs) {
1979        mWifiNetworkHistory.readNetworkHistory(configs,
1980                mScanDetailCaches,
1981                mDeletedSSIDs,
1982                mDeletedEphemeralSSIDs);
1983    }
1984
1985    /**
1986     *  Read Network history from file, merge it into mConfiguredNetowrks and mScanDetailCaches
1987     */
1988    public void writeKnownNetworkHistory() {
1989        final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
1990        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
1991            networks.add(new WifiConfiguration(config));
1992        }
1993        mWifiNetworkHistory.writeKnownNetworkHistory(networks,
1994                mScanDetailCaches,
1995                mDeletedSSIDs,
1996                mDeletedEphemeralSSIDs);
1997    }
1998
1999    public void setAndEnableLastSelectedConfiguration(int netId) {
2000        if (VDBG) {
2001            loge("setLastSelectedConfiguration " + Integer.toString(netId));
2002        }
2003        if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
2004            lastSelectedConfiguration = null;
2005            mLastSelectedTimeStamp = -1;
2006        } else {
2007            WifiConfiguration selected = getWifiConfiguration(netId);
2008            if (selected == null) {
2009                lastSelectedConfiguration = null;
2010                mLastSelectedTimeStamp = -1;
2011            } else {
2012                lastSelectedConfiguration = selected.configKey();
2013                mLastSelectedTimeStamp = System.currentTimeMillis();
2014                updateNetworkSelectionStatus(netId,
2015                        WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
2016                if (VDBG) {
2017                    loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
2018                }
2019            }
2020        }
2021    }
2022
2023    public void setLatestUserSelectedConfiguration(WifiConfiguration network) {
2024        if (network != null) {
2025            lastSelectedConfiguration = network.configKey();
2026            mLastSelectedTimeStamp = System.currentTimeMillis();
2027        }
2028    }
2029
2030    public String getLastSelectedConfiguration() {
2031        return lastSelectedConfiguration;
2032    }
2033
2034    public long getLastSelectedTimeStamp() {
2035        return mLastSelectedTimeStamp;
2036    }
2037
2038    public boolean isLastSelectedConfiguration(WifiConfiguration config) {
2039        return (lastSelectedConfiguration != null
2040                && config != null
2041                && lastSelectedConfiguration.equals(config.configKey()));
2042    }
2043
2044    private void writeIpAndProxyConfigurations() {
2045        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
2046        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
2047            if (!config.ephemeral) {
2048                networks.put(configKey(config), config.getIpConfiguration());
2049            }
2050        }
2051
2052        mIpconfigStore.writeIpAndProxyConfigurations(ipConfigFile, networks);
2053    }
2054
2055    private void readIpAndProxyConfigurations() {
2056        SparseArray<IpConfiguration> networks =
2057                mIpconfigStore.readIpAndProxyConfigurations(ipConfigFile);
2058
2059        if (networks == null || networks.size() == 0) {
2060            // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
2061            return;
2062        }
2063
2064        for (int i = 0; i < networks.size(); i++) {
2065            int id = networks.keyAt(i);
2066            WifiConfiguration config = mConfiguredNetworks.getByConfigKeyIDForAllUsers(id);
2067            // This is the only place the map is looked up through a (dangerous) hash-value!
2068
2069            if (config == null || config.ephemeral) {
2070                loge("configuration found for missing network, nid=" + id
2071                        +", ignored, networks.size=" + Integer.toString(networks.size()));
2072            } else {
2073                config.setIpConfiguration(networks.valueAt(i));
2074            }
2075        }
2076    }
2077
2078    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
2079        /*
2080         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
2081         * network configuration. Otherwise, the networkId should
2082         * refer to an existing configuration.
2083         */
2084
2085        if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
2086        if (config.isPasspoint() && !mMOManager.isEnabled()) {
2087            Log.e(TAG, "Passpoint is not enabled");
2088            return new NetworkUpdateResult(INVALID_NETWORK_ID);
2089        }
2090
2091        boolean newNetwork = false;
2092        boolean existingMO = false;
2093        WifiConfiguration currentConfig;
2094        // networkId of INVALID_NETWORK_ID means we want to create a new network
2095        if (config.networkId == INVALID_NETWORK_ID) {
2096            // Try to fetch the existing config using configKey
2097            currentConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
2098            if (currentConfig != null) {
2099                config.networkId = currentConfig.networkId;
2100            } else {
2101                if (mMOManager.getHomeSP(config.FQDN) != null) {
2102                    loge("addOrUpdateNetworkNative passpoint " + config.FQDN
2103                            + " was found, but no network Id");
2104                    existingMO = true;
2105                }
2106                newNetwork = true;
2107            }
2108        } else {
2109            // Fetch the existing config using networkID
2110            currentConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
2111        }
2112        if (!mWifiConfigStore.addOrUpdateNetwork(config, currentConfig)) {
2113            return new NetworkUpdateResult(INVALID_NETWORK_ID);
2114        }
2115        int netId = config.networkId;
2116
2117        /* An update of the network variables requires reading them
2118         * back from the supplicant to update mConfiguredNetworks.
2119         * This is because some of the variables (SSID, wep keys &
2120         * passphrases) reflect different values when read back than
2121         * when written. For example, wep key is stored as * irrespective
2122         * of the value sent to the supplicant.
2123         */
2124        if (currentConfig == null) {
2125            currentConfig = new WifiConfiguration();
2126            currentConfig.setIpAssignment(IpAssignment.DHCP);
2127            currentConfig.setProxySettings(ProxySettings.NONE);
2128            currentConfig.networkId = netId;
2129            if (config != null) {
2130                // Carry over the creation parameters
2131                currentConfig.selfAdded = config.selfAdded;
2132                currentConfig.didSelfAdd = config.didSelfAdd;
2133                currentConfig.ephemeral = config.ephemeral;
2134                currentConfig.lastConnectUid = config.lastConnectUid;
2135                currentConfig.lastUpdateUid = config.lastUpdateUid;
2136                currentConfig.creatorUid = config.creatorUid;
2137                currentConfig.creatorName = config.creatorName;
2138                currentConfig.lastUpdateName = config.lastUpdateName;
2139                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
2140                currentConfig.FQDN = config.FQDN;
2141                currentConfig.providerFriendlyName = config.providerFriendlyName;
2142                currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
2143                currentConfig.validatedInternetAccess = config.validatedInternetAccess;
2144                currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
2145                currentConfig.updateTime = config.updateTime;
2146                currentConfig.creationTime = config.creationTime;
2147                currentConfig.shared = config.shared;
2148            }
2149            if (DBG) {
2150                log("created new config netId=" + Integer.toString(netId)
2151                        + " uid=" + Integer.toString(currentConfig.creatorUid)
2152                        + " name=" + currentConfig.creatorName);
2153            }
2154        }
2155
2156        /* save HomeSP object for passpoint networks */
2157        HomeSP homeSP = null;
2158
2159        if (!existingMO && config.isPasspoint()) {
2160            try {
2161                if (config.updateIdentifier == null) {   // Only create an MO for r1 networks
2162                    Credential credential =
2163                            new Credential(config.enterpriseConfig, mKeyStore, !newNetwork);
2164                    HashSet<Long> roamingConsortiumIds = new HashSet<Long>();
2165                    for (Long roamingConsortiumId : config.roamingConsortiumIds) {
2166                        roamingConsortiumIds.add(roamingConsortiumId);
2167                    }
2168
2169                    homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
2170                            roamingConsortiumIds, Collections.<String>emptySet(),
2171                            Collections.<Long>emptySet(), Collections.<Long>emptyList(),
2172                            config.providerFriendlyName, null, credential);
2173
2174                    log("created a homeSP object for " + config.networkId + ":" + config.SSID);
2175                }
2176
2177                /* fix enterprise config properties for passpoint */
2178                currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
2179                currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
2180            }
2181            catch (IOException ioe) {
2182                Log.e(TAG, "Failed to create Passpoint config: " + ioe);
2183                return new NetworkUpdateResult(INVALID_NETWORK_ID);
2184            }
2185        }
2186
2187        if (uid != WifiConfiguration.UNKNOWN_UID) {
2188            if (newNetwork) {
2189                currentConfig.creatorUid = uid;
2190            } else {
2191                currentConfig.lastUpdateUid = uid;
2192            }
2193        }
2194
2195        // For debug, record the time the configuration was modified
2196        StringBuilder sb = new StringBuilder();
2197        sb.append("time=");
2198        Calendar c = Calendar.getInstance();
2199        c.setTimeInMillis(System.currentTimeMillis());
2200        sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
2201
2202        if (newNetwork) {
2203            currentConfig.creationTime = sb.toString();
2204        } else {
2205            currentConfig.updateTime = sb.toString();
2206        }
2207
2208        if (currentConfig.status == WifiConfiguration.Status.ENABLED) {
2209            // Make sure autojoin remain in sync with user modifying the configuration
2210            updateNetworkSelectionStatus(currentConfig.networkId,
2211                    WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
2212        }
2213
2214        if (currentConfig.configKey().equals(getLastSelectedConfiguration()) &&
2215                currentConfig.ephemeral) {
2216            // Make the config non-ephemeral since the user just explicitly clicked it.
2217            currentConfig.ephemeral = false;
2218            if (DBG) log("remove ephemeral status netId=" + Integer.toString(netId)
2219                    + " " + currentConfig.configKey());
2220        }
2221
2222        if (VDBG) log("will read network variables netId=" + Integer.toString(netId));
2223
2224        readNetworkVariables(currentConfig);
2225
2226        // Persist configuration paramaters that are not saved by supplicant.
2227        if (config.lastUpdateName != null) {
2228            currentConfig.lastUpdateName = config.lastUpdateName;
2229        }
2230        if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) {
2231            currentConfig.lastUpdateUid = config.lastUpdateUid;
2232        }
2233
2234        mConfiguredNetworks.put(currentConfig);
2235
2236        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
2237        result.setIsNewNetwork(newNetwork);
2238        result.setNetworkId(netId);
2239
2240        if (homeSP != null) {
2241            writePasspointConfigs(null, homeSP);
2242        }
2243
2244        saveConfig();
2245        writeKnownNetworkHistory();
2246
2247        return result;
2248    }
2249
2250    public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
2251        WifiConfiguration config = mConfiguredNetworks.getByFQDNForCurrentUser(homeSP.getFQDN());
2252        if (config == null) {
2253            Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
2254        }
2255        return config;
2256    }
2257
2258    public HomeSP getHomeSPForConfig(WifiConfiguration config) {
2259        WifiConfiguration storedConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
2260        return storedConfig != null && storedConfig.isPasspoint() ?
2261                mMOManager.getHomeSP(storedConfig.FQDN) : null;
2262    }
2263
2264    public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
2265        if (config == null) return null;
2266        ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
2267        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
2268            cache = new ScanDetailCache(config);
2269            mScanDetailCaches.put(config.networkId, cache);
2270        }
2271        return cache;
2272    }
2273
2274    /**
2275     * This function run thru the Saved WifiConfigurations and check if some should be linked.
2276     * @param config
2277     */
2278    public void linkConfiguration(WifiConfiguration config) {
2279        if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
2280                mWifiStateMachine.getCurrentUserProfiles())) {
2281            loge("linkConfiguration: Attempting to link config " + config.configKey()
2282                    + " that is not visible to the current user.");
2283            return;
2284        }
2285
2286        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
2287            // Ignore configurations with large number of BSSIDs
2288            return;
2289        }
2290        if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
2291            // Only link WPA_PSK config
2292            return;
2293        }
2294        for (WifiConfiguration link : mConfiguredNetworks.valuesForCurrentUser()) {
2295            boolean doLink = false;
2296
2297            if (link.configKey().equals(config.configKey())) {
2298                continue;
2299            }
2300
2301            if (link.ephemeral) {
2302                continue;
2303            }
2304
2305            // Autojoin will be allowed to dynamically jump from a linked configuration
2306            // to another, hence only link configurations that have equivalent level of security
2307            if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
2308                continue;
2309            }
2310
2311            ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
2312            if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
2313                // Ignore configurations with large number of BSSIDs
2314                continue;
2315            }
2316
2317            if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
2318                // If both default GW are known, link only if they are equal
2319                if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
2320                    if (VDBG) {
2321                        loge("linkConfiguration link due to same gw " + link.SSID +
2322                                " and " + config.SSID + " GW " + config.defaultGwMacAddress);
2323                    }
2324                    doLink = true;
2325                }
2326            } else {
2327                // We do not know BOTH default gateways hence we will try to link
2328                // hoping that WifiConfigurations are indeed behind the same gateway.
2329                // once both WifiConfiguration have been tried and thus once both efault gateways
2330                // are known we will revisit the choice of linking them
2331                if ((getScanDetailCache(config) != null)
2332                        && (getScanDetailCache(config).size() <= 6)) {
2333
2334                    for (String abssid : getScanDetailCache(config).keySet()) {
2335                        for (String bbssid : linkedScanDetailCache.keySet()) {
2336                            if (VVDBG) {
2337                                loge("linkConfiguration try to link due to DBDC BSSID match "
2338                                        + link.SSID +
2339                                        " and " + config.SSID + " bssida " + abssid
2340                                        + " bssidb " + bbssid);
2341                            }
2342                            if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
2343                                // If first 16 ascii characters of BSSID matches,
2344                                // we assume this is a DBDC
2345                                doLink = true;
2346                            }
2347                        }
2348                    }
2349                }
2350            }
2351
2352            if (doLink == true && onlyLinkSameCredentialConfigurations) {
2353                String apsk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
2354                String bpsk = readNetworkVariableFromSupplicantFile(config.SSID, "psk");
2355                if (apsk == null || bpsk == null
2356                        || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk)
2357                        || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK)
2358                        || !apsk.equals(bpsk)) {
2359                    doLink = false;
2360                }
2361            }
2362
2363            if (doLink) {
2364                if (VDBG) {
2365                    loge("linkConfiguration: will link " + link.configKey()
2366                            + " and " + config.configKey());
2367                }
2368                if (link.linkedConfigurations == null) {
2369                    link.linkedConfigurations = new HashMap<String, Integer>();
2370                }
2371                if (config.linkedConfigurations == null) {
2372                    config.linkedConfigurations = new HashMap<String, Integer>();
2373                }
2374                if (link.linkedConfigurations.get(config.configKey()) == null) {
2375                    link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
2376                }
2377                if (config.linkedConfigurations.get(link.configKey()) == null) {
2378                    config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
2379                }
2380            } else {
2381                if (link.linkedConfigurations != null
2382                        && (link.linkedConfigurations.get(config.configKey()) != null)) {
2383                    if (VDBG) {
2384                        loge("linkConfiguration: un-link " + config.configKey()
2385                                + " from " + link.configKey());
2386                    }
2387                    link.linkedConfigurations.remove(config.configKey());
2388                }
2389                if (config.linkedConfigurations != null
2390                        && (config.linkedConfigurations.get(link.configKey()) != null)) {
2391                    if (VDBG) {
2392                        loge("linkConfiguration: un-link " + link.configKey()
2393                                + " from " + config.configKey());
2394                    }
2395                    config.linkedConfigurations.remove(link.configKey());
2396                }
2397            }
2398        }
2399    }
2400
2401    public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
2402        if (config == null)
2403            return null;
2404        long now_ms = System.currentTimeMillis();
2405
2406        HashSet<Integer> channels = new HashSet<Integer>();
2407
2408        //get channels for this configuration, if there are at least 2 BSSIDs
2409        if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
2410            return null;
2411        }
2412
2413        if (VDBG) {
2414            StringBuilder dbg = new StringBuilder();
2415            dbg.append("makeChannelList age=" + Integer.toString(age)
2416                    + " for " + config.configKey()
2417                    + " max=" + maxNumActiveChannelsForPartialScans);
2418            if (getScanDetailCache(config) != null) {
2419                dbg.append(" bssids=" + getScanDetailCache(config).size());
2420            }
2421            if (config.linkedConfigurations != null) {
2422                dbg.append(" linked=" + config.linkedConfigurations.size());
2423            }
2424            loge(dbg.toString());
2425        }
2426
2427        int numChannels = 0;
2428        if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
2429            for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
2430                ScanResult result = scanDetail.getScanResult();
2431                //TODO : cout active and passive channels separately
2432                if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
2433                    break;
2434                }
2435                if (VDBG) {
2436                    boolean test = (now_ms - result.seen) < age;
2437                    loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
2438                            + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
2439                }
2440                if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2441                    channels.add(result.frequency);
2442                    numChannels++;
2443                }
2444            }
2445        }
2446
2447        //get channels for linked configurations
2448        if (config.linkedConfigurations != null) {
2449            for (String key : config.linkedConfigurations.keySet()) {
2450                WifiConfiguration linked = getWifiConfiguration(key);
2451                if (linked == null)
2452                    continue;
2453                if (getScanDetailCache(linked) == null) {
2454                    continue;
2455                }
2456                for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
2457                    ScanResult result = scanDetail.getScanResult();
2458                    if (VDBG) {
2459                        loge("has link: " + result.BSSID
2460                                + " freq=" + Integer.toString(result.frequency)
2461                                + " age=" + Long.toString(now_ms - result.seen));
2462                    }
2463                    if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
2464                        break;
2465                    }
2466                    if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
2467                        channels.add(result.frequency);
2468                        numChannels++;
2469                    }
2470                }
2471            }
2472        }
2473        return channels;
2474    }
2475
2476    private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
2477        if (!mMOManager.isConfigured()) {
2478            if (mEnableOsuQueries) {
2479                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2480                List<Constants.ANQPElementType> querySet =
2481                        ANQPFactory.buildQueryList(networkDetail, false, true);
2482
2483                if (networkDetail.queriable(querySet)) {
2484                    querySet = mAnqpCache.initiate(networkDetail, querySet);
2485                    if (querySet != null) {
2486                        mSupplicantBridge.startANQP(scanDetail, querySet);
2487                    }
2488                    updateAnqpCache(scanDetail, networkDetail.getANQPElements());
2489                }
2490            }
2491            return null;
2492        }
2493        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2494        if (!networkDetail.hasInterworking()) {
2495            return null;
2496        }
2497        updateAnqpCache(scanDetail, networkDetail.getANQPElements());
2498
2499        Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true);
2500        Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
2501                " pass 1 matches: " + toMatchString(matches));
2502        return matches;
2503    }
2504
2505    private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
2506        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2507
2508        ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
2509
2510        Map<Constants.ANQPElementType, ANQPElement> anqpElements =
2511                anqpData != null ? anqpData.getANQPElements() : null;
2512
2513        boolean queried = !query;
2514        Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values();
2515        Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
2516        Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString() +
2517                ", anqp " + ( anqpData != null ? "present" : "missing" ) +
2518                ", query " + query + ", home sps: " + homeSPs.size());
2519
2520        for (HomeSP homeSP : homeSPs) {
2521            PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor);
2522
2523            Log.d(Utils.hs2LogTag(getClass()), " -- " +
2524                    homeSP.getFQDN() + ": match " + match + ", queried " + queried);
2525
2526            if ((match == PasspointMatch.Incomplete || mEnableOsuQueries) && !queried) {
2527                boolean matchSet = match == PasspointMatch.Incomplete;
2528                boolean osu = mEnableOsuQueries;
2529                List<Constants.ANQPElementType> querySet =
2530                        ANQPFactory.buildQueryList(networkDetail, matchSet, osu);
2531                if (networkDetail.queriable(querySet)) {
2532                    querySet = mAnqpCache.initiate(networkDetail, querySet);
2533                    if (querySet != null) {
2534                        mSupplicantBridge.startANQP(scanDetail, querySet);
2535                    }
2536                }
2537                queried = true;
2538            }
2539            matches.put(homeSP, match);
2540        }
2541        return matches;
2542    }
2543
2544    public Map<Constants.ANQPElementType, ANQPElement> getANQPData(NetworkDetail network) {
2545        ANQPData data = mAnqpCache.getEntry(network);
2546        return data != null ? data.getANQPElements() : null;
2547    }
2548
2549    public SIMAccessor getSIMAccessor() {
2550        return mSIMAccessor;
2551    }
2552
2553    public void notifyANQPDone(Long bssid, boolean success) {
2554        mSupplicantBridge.notifyANQPDone(bssid, success);
2555    }
2556
2557    public void notifyIconReceived(IconEvent iconEvent) {
2558        Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION);
2559        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2560        intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, iconEvent.getBSSID());
2561        intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_FILE, iconEvent.getFileName());
2562        try {
2563            intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_DATA,
2564                    mSupplicantBridge.retrieveIcon(iconEvent));
2565        } catch (IOException ioe) {
2566            /* Simply omit the icon data as a failure indication */
2567        }
2568        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2569
2570    }
2571
2572    public void wnmFrameReceived(WnmData event) {
2573        // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url
2574        // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url
2575
2576        Intent intent = new Intent(WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION);
2577        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2578
2579        intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_BSSID, event.getBssid());
2580        intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_URL, event.getUrl());
2581
2582        if (event.isDeauthEvent()) {
2583            intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_ESS, event.isEss());
2584            intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_DELAY, event.getDelay());
2585        } else {
2586            intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_METHOD, event.getMethod());
2587            WifiConfiguration config = mWifiStateMachine.getCurrentWifiConfiguration();
2588            if (config != null && config.FQDN != null) {
2589                intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH,
2590                        matchProviderWithCurrentNetwork(config.FQDN));
2591            }
2592        }
2593        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2594    }
2595
2596    private void updateAnqpCache(ScanDetail scanDetail,
2597                                 Map<Constants.ANQPElementType,ANQPElement> anqpElements)
2598    {
2599        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2600
2601        if (anqpElements == null) {
2602            // Try to pull cached data if query failed.
2603            ANQPData data = mAnqpCache.getEntry(networkDetail);
2604            if (data != null) {
2605                scanDetail.propagateANQPInfo(data.getANQPElements());
2606            }
2607            return;
2608        }
2609
2610        mAnqpCache.update(networkDetail, anqpElements);
2611    }
2612
2613    private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
2614        StringBuilder sb = new StringBuilder();
2615        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
2616            sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
2617        }
2618        return sb.toString();
2619    }
2620
2621    private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail,
2622            Map<HomeSP, PasspointMatch> matches,
2623            List<WifiConfiguration> associatedWifiConfigurations) {
2624
2625        for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
2626            PasspointMatch match = entry.getValue();
2627            if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) {
2628                WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
2629                if (config != null) {
2630                    cacheScanResultForConfig(config, scanDetail, entry.getValue());
2631                    if (associatedWifiConfigurations != null) {
2632                        associatedWifiConfigurations.add(config);
2633                    }
2634                } else {
2635		            Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '" +
2636                            entry.getKey().getFQDN() + "'");
2637                    /* perhaps the configuration was deleted?? */
2638                }
2639            }
2640        }
2641    }
2642
2643    private void cacheScanResultForConfig(
2644            WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) {
2645
2646        ScanResult scanResult = scanDetail.getScanResult();
2647
2648        ScanDetailCache scanDetailCache = getScanDetailCache(config);
2649        if (scanDetailCache == null) {
2650            Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
2651            return;
2652        }
2653
2654        // Adding a new BSSID
2655        ScanResult result = scanDetailCache.get(scanResult.BSSID);
2656        if (result != null) {
2657            // transfer the black list status
2658            scanResult.blackListTimestamp = result.blackListTimestamp;
2659            scanResult.numIpConfigFailures = result.numIpConfigFailures;
2660            scanResult.numConnection = result.numConnection;
2661            scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
2662        }
2663
2664        if (config.ephemeral) {
2665            // For an ephemeral Wi-Fi config, the ScanResult should be considered
2666            // untrusted.
2667            scanResult.untrusted = true;
2668        }
2669
2670        if (scanDetailCache.size() > (maxNumScanCacheEntries + 64)) {
2671            long now_dbg = 0;
2672            if (VVDBG) {
2673                loge(" Will trim config " + config.configKey()
2674                        + " size " + scanDetailCache.size());
2675
2676                for (ScanDetail sd : scanDetailCache.values()) {
2677                    loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
2678                }
2679                now_dbg = SystemClock.elapsedRealtimeNanos();
2680            }
2681            // Trim the scan result cache to maxNumScanCacheEntries entries max
2682            // Since this operation is expensive, make sure it is not performed
2683            // until the cache has grown significantly above the trim treshold
2684            scanDetailCache.trim(maxNumScanCacheEntries);
2685            if (VVDBG) {
2686                long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
2687                loge(" Finished trimming config, time(ns) " + diff);
2688                for (ScanDetail sd : scanDetailCache.values()) {
2689                    loge("     " + sd.getBSSIDString() + " " + sd.getSeen());
2690                }
2691            }
2692        }
2693
2694        // Add the scan result to this WifiConfiguration
2695        if (passpointMatch != null)
2696            scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config));
2697        else
2698            scanDetailCache.put(scanDetail);
2699
2700        // Since we added a scan result to this configuration, re-attempt linking
2701        linkConfiguration(config);
2702    }
2703
2704    private boolean isEncryptionWep(String encryption) {
2705        return encryption.contains("WEP");
2706    }
2707
2708    private boolean isEncryptionPsk(String encryption) {
2709        return encryption.contains("PSK");
2710    }
2711
2712    private boolean isEncryptionEap(String encryption) {
2713        return encryption.contains("EAP");
2714    }
2715
2716    public boolean isOpenNetwork(String encryption) {
2717        if (!isEncryptionWep(encryption) && !isEncryptionPsk(encryption)
2718                && !isEncryptionEap(encryption)) {
2719            return true;
2720        }
2721        return false;
2722    }
2723
2724    public boolean isOpenNetwork(ScanResult scan) {
2725        String scanResultEncrypt = scan.capabilities;
2726        return isOpenNetwork(scanResultEncrypt);
2727    }
2728
2729    public boolean isOpenNetwork(WifiConfiguration config) {
2730        String configEncrypt = config.configKey();
2731        return isOpenNetwork(configEncrypt);
2732    }
2733
2734    /**
2735     * create a mapping between the scandetail and the Wificonfiguration it associated with
2736     * because Passpoint, one BSSID can associated with multiple SSIDs
2737     * @param scanDetail input a scanDetail from the scan result
2738     * @return List<WifiConfiguration> a list of WifiConfigurations associated to this scanDetail
2739     */
2740    public List<WifiConfiguration> updateSavedNetworkWithNewScanDetail(ScanDetail scanDetail) {
2741
2742        ScanResult scanResult = scanDetail.getScanResult();
2743        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
2744        List<WifiConfiguration> associatedWifiConfigurations = new ArrayList<WifiConfiguration>();
2745
2746        if (scanResult == null)
2747            return null;
2748
2749        String SSID = "\"" + scanResult.SSID + "\"";
2750
2751        if (networkDetail.hasInterworking()) {
2752            Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
2753            if (matches != null) {
2754                cacheScanResultForPasspointConfigs(scanDetail, matches,
2755                        associatedWifiConfigurations);
2756                //Do not return here. A BSSID can belong to both passpoint network and non-passpoint
2757                //Network
2758            }
2759        }
2760
2761        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
2762            boolean found = false;
2763            if (config.SSID == null || !config.SSID.equals(SSID)) {
2764                continue;
2765            }
2766            if (DBG) {
2767                localLog("updateSavedNetworkWithNewScanDetail(): try " + config.configKey()
2768                        + " SSID=" + config.SSID + " " + scanResult.SSID + " "
2769                        + scanResult.capabilities);
2770            }
2771
2772            String scanResultEncrypt = scanResult.capabilities;
2773            String configEncrypt = config.configKey();
2774            if (isEncryptionWep(scanResultEncrypt) && isEncryptionWep(configEncrypt)
2775                    || (isEncryptionPsk(scanResultEncrypt) && isEncryptionPsk(configEncrypt))
2776                    || (isEncryptionEap(scanResultEncrypt) && isEncryptionEap(configEncrypt))
2777                    || (isOpenNetwork(scanResultEncrypt) && isOpenNetwork(configEncrypt))) {
2778                found = true;
2779            }
2780
2781            if (found) {
2782                cacheScanResultForConfig(config, scanDetail, null);
2783                associatedWifiConfigurations.add(config);
2784            }
2785        }
2786
2787        if (associatedWifiConfigurations.size() == 0) {
2788            return null;
2789        } else {
2790            return associatedWifiConfigurations;
2791        }
2792    }
2793
2794    /**
2795     * Handles the switch to a different foreground user:
2796     * - Removes all ephemeral networks
2797     * - Disables private network configurations belonging to the previous foreground user
2798     * - Enables private network configurations belonging to the new foreground user
2799     *
2800     * TODO(b/26785736): Terminate background users if the new foreground user has one or more
2801     * private network configurations.
2802     */
2803    public void handleUserSwitch() {
2804        Set<WifiConfiguration> ephemeralConfigs = new HashSet<>();
2805        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
2806            if (config.ephemeral) {
2807                ephemeralConfigs.add(config);
2808            }
2809        }
2810        if (!ephemeralConfigs.isEmpty()) {
2811            for (WifiConfiguration config : ephemeralConfigs) {
2812                removeConfigWithoutBroadcast(config);
2813            }
2814            saveConfig();
2815            writeKnownNetworkHistory();
2816        }
2817
2818        final List<WifiConfiguration> hiddenConfigurations =
2819                mConfiguredNetworks.handleUserSwitch(mWifiStateMachine.getCurrentUserId());
2820        for (WifiConfiguration network : hiddenConfigurations) {
2821            disableNetworkNative(network);
2822        }
2823        enableAllNetworks();
2824
2825        // TODO(b/26785746): This broadcast is unnecessary if either of the following is true:
2826        // * The user switch did not change the list of visible networks
2827        // * The user switch revealed additional networks that were temporarily disabled and got
2828        //   re-enabled now (because enableAllNetworks() sent the same broadcast already).
2829        sendConfiguredNetworksChangedBroadcast();
2830    }
2831
2832    /* Compare current and new configuration and write to file on change */
2833    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
2834            WifiConfiguration currentConfig,
2835            WifiConfiguration newConfig) {
2836        boolean ipChanged = false;
2837        boolean proxyChanged = false;
2838
2839        if (VDBG) {
2840            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
2841                    newConfig.SSID + " path: " + ipConfigFile);
2842        }
2843
2844
2845        switch (newConfig.getIpAssignment()) {
2846            case STATIC:
2847                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2848                    ipChanged = true;
2849                } else {
2850                    ipChanged = !Objects.equals(
2851                            currentConfig.getStaticIpConfiguration(),
2852                            newConfig.getStaticIpConfiguration());
2853                }
2854                break;
2855            case DHCP:
2856                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
2857                    ipChanged = true;
2858                }
2859                break;
2860            case UNASSIGNED:
2861                /* Ignore */
2862                break;
2863            default:
2864                loge("Ignore invalid ip assignment during write");
2865                break;
2866        }
2867
2868        switch (newConfig.getProxySettings()) {
2869            case STATIC:
2870            case PAC:
2871                ProxyInfo newHttpProxy = newConfig.getHttpProxy();
2872                ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
2873
2874                if (newHttpProxy != null) {
2875                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
2876                } else {
2877                    proxyChanged = (currentHttpProxy != null);
2878                }
2879                break;
2880            case NONE:
2881                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
2882                    proxyChanged = true;
2883                }
2884                break;
2885            case UNASSIGNED:
2886                /* Ignore */
2887                break;
2888            default:
2889                loge("Ignore invalid proxy configuration during write");
2890                break;
2891        }
2892
2893        if (ipChanged) {
2894            currentConfig.setIpAssignment(newConfig.getIpAssignment());
2895            currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
2896            log("IP config changed SSID = " + currentConfig.SSID);
2897            if (currentConfig.getStaticIpConfiguration() != null) {
2898                log(" static configuration: " +
2899                    currentConfig.getStaticIpConfiguration().toString());
2900            }
2901        }
2902
2903        if (proxyChanged) {
2904            currentConfig.setProxySettings(newConfig.getProxySettings());
2905            currentConfig.setHttpProxy(newConfig.getHttpProxy());
2906            log("proxy changed SSID = " + currentConfig.SSID);
2907            if (currentConfig.getHttpProxy() != null) {
2908                log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
2909            }
2910        }
2911
2912        if (ipChanged || proxyChanged) {
2913            writeIpAndProxyConfigurations();
2914            sendConfiguredNetworksChangedBroadcast(currentConfig,
2915                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);
2916        }
2917        return new NetworkUpdateResult(ipChanged, proxyChanged);
2918    }
2919
2920    /**
2921     * Read the variables from the supplicant daemon that are needed to
2922     * fill in the WifiConfiguration object.
2923     *
2924     * @param config the {@link WifiConfiguration} object to be filled in.
2925     */
2926    private void readNetworkVariables(WifiConfiguration config) {
2927        mWifiConfigStore.readNetworkVariables(config);
2928    }
2929
2930    /* return the allowed key management based on a scan result */
2931
2932    public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
2933
2934        WifiConfiguration config = new WifiConfiguration();
2935
2936        config.SSID = "\"" + result.SSID + "\"";
2937
2938        if (VDBG) {
2939            loge("WifiConfiguration from scan results " +
2940                    config.SSID + " cap " + result.capabilities);
2941        }
2942        if (result.capabilities.contains("WEP")) {
2943            config.allowedKeyManagement.set(KeyMgmt.NONE);
2944            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
2945            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
2946        }
2947
2948        if (result.capabilities.contains("PSK")) {
2949            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
2950        }
2951
2952        if (result.capabilities.contains("EAP")) {
2953            //this is probably wrong, as we don't have a way to enter the enterprise config
2954            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
2955            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
2956        }
2957
2958        /* getScanDetailCache(config).put(scanDetail); */
2959
2960        return config;
2961    }
2962
2963    public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
2964        ScanResult result = scanDetail.getScanResult();
2965        return wifiConfigurationFromScanResult(result);
2966    }
2967
2968    /* Returns a unique for a given configuration */
2969    private static int configKey(WifiConfiguration config) {
2970        String key = config.configKey();
2971        return key.hashCode();
2972    }
2973
2974    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2975        pw.println("Dump of WifiConfigManager");
2976        pw.println("mLastPriority " + mLastPriority);
2977        pw.println("Configured networks");
2978        for (WifiConfiguration conf : getAllConfiguredNetworks()) {
2979            pw.println(conf);
2980        }
2981        pw.println();
2982        if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) {
2983            pw.println("LostConfigs: ");
2984            for (String s : mLostConfigsDbg) {
2985                pw.println(s);
2986            }
2987        }
2988        if (mLocalLog != null) {
2989            pw.println("WifiConfigManager - Log Begin ----");
2990            mLocalLog.dump(fd, pw, args);
2991            pw.println("WifiConfigManager - Log End ----");
2992        }
2993        if (mMOManager.isConfigured()) {
2994            pw.println("Begin dump of ANQP Cache");
2995            mAnqpCache.dump(pw);
2996            pw.println("End dump of ANQP Cache");
2997        }
2998    }
2999
3000    public String getConfigFile() {
3001        return ipConfigFile;
3002    }
3003
3004    protected void logd(String s) {
3005        Log.d(TAG, s);
3006    }
3007
3008    protected void loge(String s) {
3009        loge(s, false);
3010    }
3011
3012    protected void loge(String s, boolean stack) {
3013        if (stack) {
3014            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3015                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
3016                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
3017                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
3018        } else {
3019            Log.e(TAG, s);
3020        }
3021    }
3022
3023    private void logKernelTime() {
3024        long kernelTimeMs = System.nanoTime()/(1000*1000);
3025        StringBuilder builder = new StringBuilder();
3026        builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
3027                (kernelTimeMs%1000).append("\n");
3028        localLog(builder.toString());
3029    }
3030
3031    protected void log(String s) {
3032        Log.d(TAG, s);
3033    }
3034
3035    private void localLog(String s) {
3036        if (mLocalLog != null) {
3037            mLocalLog.log(s);
3038        }
3039    }
3040
3041    private void localLogAndLogcat(String s) {
3042        localLog(s);
3043        Log.d(TAG, s);
3044    }
3045
3046    private void localLogNetwork(String s, int netId) {
3047        if (mLocalLog == null) {
3048            return;
3049        }
3050
3051        WifiConfiguration config;
3052        synchronized(mConfiguredNetworks) {             // !!! Useless synchronization
3053            config = mConfiguredNetworks.getForAllUsers(netId);
3054        }
3055
3056        if (config != null) {
3057            mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId
3058                    + " status=" + config.status
3059                    + " key=" + config.configKey());
3060        } else {
3061            mLocalLog.log(s + " " + netId);
3062        }
3063    }
3064
3065    static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
3066        String client = config.getClientCertificateAlias();
3067        if (!TextUtils.isEmpty(client)) {
3068            // a valid client certificate is configured
3069
3070            // BUGBUG: keyStore.get() never returns certBytes; because it is not
3071            // taking WIFI_UID as a parameter. It always looks for certificate
3072            // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
3073            // all certificates need software keystore until we get the get() API
3074            // fixed.
3075
3076            return true;
3077        }
3078
3079        /*
3080        try {
3081
3082            if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
3083                    .USER_CERTIFICATE + client);
3084
3085            CertificateFactory factory = CertificateFactory.getInstance("X.509");
3086            if (factory == null) {
3087                Slog.e(TAG, "Error getting certificate factory");
3088                return;
3089            }
3090
3091            byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
3092            if (certBytes != null) {
3093                Certificate cert = (X509Certificate) factory.generateCertificate(
3094                        new ByteArrayInputStream(certBytes));
3095
3096                if (cert != null) {
3097                    mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
3098
3099                    if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
3100                            .USER_CERTIFICATE + client);
3101                    if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
3102                            "does not need" ) + " software key store");
3103                } else {
3104                    Slog.d(TAG, "could not generate certificate");
3105                }
3106            } else {
3107                Slog.e(TAG, "Could not load client certificate " + Credentials
3108                        .USER_CERTIFICATE + client);
3109                mNeedsSoftwareKeystore = true;
3110            }
3111
3112        } catch(CertificateException e) {
3113            Slog.e(TAG, "Could not read certificates");
3114            mCaCert = null;
3115            mClientCertificate = null;
3116        }
3117        */
3118
3119        return false;
3120    }
3121
3122    /**
3123     * Checks if the network is a sim config.
3124     * @param config Config corresponding to the network.
3125     * @return true if it is a sim config, false otherwise.
3126     */
3127    public boolean isSimConfig(WifiConfiguration config) {
3128        return mWifiConfigStore.isSimConfig(config);
3129    }
3130
3131    /**
3132     * Resets all sim networks from the network list.
3133     */
3134    public void resetSimNetworks() {
3135        mWifiConfigStore.resetSimNetworks(mConfiguredNetworks.valuesForCurrentUser());
3136    }
3137
3138    boolean isNetworkConfigured(WifiConfiguration config) {
3139        // Check if either we have a network Id or a WifiConfiguration
3140        // matching the one we are trying to add.
3141
3142        if(config.networkId != INVALID_NETWORK_ID) {
3143            return (mConfiguredNetworks.getForCurrentUser(config.networkId) != null);
3144        }
3145
3146        return (mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()) != null);
3147    }
3148
3149    /**
3150     * Checks if uid has access to modify the configuration corresponding to networkId.
3151     *
3152     * The conditions checked are, in descending priority order:
3153     * - Disallow modification if the the configuration is not visible to the uid.
3154     * - Allow modification if the uid represents the Device Owner app.
3155     * - Allow modification if both of the following are true:
3156     *   - The uid represents the configuration's creator or an app holding OVERRIDE_CONFIG_WIFI.
3157     *   - The modification is only for administrative annotation (e.g. when connecting) or the
3158     *     configuration is not lockdown eligible (which currently means that it was not last
3159     *     updated by the DO).
3160     * - Allow modification if configuration lockdown is explicitly disabled and the uid represents
3161     *   an app holding OVERRIDE_CONFIG_WIFI.
3162     * - In all other cases, disallow modification.
3163     */
3164    boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) {
3165        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(networkId);
3166
3167        if (config == null) {
3168            loge("canModifyNetwork: cannot find config networkId " + networkId);
3169            return false;
3170        }
3171
3172        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
3173                DevicePolicyManagerInternal.class);
3174
3175        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
3176                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
3177
3178        if (isUidDeviceOwner) {
3179            return true;
3180        }
3181
3182        final boolean isCreator = (config.creatorUid == uid);
3183
3184        if (onlyAnnotate) {
3185            return isCreator || checkConfigOverridePermission(uid);
3186        }
3187
3188        // Check if device has DPM capability. If it has and dpmi is still null, then we
3189        // treat this case with suspicion and bail out.
3190        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
3191                && dpmi == null) {
3192            return false;
3193        }
3194
3195        // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner.
3196
3197        final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
3198                config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
3199        if (!isConfigEligibleForLockdown) {
3200            return isCreator || checkConfigOverridePermission(uid);
3201        }
3202
3203        final ContentResolver resolver = mContext.getContentResolver();
3204        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
3205                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
3206        return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
3207    }
3208
3209    /**
3210     * Checks if uid has access to modify config.
3211     */
3212    boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) {
3213        if (config == null) {
3214            loge("canModifyNetowrk recieved null configuration");
3215            return false;
3216        }
3217
3218        // Resolve the correct network id.
3219        int netid;
3220        if (config.networkId != INVALID_NETWORK_ID){
3221            netid = config.networkId;
3222        } else {
3223            WifiConfiguration test =
3224                    mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
3225            if (test == null) {
3226                return false;
3227            } else {
3228                netid = test.networkId;
3229            }
3230        }
3231
3232        return canModifyNetwork(uid, netid, onlyAnnotate);
3233    }
3234
3235    boolean checkConfigOverridePermission(int uid) {
3236        try {
3237            return (mFacade.checkUidPermission(
3238                    android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid)
3239                    == PackageManager.PERMISSION_GRANTED);
3240        } catch (RemoteException e) {
3241            return false;
3242        }
3243    }
3244
3245    /** called when CS ask WiFistateMachine to disconnect the current network
3246     * because the score is bad.
3247     */
3248    void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
3249        /* TODO verify the bad network is current */
3250        WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
3251        if (config != null) {
3252            if ((info.is24GHz() && info.getRssi()
3253                    <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND)
3254                    || (info.is5GHz() && info.getRssi()
3255                    <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND)) {
3256                // We do not block due to bad RSSI since network selection should not select bad
3257                // RSSI candidate
3258            } else {
3259                // We got disabled but RSSI is good, so disable hard
3260                updateNetworkSelectionStatus(config,
3261                        WifiConfiguration.NetworkSelectionStatus.DISABLED_BAD_LINK);
3262            }
3263        }
3264        // Record last time Connectivity Service switched us away from WiFi and onto Cell
3265        lastUnwantedNetworkDisconnectTimestamp = System.currentTimeMillis();
3266    }
3267
3268    int getMaxDhcpRetries() {
3269        return mFacade.getIntegerSetting(mContext,
3270                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
3271                DEFAULT_MAX_DHCP_RETRIES);
3272    }
3273
3274    void clearBssidBlacklist() {
3275        if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
3276            if(DBG) {
3277                Log.d(TAG, "No blacklist allowed without epno enabled");
3278            }
3279            return;
3280        }
3281        mWifiConfigStore.clearBssidBlacklist();
3282    }
3283
3284    void blackListBssid(String bssid) {
3285        if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
3286            if(DBG) {
3287                Log.d(TAG, "No blacklist allowed without epno enabled");
3288            }
3289            return;
3290        }
3291        mWifiConfigStore.blackListBssid(bssid);
3292    }
3293
3294    public boolean isBssidBlacklisted(String bssid) {
3295        return mWifiConfigStore.isBssidBlacklisted(bssid);
3296    }
3297
3298    public boolean getEnableNewNetworkSelectionWhenAssociated() {
3299        return enableAutoJoinWhenAssociated.get();
3300    }
3301}
3302