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