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