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