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