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