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