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