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