WifiConfigManager.java revision 98086f240b93fc059d0d2adf9ad93c06a10f8fba
1/*
2 * Copyright (C) 2016 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 android.app.ActivityManager;
20import android.app.admin.DeviceAdminInfo;
21import android.app.admin.DevicePolicyManagerInternal;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageManager;
27import android.net.IpConfiguration;
28import android.net.ProxyInfo;
29import android.net.StaticIpConfiguration;
30import android.net.wifi.ScanResult;
31import android.net.wifi.WifiConfiguration;
32import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
33import android.net.wifi.WifiEnterpriseConfig;
34import android.net.wifi.WifiInfo;
35import android.net.wifi.WifiManager;
36import android.net.wifi.WifiScanner;
37import android.os.Process;
38import android.os.UserHandle;
39import android.os.UserManager;
40import android.provider.Settings;
41import android.telephony.TelephonyManager;
42import android.text.TextUtils;
43import android.util.ArraySet;
44import android.util.LocalLog;
45import android.util.Log;
46
47import com.android.internal.R;
48import com.android.internal.annotations.VisibleForTesting;
49import com.android.server.LocalServices;
50import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy;
51import com.android.server.wifi.hotspot2.PasspointManager;
52import com.android.server.wifi.util.ScanResultUtil;
53import com.android.server.wifi.util.TelephonyUtil;
54import com.android.server.wifi.util.WifiPermissionsUtil;
55import com.android.server.wifi.util.WifiPermissionsWrapper;
56
57import org.xmlpull.v1.XmlPullParserException;
58
59import java.io.FileDescriptor;
60import java.io.IOException;
61import java.io.PrintWriter;
62import java.util.ArrayList;
63import java.util.BitSet;
64import java.util.Calendar;
65import java.util.Collection;
66import java.util.Collections;
67import java.util.HashMap;
68import java.util.HashSet;
69import java.util.Iterator;
70import java.util.List;
71import java.util.Map;
72import java.util.Set;
73
74/**
75 * This class provides the APIs to manage configured Wi-Fi networks.
76 * It deals with the following:
77 * - Maintaining a list of configured networks for quick access.
78 * - Persisting the configurations to store when required.
79 * - Supporting WifiManager Public API calls:
80 *   > addOrUpdateNetwork()
81 *   > removeNetwork()
82 *   > enableNetwork()
83 *   > disableNetwork()
84 * - Handle user switching on multi-user devices.
85 *
86 * All network configurations retrieved from this class are copies of the original configuration
87 * stored in the internal database. So, any updates to the retrieved configuration object are
88 * meaningless and will not be reflected in the original database.
89 * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored
90 * in the internal database. Any configuration updates should be triggered with appropriate helper
91 * methods of this class using the configuration's unique networkId.
92 *
93 * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread.
94 */
95public class WifiConfigManager {
96    /**
97     * String used to mask passwords to public interface.
98     */
99    @VisibleForTesting
100    public static final String PASSWORD_MASK = "*";
101    /**
102     * Package name for SysUI. This is used to lookup the UID of SysUI which is used to allow
103     * Quick settings to modify network configurations.
104     */
105    @VisibleForTesting
106    public static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
107    /**
108     * Network Selection disable reason thresholds. These numbers are used to debounce network
109     * failures before we disable them.
110     * These are indexed using the disable reason constants defined in
111     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
112     */
113    @VisibleForTesting
114    public static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
115            -1, //  threshold for NETWORK_SELECTION_ENABLE
116            1,  //  threshold for DISABLED_BAD_LINK
117            5,  //  threshold for DISABLED_ASSOCIATION_REJECTION
118            5,  //  threshold for DISABLED_AUTHENTICATION_FAILURE
119            5,  //  threshold for DISABLED_DHCP_FAILURE
120            5,  //  threshold for DISABLED_DNS_FAILURE
121            1,  //  threshold for DISABLED_WPS_START
122            6,  //  threshold for DISABLED_TLS_VERSION_MISMATCH
123            1,  //  threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
124            1,  //  threshold for DISABLED_NO_INTERNET
125            1,  //  threshold for DISABLED_BY_WIFI_MANAGER
126            1   //  threshold for DISABLED_BY_USER_SWITCH
127    };
128    /**
129     * Network Selection disable timeout for each kind of error. After the timeout milliseconds,
130     * enable the network again.
131     * These are indexed using the disable reason constants defined in
132     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
133     */
134    @VisibleForTesting
135    public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
136            Integer.MAX_VALUE,  // threshold for NETWORK_SELECTION_ENABLE
137            15 * 60 * 1000,     // threshold for DISABLED_BAD_LINK
138            5 * 60 * 1000,      // threshold for DISABLED_ASSOCIATION_REJECTION
139            5 * 60 * 1000,      // threshold for DISABLED_AUTHENTICATION_FAILURE
140            5 * 60 * 1000,      // threshold for DISABLED_DHCP_FAILURE
141            5 * 60 * 1000,      // threshold for DISABLED_DNS_FAILURE
142            0 * 60 * 1000,      // threshold for DISABLED_WPS_START
143            Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
144            Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
145            Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET
146            Integer.MAX_VALUE,  // threshold for DISABLED_BY_WIFI_MANAGER
147            Integer.MAX_VALUE   // threshold for DISABLED_BY_USER_SWITCH
148    };
149    /**
150     * Interface for other modules to listen to the saved network updated
151     * events.
152     */
153    public interface OnSavedNetworkUpdateListener {
154        /**
155         * Invoked on saved network being added.
156         */
157        void onSavedNetworkAdded(int networkId);
158        /**
159         * Invoked on saved network being enabled.
160         */
161        void onSavedNetworkEnabled(int networkId);
162        /**
163         * Invoked on saved network being permanently disabled.
164         */
165        void onSavedNetworkPermanentlyDisabled(int networkId);
166        /**
167         * Invoked on saved network being removed.
168         */
169        void onSavedNetworkRemoved(int networkId);
170        /**
171         * Invoked on saved network being temporarily disabled.
172         */
173        void onSavedNetworkTemporarilyDisabled(int networkId);
174        /**
175         * Invoked on saved network being updated.
176         */
177        void onSavedNetworkUpdated(int networkId);
178    }
179    /**
180     * Max size of scan details to cache in {@link #mScanDetailCaches}.
181     */
182    @VisibleForTesting
183    public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192;
184    /**
185     * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds
186     * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some
187     * buffer time before the next eviction.
188     */
189    @VisibleForTesting
190    public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128;
191    /**
192     * Link networks only if they have less than this number of scan cache entries.
193     */
194    @VisibleForTesting
195    public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6;
196    /**
197     * Link networks only if the bssid in scan results for the networks match in the first
198     * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7"
199     */
200    @VisibleForTesting
201    public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16;
202    /**
203     * Flags to be passed in for |canModifyNetwork| to decide if the change is minor and can
204     * bypass the lockdown checks.
205     */
206    private static final boolean ALLOW_LOCKDOWN_CHECK_BYPASS = true;
207    private static final boolean DISALLOW_LOCKDOWN_CHECK_BYPASS = false;
208    /**
209     * Log tag for this class.
210     */
211    private static final String TAG = "WifiConfigManager";
212    /**
213     * Maximum age of scan results that can be used for averaging out RSSI value.
214     */
215    private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
216    /**
217     * General sorting algorithm of all networks for scanning purposes:
218     * Place the configurations in descending order of their |numAssociation| values. If networks
219     * have the same |numAssociation|, place the configurations with
220     * |lastSeenInQualifiedNetworkSelection| set first.
221     */
222    private static final WifiConfigurationUtil.WifiConfigurationComparator sScanListComparator =
223            new WifiConfigurationUtil.WifiConfigurationComparator() {
224                @Override
225                public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
226                    if (a.numAssociation != b.numAssociation) {
227                        return Long.compare(b.numAssociation, a.numAssociation);
228                    } else {
229                        boolean isConfigALastSeen =
230                                a.getNetworkSelectionStatus()
231                                        .getSeenInLastQualifiedNetworkSelection();
232                        boolean isConfigBLastSeen =
233                                b.getNetworkSelectionStatus()
234                                        .getSeenInLastQualifiedNetworkSelection();
235                        return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
236                    }
237                }
238            };
239
240    /**
241     * List of external dependencies for WifiConfigManager.
242     */
243    private final Context mContext;
244    private final Clock mClock;
245    private final UserManager mUserManager;
246    private final BackupManagerProxy mBackupManagerProxy;
247    private final TelephonyManager mTelephonyManager;
248    private final WifiKeyStore mWifiKeyStore;
249    private final WifiConfigStore mWifiConfigStore;
250    private final WifiConfigStoreLegacy mWifiConfigStoreLegacy;
251    private final WifiPermissionsUtil mWifiPermissionsUtil;
252    private final WifiPermissionsWrapper mWifiPermissionsWrapper;
253    /**
254     * Local log used for debugging any WifiConfigManager issues.
255     */
256    private final LocalLog mLocalLog =
257            new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256);
258    /**
259     * Map of configured networks with network id as the key.
260     */
261    private final ConfigurationMap mConfiguredNetworks;
262    /**
263     * Stores a map of NetworkId to ScanDetailCache.
264     */
265    private final Map<Integer, ScanDetailCache> mScanDetailCaches;
266    /**
267     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
268     * so as, framework knows not to autoconnect again those SSIDs based on scorer input.
269     * The list is never cleared up.
270     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
271     */
272    private final Set<String> mDeletedEphemeralSSIDs;
273    /**
274     * Flag to indicate if only networks with the same psk should be linked.
275     * TODO(b/30706406): Remove this flag if unused.
276     */
277    private final boolean mOnlyLinkSameCredentialConfigurations;
278    /**
279     * Number of channels to scan for during partial scans initiated while connected.
280     */
281    private final int mMaxNumActiveChannelsForPartialScans;
282    /**
283     * Verbose logging flag. Toggled by developer options.
284     */
285    private boolean mVerboseLoggingEnabled = false;
286    /**
287     * Current logged in user ID.
288     */
289    private int mCurrentUserId = UserHandle.USER_SYSTEM;
290    /**
291     * Flag to indicate that the new user's store has not yet been read since user switch.
292     * Initialize this flag to |true| to trigger a read on the first user unlock after
293     * bootup.
294     */
295    private boolean mPendingUnlockStoreRead = true;
296    /**
297     * Flag to indicate if we have performed a read from store at all. This is used to gate
298     * any user unlock/switch operations until we read the store (Will happen if wifi is disabled
299     * when user updates from N to O).
300     */
301    private boolean mPendingStoreRead = true;
302    /**
303     * Flag to indicate if the user unlock was deferred until the store load occurs.
304     */
305    private boolean mDeferredUserUnlockRead = false;
306    /**
307     * Flag to indicate if SIM is present.
308     */
309    private boolean mSimPresent = false;
310    /**
311     * This is keeping track of the next network ID to be assigned. Any new networks will be
312     * assigned |mNextNetworkId| as network ID.
313     */
314    private int mNextNetworkId = 0;
315    /**
316     * UID of system UI. This uid is allowed to modify network configurations regardless of which
317     * user is logged in.
318     */
319    private int mSystemUiUid = -1;
320    /**
321     * This is used to remember which network was selected successfully last by an app. This is set
322     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
323     * This is the only way for an app to request connection to a specific network using the
324     * {@link WifiManager} API's.
325     */
326    private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
327    private long mLastSelectedTimeStamp =
328            WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
329
330    // Store data for network list and deleted ephemeral SSID list.  Used for serializing
331    // parsing data to/from the config store.
332    private final NetworkListStoreData mNetworkListStoreData;
333    private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData;
334
335    // Store the saved network update listener.
336    private OnSavedNetworkUpdateListener mListener = null;
337
338    /**
339     * Create new instance of WifiConfigManager.
340     */
341    WifiConfigManager(
342            Context context, Clock clock, UserManager userManager,
343            TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore,
344            WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy,
345            WifiPermissionsUtil wifiPermissionsUtil,
346            WifiPermissionsWrapper wifiPermissionsWrapper,
347            NetworkListStoreData networkListStoreData,
348            DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData) {
349        mContext = context;
350        mClock = clock;
351        mUserManager = userManager;
352        mBackupManagerProxy = new BackupManagerProxy();
353        mTelephonyManager = telephonyManager;
354        mWifiKeyStore = wifiKeyStore;
355        mWifiConfigStore = wifiConfigStore;
356        mWifiConfigStoreLegacy = wifiConfigStoreLegacy;
357        mWifiPermissionsUtil = wifiPermissionsUtil;
358        mWifiPermissionsWrapper = wifiPermissionsWrapper;
359
360        mConfiguredNetworks = new ConfigurationMap(userManager);
361        mScanDetailCaches = new HashMap<>(16, 0.75f);
362        mDeletedEphemeralSSIDs = new HashSet<>();
363
364        // Register store data for network list and deleted ephemeral SSIDs.
365        mNetworkListStoreData = networkListStoreData;
366        mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData;
367        mWifiConfigStore.registerStoreData(mNetworkListStoreData);
368        mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData);
369
370        mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
371                R.bool.config_wifi_only_link_same_credential_configurations);
372        mMaxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
373                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
374
375        try {
376            mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME,
377                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
378        } catch (PackageManager.NameNotFoundException e) {
379            Log.e(TAG, "Unable to resolve SystemUI's UID.");
380        }
381    }
382
383    /**
384     * Construct the string to be put in the |creationTime| & |updateTime| elements of
385     * WifiConfiguration from the provided wall clock millis.
386     *
387     * @param wallClockMillis Time in milliseconds to be converted to string.
388     */
389    @VisibleForTesting
390    public static String createDebugTimeStampString(long wallClockMillis) {
391        StringBuilder sb = new StringBuilder();
392        sb.append("time=");
393        Calendar c = Calendar.getInstance();
394        c.setTimeInMillis(wallClockMillis);
395        sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
396        return sb.toString();
397    }
398
399    /**
400     * Enable/disable verbose logging in WifiConfigManager & its helper classes.
401     */
402    public void enableVerboseLogging(int verbose) {
403        if (verbose > 0) {
404            mVerboseLoggingEnabled = true;
405        } else {
406            mVerboseLoggingEnabled = false;
407        }
408        mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
409        mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
410    }
411
412    /**
413     * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
414     * is needed when the network configurations are being requested via the public WifiManager
415     * API's.
416     * This currently masks the following elements: psk, wepKeys & enterprise config password.
417     */
418    private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
419        if (!TextUtils.isEmpty(configuration.preSharedKey)) {
420            configuration.preSharedKey = PASSWORD_MASK;
421        }
422        if (configuration.wepKeys != null) {
423            for (int i = 0; i < configuration.wepKeys.length; i++) {
424                if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
425                    configuration.wepKeys[i] = PASSWORD_MASK;
426                }
427            }
428        }
429        if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
430            configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
431        }
432    }
433
434    /**
435     * Helper method to create a copy of the provided internal WifiConfiguration object to be
436     * passed to external modules.
437     *
438     * @param configuration provided WifiConfiguration object.
439     * @param maskPasswords Mask passwords or not.
440     * @return Copy of the WifiConfiguration object.
441     */
442    private WifiConfiguration createExternalWifiConfiguration(
443            WifiConfiguration configuration, boolean maskPasswords) {
444        WifiConfiguration network = new WifiConfiguration(configuration);
445        if (maskPasswords) {
446            maskPasswordsInWifiConfiguration(network);
447        }
448        return network;
449    }
450
451    /**
452     * Fetch the list of currently configured networks maintained in WifiConfigManager.
453     *
454     * This retrieves a copy of the internal configurations maintained by WifiConfigManager and
455     * should be used for any public interfaces.
456     *
457     * @param savedOnly     Retrieve only saved networks.
458     * @param maskPasswords Mask passwords or not.
459     * @return List of WifiConfiguration objects representing the networks.
460     */
461    private List<WifiConfiguration> getConfiguredNetworks(
462            boolean savedOnly, boolean maskPasswords) {
463        List<WifiConfiguration> networks = new ArrayList<>();
464        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
465            if (savedOnly && config.ephemeral) {
466                continue;
467            }
468            networks.add(createExternalWifiConfiguration(config, maskPasswords));
469        }
470        return networks;
471    }
472
473    /**
474     * Retrieves the list of all configured networks with passwords masked.
475     *
476     * @return List of WifiConfiguration objects representing the networks.
477     */
478    public List<WifiConfiguration> getConfiguredNetworks() {
479        return getConfiguredNetworks(false, true);
480    }
481
482    /**
483     * Retrieves the list of all configured networks with the passwords in plaintext.
484     *
485     * WARNING: Don't use this to pass network configurations to external apps. Should only be
486     * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
487     * TODO: Need to understand the current use case of this API.
488     *
489     * @return List of WifiConfiguration objects representing the networks.
490     */
491    public List<WifiConfiguration> getConfiguredNetworksWithPasswords() {
492        return getConfiguredNetworks(false, false);
493    }
494
495    /**
496     * Retrieves the list of all configured networks with the passwords masked.
497     *
498     * @return List of WifiConfiguration objects representing the networks.
499     */
500    public List<WifiConfiguration> getSavedNetworks() {
501        return getConfiguredNetworks(true, true);
502    }
503
504    /**
505     * Retrieves the configured network corresponding to the provided networkId with password
506     * masked.
507     *
508     * @param networkId networkId of the requested network.
509     * @return WifiConfiguration object if found, null otherwise.
510     */
511    public WifiConfiguration getConfiguredNetwork(int networkId) {
512        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
513        if (config == null) {
514            return null;
515        }
516        // Create a new configuration object with the passwords masked to send out to the external
517        // world.
518        return createExternalWifiConfiguration(config, true);
519    }
520
521    /**
522     * Retrieves the configured network corresponding to the provided config key with password
523     * masked.
524     *
525     * @param configKey configKey of the requested network.
526     * @return WifiConfiguration object if found, null otherwise.
527     */
528    public WifiConfiguration getConfiguredNetwork(String configKey) {
529        WifiConfiguration config = getInternalConfiguredNetwork(configKey);
530        if (config == null) {
531            return null;
532        }
533        // Create a new configuration object with the passwords masked to send out to the external
534        // world.
535        return createExternalWifiConfiguration(config, true);
536    }
537
538    /**
539     * Retrieves the configured network corresponding to the provided networkId with password
540     * in plaintext.
541     *
542     * WARNING: Don't use this to pass network configurations to external apps. Should only be
543     * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
544     *
545     * @param networkId networkId of the requested network.
546     * @return WifiConfiguration object if found, null otherwise.
547     */
548    public WifiConfiguration getConfiguredNetworkWithPassword(int networkId) {
549        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
550        if (config == null) {
551            return null;
552        }
553        // Create a new configuration object without the passwords masked to send out to the
554        // external world.
555        return createExternalWifiConfiguration(config, false);
556    }
557
558    /**
559     * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
560     * the networks in our database.
561     */
562    private Collection<WifiConfiguration> getInternalConfiguredNetworks() {
563        return mConfiguredNetworks.valuesForCurrentUser();
564    }
565
566    /**
567     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
568     * provided configuration in our database.
569     * This first attempts to find the network using the provided network ID in configuration,
570     * else it attempts to find a matching configuration using the configKey.
571     */
572    private WifiConfiguration getInternalConfiguredNetwork(WifiConfiguration config) {
573        WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
574        if (internalConfig != null) {
575            return internalConfig;
576        }
577        internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
578        if (internalConfig == null) {
579            Log.e(TAG, "Cannot find network with networkId " + config.networkId
580                    + " or configKey " + config.configKey());
581        }
582        return internalConfig;
583    }
584
585    /**
586     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
587     * provided network ID in our database.
588     */
589    private WifiConfiguration getInternalConfiguredNetwork(int networkId) {
590        if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
591            Log.w(TAG, "Looking up network with invalid networkId -1");
592            return null;
593        }
594        WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId);
595        if (internalConfig == null) {
596            Log.e(TAG, "Cannot find network with networkId " + networkId);
597        }
598        return internalConfig;
599    }
600
601    /**
602     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
603     * provided configKey in our database.
604     */
605    private WifiConfiguration getInternalConfiguredNetwork(String configKey) {
606        WifiConfiguration internalConfig =
607                mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey);
608        if (internalConfig == null) {
609            Log.e(TAG, "Cannot find network with configKey " + configKey);
610        }
611        return internalConfig;
612    }
613
614    /**
615     * Method to send out the configured networks change broadcast when a single network
616     * configuration is changed.
617     *
618     * @param network WifiConfiguration corresponding to the network that was changed.
619     * @param reason  The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
620     *                WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
621     */
622    private void sendConfiguredNetworkChangedBroadcast(
623            WifiConfiguration network, int reason) {
624        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
625        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
626        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
627        // Create a new WifiConfiguration with passwords masked before we send it out.
628        WifiConfiguration broadcastNetwork = new WifiConfiguration(network);
629        maskPasswordsInWifiConfiguration(broadcastNetwork);
630        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, broadcastNetwork);
631        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
632        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
633    }
634
635    /**
636     * Method to send out the configured networks change broadcast when multiple network
637     * configurations are changed.
638     */
639    private void sendConfiguredNetworksChangedBroadcast() {
640        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
641        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
642        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
643        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
644    }
645
646    /**
647     * Checks if |uid| has permission to modify the provided configuration.
648     *
649     * @param config         WifiConfiguration object corresponding to the network to be modified.
650     * @param uid            UID of the app requesting the modification.
651     * @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts.
652     */
653    private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
654        // Passpoint configurations are generated and managed by PasspointManager. They can be
655        // added by either PasspointNetworkEvaluator (for auto connection) or Settings app
656        // (for manual connection), and need to be removed once the connection is completed.
657        // Since it is "owned" by us, so always allow us to modify them.
658        if (config.isPasspoint() && uid == Process.WIFI_UID) {
659            return true;
660        }
661
662        // EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
663        // by authenticator back to the WifiConfiguration object.
664        // Since it is "owned" by us, so always allow us to modify them.
665        if (config.enterpriseConfig != null
666                && uid == Process.WIFI_UID
667                && TelephonyUtil.isSimEapMethod(config.enterpriseConfig.getEapMethod())) {
668            return true;
669        }
670
671        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
672                DevicePolicyManagerInternal.class);
673
674        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
675                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
676
677        // If |uid| corresponds to the device owner, allow all modifications.
678        if (isUidDeviceOwner) {
679            return true;
680        }
681
682        final boolean isCreator = (config.creatorUid == uid);
683
684        // Check if the |uid| holds the |OVERRIDE_CONFIG_WIFI| permission if the caller asks us to
685        // bypass the lockdown checks.
686        if (ignoreLockdown) {
687            return mWifiPermissionsUtil.checkConfigOverridePermission(uid);
688        }
689
690        // Check if device has DPM capability. If it has and |dpmi| is still null, then we
691        // treat this case with suspicion and bail out.
692        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
693                && dpmi == null) {
694            Log.w(TAG, "Error retrieving DPMI service.");
695            return false;
696        }
697
698        // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
699        final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
700                config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
701        if (!isConfigEligibleForLockdown) {
702            return isCreator || mWifiPermissionsUtil.checkConfigOverridePermission(uid);
703        }
704
705        final ContentResolver resolver = mContext.getContentResolver();
706        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
707                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
708        return !isLockdownFeatureEnabled && mWifiPermissionsUtil.checkConfigOverridePermission(uid);
709    }
710
711    /**
712     * Method to check if the provided UID belongs to the current foreground user or some other
713     * app (only SysUI today) running on behalf of the user.
714     * This is used to prevent any background user apps from modifying network configurations.
715     *
716     * @param uid uid of the app.
717     * @return true if the UID belongs to the current foreground app or SystemUI, false otherwise.
718     */
719    private boolean doesUidBelongToCurrentUser(int uid) {
720        return (WifiConfigurationUtil.doesUidBelongToAnyProfile(
721                uid, mUserManager.getProfiles(mCurrentUserId)) || (uid == mSystemUiUid));
722    }
723
724    /**
725     * Copy over public elements from an external WifiConfiguration object to the internal
726     * configuration object if element has been set in the provided external WifiConfiguration.
727     * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
728     * for every update.
729     *
730     * This method updates all elements that are common to both network addition & update.
731     * The following fields of {@link WifiConfiguration} are not copied from external configs:
732     *  > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
733     *  > status - The status needs to be explicitly updated using
734     *             {@link WifiManager#enableNetwork(int, boolean)} or
735     *             {@link WifiManager#disableNetwork(int)}.
736     *
737     * @param internalConfig WifiConfiguration object in our internal map.
738     * @param externalConfig WifiConfiguration object provided from the external API.
739     */
740    private void mergeWithInternalWifiConfiguration(
741            WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
742        if (externalConfig.SSID != null) {
743            internalConfig.SSID = externalConfig.SSID;
744        }
745        if (externalConfig.BSSID != null) {
746            internalConfig.BSSID = externalConfig.BSSID.toLowerCase();
747        }
748        internalConfig.hiddenSSID = externalConfig.hiddenSSID;
749        if (externalConfig.preSharedKey != null
750                && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
751            internalConfig.preSharedKey = externalConfig.preSharedKey;
752        }
753        // Modify only wep keys are present in the provided configuration. This is a little tricky
754        // because there is no easy way to tell if the app is actually trying to null out the
755        // existing keys or not.
756        if (externalConfig.wepKeys != null) {
757            boolean hasWepKey = false;
758            for (int i = 0; i < internalConfig.wepKeys.length; i++) {
759                if (externalConfig.wepKeys[i] != null
760                        && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
761                    internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
762                    hasWepKey = true;
763                }
764            }
765            if (hasWepKey) {
766                internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
767            }
768        }
769        if (externalConfig.FQDN != null) {
770            internalConfig.FQDN = externalConfig.FQDN;
771        }
772        if (externalConfig.providerFriendlyName != null) {
773            internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
774        }
775        if (externalConfig.roamingConsortiumIds != null) {
776            internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone();
777        }
778
779        // Copy over all the auth/protocol/key mgmt parameters if set.
780        if (externalConfig.allowedAuthAlgorithms != null
781                && !externalConfig.allowedAuthAlgorithms.isEmpty()) {
782            internalConfig.allowedAuthAlgorithms =
783                    (BitSet) externalConfig.allowedAuthAlgorithms.clone();
784        }
785        if (externalConfig.allowedProtocols != null
786                && !externalConfig.allowedProtocols.isEmpty()) {
787            internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone();
788        }
789        if (externalConfig.allowedKeyManagement != null
790                && !externalConfig.allowedKeyManagement.isEmpty()) {
791            internalConfig.allowedKeyManagement =
792                    (BitSet) externalConfig.allowedKeyManagement.clone();
793        }
794        if (externalConfig.allowedPairwiseCiphers != null
795                && !externalConfig.allowedPairwiseCiphers.isEmpty()) {
796            internalConfig.allowedPairwiseCiphers =
797                    (BitSet) externalConfig.allowedPairwiseCiphers.clone();
798        }
799        if (externalConfig.allowedGroupCiphers != null
800                && !externalConfig.allowedGroupCiphers.isEmpty()) {
801            internalConfig.allowedGroupCiphers =
802                    (BitSet) externalConfig.allowedGroupCiphers.clone();
803        }
804
805        // Copy over the |IpConfiguration| parameters if set.
806        if (externalConfig.getIpConfiguration() != null) {
807            IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
808            if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
809                internalConfig.setIpAssignment(ipAssignment);
810                if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
811                    internalConfig.setStaticIpConfiguration(
812                            new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
813                }
814            }
815            IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
816            if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
817                internalConfig.setProxySettings(proxySettings);
818                if (proxySettings == IpConfiguration.ProxySettings.PAC
819                        || proxySettings == IpConfiguration.ProxySettings.STATIC) {
820                    internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
821                }
822            }
823        }
824
825        // Copy over the |WifiEnterpriseConfig| parameters if set.
826        if (externalConfig.enterpriseConfig != null) {
827            internalConfig.enterpriseConfig =
828                    new WifiEnterpriseConfig(externalConfig.enterpriseConfig);
829        }
830    }
831
832    /**
833     * Set all the exposed defaults in the newly created WifiConfiguration object.
834     * These fields have a default value advertised in our public documentation. The only exception
835     * is the hidden |IpConfiguration| parameters, these have a default value even though they're
836     * hidden.
837     *
838     * @param configuration provided WifiConfiguration object.
839     */
840    private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
841        configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
842
843        configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
844        configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
845
846        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
847        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
848
849        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
850        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
851
852        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
853        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
854        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
855        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
856
857        configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
858        configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
859
860        configuration.status = WifiConfiguration.Status.DISABLED;
861        configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
862                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
863    }
864
865    /**
866     * Create a new internal WifiConfiguration object by copying over parameters from the provided
867     * external configuration and set defaults for the appropriate parameters.
868     *
869     * @param externalConfig WifiConfiguration object provided from the external API.
870     * @return New WifiConfiguration object with parameters merged from the provided external
871     * configuration.
872     */
873    private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
874            WifiConfiguration externalConfig, int uid) {
875        WifiConfiguration newInternalConfig = new WifiConfiguration();
876
877        // First allocate a new network ID for the configuration.
878        newInternalConfig.networkId = mNextNetworkId++;
879
880        // First set defaults in the new configuration created.
881        setDefaultsInWifiConfiguration(newInternalConfig);
882
883        // Copy over all the public elements from the provided configuration.
884        mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
885
886        // Copy over the hidden configuration parameters. These are the only parameters used by
887        // system apps to indicate some property about the network being added.
888        // These are only copied over for network additions and ignored for network updates.
889        newInternalConfig.requirePMF = externalConfig.requirePMF;
890        newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
891        newInternalConfig.ephemeral = externalConfig.ephemeral;
892        newInternalConfig.meteredHint = externalConfig.meteredHint;
893        newInternalConfig.useExternalScores = externalConfig.useExternalScores;
894        newInternalConfig.shared = externalConfig.shared;
895
896        // Add debug information for network addition.
897        newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
898        newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
899                mContext.getPackageManager().getNameForUid(uid);
900        newInternalConfig.creationTime = newInternalConfig.updateTime =
901                createDebugTimeStampString(mClock.getWallClockMillis());
902
903        return newInternalConfig;
904    }
905
906    /**
907     * Create a new internal WifiConfiguration object by copying over parameters from the provided
908     * external configuration to a copy of the existing internal WifiConfiguration object.
909     *
910     * @param internalConfig WifiConfiguration object in our internal map.
911     * @param externalConfig WifiConfiguration object provided from the external API.
912     * @return Copy of existing WifiConfiguration object with parameters merged from the provided
913     * configuration.
914     */
915    private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
916            WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid) {
917        WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
918
919        // Copy over all the public elements from the provided configuration.
920        mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
921
922        // Add debug information for network update.
923        newInternalConfig.lastUpdateUid = uid;
924        newInternalConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
925        newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis());
926
927        return newInternalConfig;
928    }
929
930    /**
931     * Add a network or update a network configuration to our database.
932     * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
933     * network configuration. Otherwise, the networkId should refer to an existing configuration.
934     *
935     * @param config provided WifiConfiguration object.
936     * @param uid    UID of the app requesting the network addition/deletion.
937     * @return NetworkUpdateResult object representing status of the update.
938     */
939    private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) {
940        if (mVerboseLoggingEnabled) {
941            Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
942        }
943        WifiConfiguration newInternalConfig = null;
944
945        // First check if we already have a network with the provided network id or configKey.
946        WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
947        // No existing network found. So, potentially a network add.
948        if (existingInternalConfig == null) {
949            newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
950            // Since the original config provided may have had an empty
951            // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
952            // network with the the same configkey.
953            existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey());
954        }
955        // Existing network found. So, a network update.
956        if (existingInternalConfig != null) {
957            // Check for the app's permission before we let it update this network.
958            if (!canModifyNetwork(existingInternalConfig, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
959                Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
960                        + config.configKey());
961                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
962            }
963            newInternalConfig =
964                    updateExistingInternalWifiConfigurationFromExternal(
965                            existingInternalConfig, config, uid);
966        }
967
968        // Only add networks with proxy settings if the user has permission to
969        if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
970                && !canModifyProxySettings(uid)) {
971            Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
972                    + config.configKey() + ". Must have OVERRIDE_WIFI_CONFIG,"
973                    + " or be device or profile owner.");
974            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
975        }
976
977        // Update the keys for non-Passpoint enterprise networks.  For Passpoint, the certificates
978        // and keys are installed at the time the provider is installed.
979        if (config.enterpriseConfig != null
980                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
981                && !config.isPasspoint()) {
982            if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
983                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
984            }
985        }
986
987        boolean newNetwork = (existingInternalConfig == null);
988        // This is needed to inform IpManager about any IP configuration changes.
989        boolean hasIpChanged =
990                newNetwork || WifiConfigurationUtil.hasIpChanged(
991                        existingInternalConfig, newInternalConfig);
992        boolean hasProxyChanged =
993                newNetwork || WifiConfigurationUtil.hasProxyChanged(
994                        existingInternalConfig, newInternalConfig);
995        // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
996        boolean hasCredentialChanged =
997                newNetwork || WifiConfigurationUtil.hasCredentialChanged(
998                        existingInternalConfig, newInternalConfig);
999        if (hasCredentialChanged) {
1000            newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
1001        }
1002
1003        // Add it to our internal map. This will replace any existing network configuration for
1004        // updates.
1005        mConfiguredNetworks.put(newInternalConfig);
1006
1007        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
1008            if (mVerboseLoggingEnabled) {
1009                Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
1010            }
1011        }
1012
1013        // Stage the backup of the SettingsProvider package which backs this up.
1014        mBackupManagerProxy.notifyDataChanged();
1015
1016        NetworkUpdateResult result =
1017                new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);
1018        result.setIsNewNetwork(newNetwork);
1019        result.setNetworkId(newInternalConfig.networkId);
1020
1021        localLog("addOrUpdateNetworkInternal: added/updated config."
1022                + " netId=" + newInternalConfig.networkId
1023                + " configKey=" + newInternalConfig.configKey()
1024                + " uid=" + Integer.toString(newInternalConfig.creatorUid)
1025                + " name=" + newInternalConfig.creatorName);
1026        return result;
1027    }
1028
1029    /**
1030     * Add a network or update a network configuration to our database.
1031     * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1032     * network configuration. Otherwise, the networkId should refer to an existing configuration.
1033     *
1034     * @param config provided WifiConfiguration object.
1035     * @param uid    UID of the app requesting the network addition/modification.
1036     * @return NetworkUpdateResult object representing status of the update.
1037     */
1038    public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
1039        if (!doesUidBelongToCurrentUser(uid)) {
1040            Log.e(TAG, "UID " + uid + " not visible to the current user");
1041            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1042        }
1043        if (!WifiConfigurationUtil.validate(config)) {
1044            Log.e(TAG, "Cannot add/update network with invalid config");
1045            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1046        }
1047        if (mPendingStoreRead) {
1048            Log.e(TAG, "Cannot add/update network before store is read!");
1049            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1050        }
1051        NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
1052        if (!result.isSuccess()) {
1053            Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
1054            return result;
1055        }
1056        WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
1057        sendConfiguredNetworkChangedBroadcast(
1058                newConfig,
1059                result.isNewNetwork()
1060                        ? WifiManager.CHANGE_REASON_ADDED
1061                        : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1062        // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
1063        if (!config.ephemeral && !config.isPasspoint()) {
1064            saveToStore(true);
1065            if (mListener != null) {
1066                if (result.isNewNetwork()) {
1067                    mListener.onSavedNetworkAdded(newConfig.networkId);
1068                } else {
1069                    mListener.onSavedNetworkUpdated(newConfig.networkId);
1070                }
1071            }
1072        }
1073        return result;
1074    }
1075
1076    /**
1077     * Removes the specified network configuration from our database.
1078     *
1079     * @param config provided WifiConfiguration object.
1080     * @return true if successful, false otherwise.
1081     */
1082    private boolean removeNetworkInternal(WifiConfiguration config) {
1083        if (mVerboseLoggingEnabled) {
1084            Log.v(TAG, "Removing network " + config.getPrintableSsid());
1085        }
1086        // Remove any associated enterprise keys for non-Passpoint networks.
1087        if (!config.isPasspoint() && config.enterpriseConfig != null
1088                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1089            mWifiKeyStore.removeKeys(config.enterpriseConfig);
1090        }
1091
1092        removeConnectChoiceFromAllNetworks(config.configKey());
1093        mConfiguredNetworks.remove(config.networkId);
1094        mScanDetailCaches.remove(config.networkId);
1095        // Stage the backup of the SettingsProvider package which backs this up.
1096        mBackupManagerProxy.notifyDataChanged();
1097
1098        localLog("removeNetworkInternal: removed config."
1099                + " netId=" + config.networkId
1100                + " configKey=" + config.configKey());
1101        return true;
1102    }
1103
1104    /**
1105     * Removes the specified network configuration from our database.
1106     *
1107     * @param networkId network ID of the provided network.
1108     * @param uid       UID of the app requesting the network deletion.
1109     * @return true if successful, false otherwise.
1110     */
1111    public boolean removeNetwork(int networkId, int uid) {
1112        if (!doesUidBelongToCurrentUser(uid)) {
1113            Log.e(TAG, "UID " + uid + " not visible to the current user");
1114            return false;
1115        }
1116        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1117        if (config == null) {
1118            return false;
1119        }
1120        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1121            Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1122                    + config.configKey());
1123            return false;
1124        }
1125        if (!removeNetworkInternal(config)) {
1126            Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1127            return false;
1128        }
1129        if (networkId == mLastSelectedNetworkId) {
1130            clearLastSelectedNetwork();
1131        }
1132        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
1133        // Unless the removed network is ephemeral or Passpoint, persist the network removal.
1134        if (!config.ephemeral && !config.isPasspoint()) {
1135            saveToStore(true);
1136            if (mListener != null) mListener.onSavedNetworkRemoved(networkId);
1137        }
1138        return true;
1139    }
1140
1141    /**
1142     * Remove all networks associated with an application.
1143     *
1144     * @param app Application info of the package of networks to remove.
1145     * @return the {@link Set} of networks that were removed by this call. Networks which matched
1146     *         but failed to remove are omitted from this set.
1147     */
1148    public Set<Integer> removeNetworksForApp(ApplicationInfo app) {
1149        if (app == null || app.packageName == null) {
1150            return Collections.<Integer>emptySet();
1151        }
1152        Log.d(TAG, "Remove all networks for app " + app);
1153        Set<Integer> removedNetworks = new ArraySet<>();
1154        WifiConfiguration[] copiedConfigs =
1155                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1156        for (WifiConfiguration config : copiedConfigs) {
1157            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
1158                continue;
1159            }
1160            localLog("Removing network " + config.SSID
1161                    + ", application \"" + app.packageName + "\" uninstalled"
1162                    + " from user " + UserHandle.getUserId(app.uid));
1163            if (removeNetwork(config.networkId, mSystemUiUid)) {
1164                removedNetworks.add(config.networkId);
1165            }
1166        }
1167        return removedNetworks;
1168    }
1169
1170    /**
1171     * Remove all networks associated with a user.
1172     *
1173     * @param userId The identifier of the user which is being removed.
1174     * @return the {@link Set} of networks that were removed by this call. Networks which matched
1175     *         but failed to remove are omitted from this set.
1176     */
1177    Set<Integer> removeNetworksForUser(int userId) {
1178        Log.d(TAG, "Remove all networks for user " + userId);
1179        Set<Integer> removedNetworks = new ArraySet<>();
1180        WifiConfiguration[] copiedConfigs =
1181                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1182        for (WifiConfiguration config : copiedConfigs) {
1183            if (userId != UserHandle.getUserId(config.creatorUid)) {
1184                continue;
1185            }
1186            localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1187            if (removeNetwork(config.networkId, mSystemUiUid)) {
1188                removedNetworks.add(config.networkId);
1189            }
1190        }
1191        return removedNetworks;
1192    }
1193
1194    /**
1195     * Helper method to mark a network enabled for network selection.
1196     */
1197    private void setNetworkSelectionEnabled(WifiConfiguration config) {
1198        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1199        status.setNetworkSelectionStatus(
1200                NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
1201        status.setDisableTime(
1202                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1203        status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1204
1205        // Clear out all the disable reason counters.
1206        status.clearDisableReasonCounter();
1207        if (mListener != null) mListener.onSavedNetworkEnabled(config.networkId);
1208    }
1209
1210    /**
1211     * Helper method to mark a network temporarily disabled for network selection.
1212     */
1213    private void setNetworkSelectionTemporarilyDisabled(
1214            WifiConfiguration config, int disableReason) {
1215        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1216        status.setNetworkSelectionStatus(
1217                NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
1218        // Only need a valid time filled in for temporarily disabled networks.
1219        status.setDisableTime(mClock.getElapsedSinceBootMillis());
1220        status.setNetworkSelectionDisableReason(disableReason);
1221        if (mListener != null) mListener.onSavedNetworkTemporarilyDisabled(config.networkId);
1222    }
1223
1224    /**
1225     * Helper method to mark a network permanently disabled for network selection.
1226     */
1227    private void setNetworkSelectionPermanentlyDisabled(
1228            WifiConfiguration config, int disableReason) {
1229        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1230        status.setNetworkSelectionStatus(
1231                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1232        status.setDisableTime(
1233                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1234        status.setNetworkSelectionDisableReason(disableReason);
1235        if (mListener != null) mListener.onSavedNetworkPermanentlyDisabled(config.networkId);
1236    }
1237
1238    /**
1239     * Helper method to set the publicly exposed status for the network and send out the network
1240     * status change broadcast.
1241     */
1242    private void setNetworkStatus(WifiConfiguration config, int status) {
1243        config.status = status;
1244        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1245    }
1246
1247    /**
1248     * Sets a network's status (both internal and public) according to the update reason and
1249     * its current state.
1250     *
1251     * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
1252     * public {@link WifiConfiguration#status} field if the network is either enabled or
1253     * permanently disabled.
1254     *
1255     * @param config network to be updated.
1256     * @param reason reason code for update.
1257     * @return true if the input configuration has been updated, false otherwise.
1258     */
1259    private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
1260        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1261        if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
1262            Log.e(TAG, "Invalid Network disable reason " + reason);
1263            return false;
1264        }
1265        if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1266            setNetworkSelectionEnabled(config);
1267            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1268        } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
1269            setNetworkSelectionTemporarilyDisabled(config, reason);
1270        } else {
1271            setNetworkSelectionPermanentlyDisabled(config, reason);
1272            setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
1273        }
1274        localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
1275                + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
1276                + networkStatus.getNetworkDisableReasonString() + " at="
1277                + createDebugTimeStampString(mClock.getWallClockMillis()));
1278        saveToStore(false);
1279        return true;
1280    }
1281
1282    /**
1283     * Update a network's status (both internal and public) according to the update reason and
1284     * its current state.
1285     *
1286     * @param config network to be updated.
1287     * @param reason reason code for update.
1288     * @return true if the input configuration has been updated, false otherwise.
1289     */
1290    private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1291        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1292        if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1293            networkStatus.incrementDisableReasonCounter(reason);
1294            // For network disable reasons, we should only update the status if we cross the
1295            // threshold.
1296            int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
1297            int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
1298            if (disableReasonCounter < disableReasonThreshold) {
1299                if (mVerboseLoggingEnabled) {
1300                    Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
1301                            + " for reason "
1302                            + NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is "
1303                            + networkStatus.getDisableReasonCounter(reason) + " and threshold is "
1304                            + disableReasonThreshold);
1305                }
1306                return true;
1307            }
1308        }
1309        return setNetworkSelectionStatus(config, reason);
1310    }
1311
1312    /**
1313     * Update a network's status (both internal and public) according to the update reason and
1314     * its current state.
1315     *
1316     * Each network has 2 status:
1317     * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
1318     * for temporarily disabling a network for Network Selector.
1319     * 2. Status: This is the exposed status for a network. This is mostly set by
1320     * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
1321     * {@link WifiManager#disableNetwork(int)}.
1322     *
1323     * @param networkId network ID of the network that needs the update.
1324     * @param reason    reason to update the network.
1325     * @return true if the input configuration has been updated, false otherwise.
1326     */
1327    public boolean updateNetworkSelectionStatus(int networkId, int reason) {
1328        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1329        if (config == null) {
1330            return false;
1331        }
1332        return updateNetworkSelectionStatus(config, reason);
1333    }
1334
1335    /**
1336     * Update whether a network is currently not recommended by {@link RecommendedNetworkEvaluator}.
1337     *
1338     * @param networkId network ID of the network to be updated
1339     * @param notRecommended whether this network is not recommended
1340     * @return true if the network is updated, false otherwise
1341     */
1342    public boolean updateNetworkNotRecommended(int networkId, boolean notRecommended) {
1343        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1344        if (config == null) {
1345            return false;
1346        }
1347
1348        config.getNetworkSelectionStatus().setNotRecommended(notRecommended);
1349        if (mVerboseLoggingEnabled) {
1350            localLog("updateNetworkRecommendation: configKey=" + config.configKey()
1351                    + " notRecommended=" + notRecommended);
1352        }
1353        saveToStore(false);
1354        return true;
1355    }
1356
1357    /**
1358     * Attempt to re-enable a network for network selection, if this network was either:
1359     * a) Previously temporarily disabled, but its disable timeout has expired, or
1360     * b) Previously disabled because of a user switch, but is now visible to the current
1361     * user.
1362     *
1363     * @param config configuration for the network to be re-enabled for network selection. The
1364     *               network corresponding to the config must be visible to the current user.
1365     * @return true if the network identified by {@param config} was re-enabled for qualified
1366     * network selection, false otherwise.
1367     */
1368    private boolean tryEnableNetwork(WifiConfiguration config) {
1369        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1370        if (networkStatus.isNetworkTemporaryDisabled()) {
1371            long timeDifferenceMs =
1372                    mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
1373            int disableReason = networkStatus.getNetworkSelectionDisableReason();
1374            long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
1375            if (timeDifferenceMs >= disableTimeoutMs) {
1376                return updateNetworkSelectionStatus(
1377                        config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1378            }
1379        } else if (networkStatus.isDisabledByReason(
1380                NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
1381            return updateNetworkSelectionStatus(
1382                    config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1383        }
1384        return false;
1385    }
1386
1387    /**
1388     * Attempt to re-enable a network for network selection, if this network was either:
1389     * a) Previously temporarily disabled, but its disable timeout has expired, or
1390     * b) Previously disabled because of a user switch, but is now visible to the current
1391     * user.
1392     *
1393     * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1394     * @return true if the network identified by {@param networkId} was re-enabled for qualified
1395     * network selection, false otherwise.
1396     */
1397    public boolean tryEnableNetwork(int networkId) {
1398        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1399        if (config == null) {
1400            return false;
1401        }
1402        return tryEnableNetwork(config);
1403    }
1404
1405    /**
1406     * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
1407     *
1408     * @param networkId     network ID of the network that needs the update.
1409     * @param disableOthers Whether to disable all other networks or not. This is used to indicate
1410     *                      that the app requested connection to a specific network.
1411     * @param uid           uid of the app requesting the update.
1412     * @return true if it succeeds, false otherwise
1413     */
1414    public boolean enableNetwork(int networkId, boolean disableOthers, int uid) {
1415        if (mVerboseLoggingEnabled) {
1416            Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
1417        }
1418        if (!doesUidBelongToCurrentUser(uid)) {
1419            Log.e(TAG, "UID " + uid + " not visible to the current user");
1420            return false;
1421        }
1422        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1423        if (config == null) {
1424            return false;
1425        }
1426        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1427            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1428                    + config.configKey());
1429            return false;
1430        }
1431        if (!updateNetworkSelectionStatus(
1432                networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) {
1433            return false;
1434        }
1435        if (disableOthers) {
1436            setLastSelectedNetwork(networkId);
1437        }
1438        saveToStore(true);
1439        return true;
1440    }
1441
1442    /**
1443     * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
1444     *
1445     * @param networkId network ID of the network that needs the update.
1446     * @param uid       uid of the app requesting the update.
1447     * @return true if it succeeds, false otherwise
1448     */
1449    public boolean disableNetwork(int networkId, int uid) {
1450        if (mVerboseLoggingEnabled) {
1451            Log.v(TAG, "Disabling network " + networkId);
1452        }
1453        if (!doesUidBelongToCurrentUser(uid)) {
1454            Log.e(TAG, "UID " + uid + " not visible to the current user");
1455            return false;
1456        }
1457        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1458        if (config == null) {
1459            return false;
1460        }
1461        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1462            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1463                    + config.configKey());
1464            return false;
1465        }
1466        if (!updateNetworkSelectionStatus(
1467                networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
1468            return false;
1469        }
1470        if (networkId == mLastSelectedNetworkId) {
1471            clearLastSelectedNetwork();
1472        }
1473        saveToStore(true);
1474        return true;
1475    }
1476
1477    /**
1478     * Checks if the |uid| has the necessary permission to force a connection to a network
1479     * and updates the last connected UID for the provided configuration.
1480     *
1481     * @param networkId network ID corresponding to the network.
1482     * @param uid       uid of the app requesting the connection.
1483     * @return true if |uid| has the necessary permission to trigger explicit connection to the
1484     * network, false otherwise.
1485     * Note: This returns true only for the system settings/sysui app which holds the
1486     * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let
1487     * any other app force connection to a network.
1488     */
1489    public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
1490        if (mVerboseLoggingEnabled) {
1491            Log.v(TAG, "Update network last connect UID for " + networkId);
1492        }
1493        if (!doesUidBelongToCurrentUser(uid)) {
1494            Log.e(TAG, "UID " + uid + " not visible to the current user");
1495            return false;
1496        }
1497        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1498        if (config == null) {
1499            return false;
1500        }
1501        if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) {
1502            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1503                    + config.configKey());
1504            return false;
1505        }
1506        config.lastConnectUid = uid;
1507        return true;
1508    }
1509
1510    /**
1511     * Updates a network configuration after a successful connection to it.
1512     *
1513     * This method updates the following WifiConfiguration elements:
1514     * 1. Set the |lastConnected| timestamp.
1515     * 2. Increment |numAssociation| counter.
1516     * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
1517     * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
1518     * 5. Sets the status of network as |CURRENT|.
1519     *
1520     * @param networkId network ID corresponding to the network.
1521     * @return true if the network was found, false otherwise.
1522     */
1523    public boolean updateNetworkAfterConnect(int networkId) {
1524        if (mVerboseLoggingEnabled) {
1525            Log.v(TAG, "Update network after connect for " + networkId);
1526        }
1527        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1528        if (config == null) {
1529            return false;
1530        }
1531        config.lastConnected = mClock.getWallClockMillis();
1532        config.numAssociation++;
1533        config.getNetworkSelectionStatus().clearDisableReasonCounter();
1534        config.getNetworkSelectionStatus().setHasEverConnected(true);
1535        setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
1536        saveToStore(false);
1537        return true;
1538    }
1539
1540    /**
1541     * Updates a network configuration after disconnection from it.
1542     *
1543     * This method updates the following WifiConfiguration elements:
1544     * 1. Set the |lastDisConnected| timestamp.
1545     * 2. Sets the status of network back to |ENABLED|.
1546     *
1547     * @param networkId network ID corresponding to the network.
1548     * @return true if the network was found, false otherwise.
1549     */
1550    public boolean updateNetworkAfterDisconnect(int networkId) {
1551        if (mVerboseLoggingEnabled) {
1552            Log.v(TAG, "Update network after disconnect for " + networkId);
1553        }
1554        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1555        if (config == null) {
1556            return false;
1557        }
1558        config.lastDisconnected = mClock.getWallClockMillis();
1559        // If the network hasn't been disabled, mark it back as
1560        // enabled after disconnection.
1561        if (config.status == WifiConfiguration.Status.CURRENT) {
1562            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1563        }
1564        saveToStore(false);
1565        return true;
1566    }
1567
1568    /**
1569     * Set default GW MAC address for the provided network.
1570     *
1571     * @param networkId  network ID corresponding to the network.
1572     * @param macAddress MAC address of the gateway to be set.
1573     * @return true if the network was found, false otherwise.
1574     */
1575    public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
1576        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1577        if (config == null) {
1578            return false;
1579        }
1580        config.defaultGwMacAddress = macAddress;
1581        return true;
1582    }
1583
1584    /**
1585     * Clear the {@link NetworkSelectionStatus#mCandidate},
1586     * {@link NetworkSelectionStatus#mCandidateScore} &
1587     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1588     *
1589     * This is invoked by Network Selector at the start of every selection procedure to clear all
1590     * configured networks' scan-result-candidates.
1591     *
1592     * @param networkId network ID corresponding to the network.
1593     * @return true if the network was found, false otherwise.
1594     */
1595    public boolean clearNetworkCandidateScanResult(int networkId) {
1596        if (mVerboseLoggingEnabled) {
1597            Log.v(TAG, "Clear network candidate scan result for " + networkId);
1598        }
1599        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1600        if (config == null) {
1601            return false;
1602        }
1603        config.getNetworkSelectionStatus().setCandidate(null);
1604        config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
1605        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
1606        return true;
1607    }
1608
1609    /**
1610     * Set the {@link NetworkSelectionStatus#mCandidate},
1611     * {@link NetworkSelectionStatus#mCandidateScore} &
1612     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1613     *
1614     * This is invoked by Network Selector when it sees a network during network selection procedure
1615     * to set the scan result candidate.
1616     *
1617     * @param networkId  network ID corresponding to the network.
1618     * @param scanResult Candidate ScanResult associated with this network.
1619     * @param score      Score assigned to the candidate.
1620     * @return true if the network was found, false otherwise.
1621     */
1622    public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) {
1623        if (mVerboseLoggingEnabled) {
1624            Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId);
1625        }
1626        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1627        if (config == null) {
1628            return false;
1629        }
1630        config.getNetworkSelectionStatus().setCandidate(scanResult);
1631        config.getNetworkSelectionStatus().setCandidateScore(score);
1632        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
1633        return true;
1634    }
1635
1636    /**
1637     * Iterate through all the saved networks and remove the provided configuration from the
1638     * {@link NetworkSelectionStatus#mConnectChoice} from them.
1639     *
1640     * This is invoked when a network is removed from our records.
1641     *
1642     * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
1643     */
1644    private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
1645        if (mVerboseLoggingEnabled) {
1646            Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
1647        }
1648        if (connectChoiceConfigKey == null) {
1649            return;
1650        }
1651        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1652            WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1653            String connectChoice = status.getConnectChoice();
1654            if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
1655                Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
1656                        + " : " + config.networkId);
1657                clearNetworkConnectChoice(config.networkId);
1658            }
1659        }
1660    }
1661
1662    /**
1663     * Clear the {@link NetworkSelectionStatus#mConnectChoice} &
1664     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1665     *
1666     * @param networkId network ID corresponding to the network.
1667     * @return true if the network was found, false otherwise.
1668     */
1669    public boolean clearNetworkConnectChoice(int networkId) {
1670        if (mVerboseLoggingEnabled) {
1671            Log.v(TAG, "Clear network connect choice for " + networkId);
1672        }
1673        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1674        if (config == null) {
1675            return false;
1676        }
1677        config.getNetworkSelectionStatus().setConnectChoice(null);
1678        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(
1679                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1680        saveToStore(false);
1681        return true;
1682    }
1683
1684    /**
1685     * Set the {@link NetworkSelectionStatus#mConnectChoice} &
1686     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1687     *
1688     * This is invoked by Network Selector when the user overrides the currently connected network
1689     * choice.
1690     *
1691     * @param networkId              network ID corresponding to the network.
1692     * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
1693     *                               this network.
1694     * @param timestamp              timestamp at which the choice was made.
1695     * @return true if the network was found, false otherwise.
1696     */
1697    public boolean setNetworkConnectChoice(
1698            int networkId, String connectChoiceConfigKey, long timestamp) {
1699        if (mVerboseLoggingEnabled) {
1700            Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
1701        }
1702        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1703        if (config == null) {
1704            return false;
1705        }
1706        config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
1707        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp);
1708        saveToStore(false);
1709        return true;
1710    }
1711
1712    /**
1713     * Increments the number of no internet access reports in the provided network.
1714     *
1715     * @param networkId network ID corresponding to the network.
1716     * @return true if the network was found, false otherwise.
1717     */
1718    public boolean incrementNetworkNoInternetAccessReports(int networkId) {
1719        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1720        if (config == null) {
1721            return false;
1722        }
1723        config.numNoInternetAccessReports++;
1724        return true;
1725    }
1726
1727    /**
1728     * Sets the internet access is validated or not in the provided network.
1729     *
1730     * @param networkId network ID corresponding to the network.
1731     * @param validated Whether access is validated or not.
1732     * @return true if the network was found, false otherwise.
1733     */
1734    public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
1735        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1736        if (config == null) {
1737            return false;
1738        }
1739        config.validatedInternetAccess = validated;
1740        config.numNoInternetAccessReports = 0;
1741        saveToStore(false);
1742        return true;
1743    }
1744
1745    /**
1746     * Sets whether the internet access is expected or not in the provided network.
1747     *
1748     * @param networkId network ID corresponding to the network.
1749     * @param expected  Whether access is expected or not.
1750     * @return true if the network was found, false otherwise.
1751     */
1752    public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
1753        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1754        if (config == null) {
1755            return false;
1756        }
1757        config.noInternetAccessExpected = expected;
1758        return true;
1759    }
1760
1761    /**
1762     * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
1763     * is done when either the corresponding network is either removed or disabled.
1764     */
1765    private void clearLastSelectedNetwork() {
1766        if (mVerboseLoggingEnabled) {
1767            Log.v(TAG, "Clearing last selected network");
1768        }
1769        mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1770        mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
1771    }
1772
1773    /**
1774     * Helper method to mark a network as the last selected one by an app/user. This is set
1775     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
1776     * This is used by network selector to assign a special bonus during network selection.
1777     */
1778    private void setLastSelectedNetwork(int networkId) {
1779        if (mVerboseLoggingEnabled) {
1780            Log.v(TAG, "Setting last selected network to " + networkId);
1781        }
1782        mLastSelectedNetworkId = networkId;
1783        mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
1784    }
1785
1786    /**
1787     * Retrieve the network Id corresponding to the last network that was explicitly selected by
1788     * an app/user.
1789     *
1790     * @return network Id corresponding to the last selected network.
1791     */
1792    public int getLastSelectedNetwork() {
1793        return mLastSelectedNetworkId;
1794    }
1795
1796    /**
1797     * Retrieve the configKey corresponding to the last network that was explicitly selected by
1798     * an app/user.
1799     *
1800     * @return network Id corresponding to the last selected network.
1801     */
1802    public String getLastSelectedNetworkConfigKey() {
1803        if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
1804            return "";
1805        }
1806        WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
1807        if (config == null) {
1808            return "";
1809        }
1810        return config.configKey();
1811    }
1812
1813    /**
1814     * Retrieve the time stamp at which a network was explicitly selected by an app/user.
1815     *
1816     * @return timestamp in milliseconds from boot when this was set.
1817     */
1818    public long getLastSelectedTimeStamp() {
1819        return mLastSelectedTimeStamp;
1820    }
1821
1822    /**
1823     * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
1824     * network.
1825     *
1826     * @param networkId network ID corresponding to the network.
1827     * @return existing {@link ScanDetailCache} entry if one exists or null.
1828     */
1829    public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
1830        return mScanDetailCaches.get(networkId);
1831    }
1832
1833    /**
1834     * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
1835     * the provided network.
1836     *
1837     * @param config configuration corresponding to the the network.
1838     * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
1839     * this network.
1840     */
1841    private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
1842        if (config == null) return null;
1843        ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
1844        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
1845            cache = new ScanDetailCache(
1846                    config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
1847            mScanDetailCaches.put(config.networkId, cache);
1848        }
1849        return cache;
1850    }
1851
1852    /**
1853     * Saves the provided ScanDetail into the corresponding scan detail cache entry
1854     * {@link #mScanDetailCaches} for the provided network.
1855     *
1856     * @param config     configuration corresponding to the the network.
1857     * @param scanDetail new scan detail instance to be saved into the cache.
1858     */
1859    private void saveToScanDetailCacheForNetwork(
1860            WifiConfiguration config, ScanDetail scanDetail) {
1861        ScanResult scanResult = scanDetail.getScanResult();
1862
1863        ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
1864        if (scanDetailCache == null) {
1865            Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
1866            return;
1867        }
1868
1869        // Adding a new BSSID
1870        ScanResult result = scanDetailCache.get(scanResult.BSSID);
1871        if (result != null) {
1872            // transfer the black list status
1873            scanResult.blackListTimestamp = result.blackListTimestamp;
1874            scanResult.numIpConfigFailures = result.numIpConfigFailures;
1875            scanResult.numConnection = result.numConnection;
1876        }
1877        if (config.ephemeral) {
1878            // For an ephemeral Wi-Fi config, the ScanResult should be considered
1879            // untrusted.
1880            scanResult.untrusted = true;
1881        }
1882
1883        // Add the scan detail to this network's scan detail cache.
1884        scanDetailCache.put(scanDetail);
1885
1886        // Since we added a scan result to this configuration, re-attempt linking.
1887        // TODO: Do we really need to do this after every scan result?
1888        attemptNetworkLinking(config);
1889    }
1890
1891    /**
1892     * Retrieves a configured network corresponding to the provided scan detail if one exists.
1893     *
1894     * @param scanDetail ScanDetail instance  to use for looking up the network.
1895     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1896     * null if none exists.
1897     */
1898    private WifiConfiguration getConfiguredNetworkForScanDetail(ScanDetail scanDetail) {
1899        ScanResult scanResult = scanDetail.getScanResult();
1900        if (scanResult == null) {
1901            Log.e(TAG, "No scan result found in scan detail");
1902            return null;
1903        }
1904        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
1905            if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) {
1906                if (mVerboseLoggingEnabled) {
1907                    Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
1908                            + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
1909                }
1910                return config;
1911            }
1912        }
1913        return null;
1914    }
1915
1916    /**
1917     * Retrieves a configured network corresponding to the provided scan detail if one exists and
1918     * caches the provided |scanDetail| into the corresponding scan detail cache entry
1919     * {@link #mScanDetailCaches} for the retrieved network.
1920     *
1921     * @param scanDetail input a scanDetail from the scan result
1922     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1923     * null if none exists.
1924     */
1925    public WifiConfiguration getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail) {
1926        WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail);
1927        if (network == null) {
1928            return null;
1929        }
1930        saveToScanDetailCacheForNetwork(network, scanDetail);
1931        // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
1932        // Information Element (IE), into the associated WifiConfigurations. Most of the
1933        // time there is no TIM IE in the scan result (Probe Response instead of Beacon
1934        // Frame), these scanResult DTIM's are negative and ignored.
1935        // Used for metrics collection.
1936        if (scanDetail.getNetworkDetail() != null
1937                && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
1938            network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
1939        }
1940        return createExternalWifiConfiguration(network, true);
1941    }
1942
1943    /**
1944     * Update the scan detail cache associated with current connected network with latest
1945     * RSSI value in the provided WifiInfo.
1946     * This is invoked when we get an RSSI poll update after connection.
1947     *
1948     * @param info WifiInfo instance pointing to the current connected network.
1949     */
1950    public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
1951        WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
1952        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
1953        if (config != null && scanDetailCache != null) {
1954            ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
1955            if (scanDetail != null) {
1956                ScanResult result = scanDetail.getScanResult();
1957                long previousSeen = result.seen;
1958                int previousRssi = result.level;
1959                // Update the scan result
1960                scanDetail.setSeen();
1961                result.level = info.getRssi();
1962                // Average the RSSI value
1963                result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS);
1964                if (mVerboseLoggingEnabled) {
1965                    Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
1966                            + " BSSID=" + result.BSSID
1967                            + " RSSI=" + result.level
1968                            + " for " + config.configKey());
1969                }
1970            }
1971        }
1972    }
1973
1974    /**
1975     * Save the ScanDetail to the ScanDetailCache of the given network.  This is used
1976     * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching
1977     * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
1978     *
1979     * @param networkId The ID of the network to save ScanDetail to
1980     * @param scanDetail The ScanDetail to cache
1981     */
1982    public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
1983        WifiConfiguration network = getInternalConfiguredNetwork(networkId);
1984        if (network == null) {
1985            return;
1986        }
1987        saveToScanDetailCacheForNetwork(network, scanDetail);
1988    }
1989
1990    /**
1991     * Helper method to check if the 2 provided networks can be linked or not.
1992     * Networks are considered for linking if:
1993     * 1. Share the same GW MAC address.
1994     * 2. Scan results for the networks have AP's with MAC address which differ only in the last
1995     * nibble.
1996     *
1997     * @param network1         WifiConfiguration corresponding to network 1.
1998     * @param network2         WifiConfiguration corresponding to network 2.
1999     * @param scanDetailCache1 ScanDetailCache entry for network 1.
2000     * @param scanDetailCache1 ScanDetailCache entry for network 2.
2001     * @return true if the networks should be linked, false if the networks should be unlinked.
2002     */
2003    private boolean shouldNetworksBeLinked(
2004            WifiConfiguration network1, WifiConfiguration network2,
2005            ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
2006        // TODO (b/30706406): Link networks only with same passwords if the
2007        // |mOnlyLinkSameCredentialConfigurations| flag is set.
2008        if (mOnlyLinkSameCredentialConfigurations) {
2009            if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
2010                if (mVerboseLoggingEnabled) {
2011                    Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch");
2012                }
2013                return false;
2014            }
2015        }
2016        if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
2017            // If both default GW are known, link only if they are equal
2018            if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) {
2019                if (mVerboseLoggingEnabled) {
2020                    Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
2021                            + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
2022                }
2023                return true;
2024            }
2025        } else {
2026            // We do not know BOTH default gateways hence we will try to link
2027            // hoping that WifiConfigurations are indeed behind the same gateway.
2028            // once both WifiConfiguration have been tried and thus once both default gateways
2029            // are known we will revisit the choice of linking them.
2030            if (scanDetailCache1 != null && scanDetailCache2 != null) {
2031                for (String abssid : scanDetailCache1.keySet()) {
2032                    for (String bbssid : scanDetailCache2.keySet()) {
2033                        if (abssid.regionMatches(
2034                                true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
2035                            // If first 16 ASCII characters of BSSID matches,
2036                            // we assume this is a DBDC.
2037                            if (mVerboseLoggingEnabled) {
2038                                Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
2039                                        + network2.SSID + " and " + network1.SSID
2040                                        + " bssida " + abssid + " bssidb " + bbssid);
2041                            }
2042                            return true;
2043                        }
2044                    }
2045                }
2046            }
2047        }
2048        return false;
2049    }
2050
2051    /**
2052     * Helper methods to link 2 networks together.
2053     *
2054     * @param network1 WifiConfiguration corresponding to network 1.
2055     * @param network2 WifiConfiguration corresponding to network 2.
2056     */
2057    private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2058        if (mVerboseLoggingEnabled) {
2059            Log.v(TAG, "linkNetworks will link " + network2.configKey()
2060                    + " and " + network1.configKey());
2061        }
2062        if (network2.linkedConfigurations == null) {
2063            network2.linkedConfigurations = new HashMap<>();
2064        }
2065        if (network1.linkedConfigurations == null) {
2066            network1.linkedConfigurations = new HashMap<>();
2067        }
2068        // TODO (b/30638473): This needs to become a set instead of map, but it will need
2069        // public interface changes and need some migration of existing store data.
2070        network2.linkedConfigurations.put(network1.configKey(), 1);
2071        network1.linkedConfigurations.put(network2.configKey(), 1);
2072    }
2073
2074    /**
2075     * Helper methods to unlink 2 networks from each other.
2076     *
2077     * @param network1 WifiConfiguration corresponding to network 1.
2078     * @param network2 WifiConfiguration corresponding to network 2.
2079     */
2080    private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2081        if (network2.linkedConfigurations != null
2082                && (network2.linkedConfigurations.get(network1.configKey()) != null)) {
2083            if (mVerboseLoggingEnabled) {
2084                Log.v(TAG, "unlinkNetworks un-link " + network1.configKey()
2085                        + " from " + network2.configKey());
2086            }
2087            network2.linkedConfigurations.remove(network1.configKey());
2088        }
2089        if (network1.linkedConfigurations != null
2090                && (network1.linkedConfigurations.get(network2.configKey()) != null)) {
2091            if (mVerboseLoggingEnabled) {
2092                Log.v(TAG, "unlinkNetworks un-link " + network2.configKey()
2093                        + " from " + network1.configKey());
2094            }
2095            network1.linkedConfigurations.remove(network2.configKey());
2096        }
2097    }
2098
2099    /**
2100     * This method runs through all the saved networks and checks if the provided network can be
2101     * linked with any of them.
2102     *
2103     * @param config WifiConfiguration object corresponding to the network that needs to be
2104     *               checked for potential links.
2105     */
2106    private void attemptNetworkLinking(WifiConfiguration config) {
2107        // Only link WPA_PSK config.
2108        if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2109            return;
2110        }
2111        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2112        // Ignore configurations with large number of BSSIDs.
2113        if (scanDetailCache != null
2114                && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2115            return;
2116        }
2117        for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
2118            if (linkConfig.configKey().equals(config.configKey())) {
2119                continue;
2120            }
2121            if (linkConfig.ephemeral) {
2122                continue;
2123            }
2124            // Network Selector will be allowed to dynamically jump from a linked configuration
2125            // to another, hence only link configurations that have WPA_PSK security type.
2126            if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2127                continue;
2128            }
2129            ScanDetailCache linkScanDetailCache =
2130                    getScanDetailCacheForNetwork(linkConfig.networkId);
2131            // Ignore configurations with large number of BSSIDs.
2132            if (linkScanDetailCache != null
2133                    && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2134                continue;
2135            }
2136            // Check if the networks should be linked/unlinked.
2137            if (shouldNetworksBeLinked(
2138                    config, linkConfig, scanDetailCache, linkScanDetailCache)) {
2139                linkNetworks(config, linkConfig);
2140            } else {
2141                unlinkNetworks(config, linkConfig);
2142            }
2143        }
2144    }
2145
2146    /**
2147     * Helper method to fetch list of channels for a network from the associated ScanResult's cache
2148     * and add it to the provided channel as long as the size of the set is less than
2149     * |maxChannelSetSize|.
2150     *
2151     * @param channelSet        Channel set holding all the channels for the network.
2152     * @param scanDetailCache   ScanDetailCache entry associated with the network.
2153     * @param nowInMillis       current timestamp to be used for age comparison.
2154     * @param ageInMillis       only consider scan details whose timestamps are earlier than this
2155     *                          value.
2156     * @param maxChannelSetSize Maximum number of channels to be added to the set.
2157     * @return false if the list is full, true otherwise.
2158     */
2159    private boolean addToChannelSetForNetworkFromScanDetailCache(
2160            Set<Integer> channelSet, ScanDetailCache scanDetailCache,
2161            long nowInMillis, long ageInMillis, int maxChannelSetSize) {
2162        if (scanDetailCache != null && scanDetailCache.size() > 0) {
2163            for (ScanDetail scanDetail : scanDetailCache.values()) {
2164                ScanResult result = scanDetail.getScanResult();
2165                boolean valid = (nowInMillis - result.seen) < ageInMillis;
2166                if (mVerboseLoggingEnabled) {
2167                    Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq "
2168                            + result.frequency + " age " + (nowInMillis - result.seen)
2169                            + " ?=" + valid);
2170                }
2171                if (valid) {
2172                    channelSet.add(result.frequency);
2173                }
2174                if (channelSet.size() >= maxChannelSetSize) {
2175                    return false;
2176                }
2177            }
2178        }
2179        return true;
2180    }
2181
2182    /**
2183     * Retrieve a set of channels on which AP's for the provided network was seen using the
2184     * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial
2185     * scans for the currently connected network.
2186     *
2187     * @param networkId       network ID corresponding to the network.
2188     * @param ageInMillis     only consider scan details whose timestamps are earlier than this value.
2189     * @param homeChannelFreq frequency of the currently connected network.
2190     * @return Set containing the frequencies on which this network was found, null if the network
2191     * was not found or there are no associated scan details in the cache.
2192     */
2193    public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis,
2194                                                                int homeChannelFreq) {
2195        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2196        if (config == null) {
2197            return null;
2198        }
2199        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId);
2200        if (scanDetailCache == null && config.linkedConfigurations == null) {
2201            Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId);
2202            return null;
2203        }
2204        if (mVerboseLoggingEnabled) {
2205            StringBuilder dbg = new StringBuilder();
2206            dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ")
2207                    .append(ageInMillis)
2208                    .append(" for ")
2209                    .append(config.configKey())
2210                    .append(" max ")
2211                    .append(mMaxNumActiveChannelsForPartialScans);
2212            if (scanDetailCache != null) {
2213                dbg.append(" bssids " + scanDetailCache.size());
2214            }
2215            if (config.linkedConfigurations != null) {
2216                dbg.append(" linked " + config.linkedConfigurations.size());
2217            }
2218            Log.v(TAG, dbg.toString());
2219        }
2220        Set<Integer> channelSet = new HashSet<>();
2221
2222        // First add the currently connected network channel.
2223        if (homeChannelFreq > 0) {
2224            channelSet.add(homeChannelFreq);
2225            if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) {
2226                return channelSet;
2227            }
2228        }
2229
2230        long nowInMillis = mClock.getWallClockMillis();
2231
2232        // Then get channels for the network.
2233        if (!addToChannelSetForNetworkFromScanDetailCache(
2234                channelSet, scanDetailCache, nowInMillis, ageInMillis,
2235                mMaxNumActiveChannelsForPartialScans)) {
2236            return channelSet;
2237        }
2238
2239        // Lastly get channels for linked networks.
2240        if (config.linkedConfigurations != null) {
2241            for (String configKey : config.linkedConfigurations.keySet()) {
2242                WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey);
2243                if (linkedConfig == null) {
2244                    continue;
2245                }
2246                ScanDetailCache linkedScanDetailCache =
2247                        getScanDetailCacheForNetwork(linkedConfig.networkId);
2248                if (!addToChannelSetForNetworkFromScanDetailCache(
2249                        channelSet, linkedScanDetailCache, nowInMillis, ageInMillis,
2250                        mMaxNumActiveChannelsForPartialScans)) {
2251                    break;
2252                }
2253            }
2254        }
2255        return channelSet;
2256    }
2257
2258    /**
2259     * Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
2260     *
2261     * PNO network list sent to the firmware has limited size. If there are a lot of saved
2262     * networks, this list will be truncated and we might end up not sending the networks
2263     * with the highest chance of connecting to the firmware.
2264     * So, re-sort the network list based on the frequency of connection to those networks
2265     * and whether it was last seen in the scan results.
2266     *
2267     * TODO (b/30399964): Recalculate the list whenever network status changes.
2268     * @return list of networks with updated priorities.
2269     */
2270    public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
2271        List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
2272        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2273        // Remove any permanently disabled networks.
2274        Iterator<WifiConfiguration> iter = networks.iterator();
2275        while (iter.hasNext()) {
2276            WifiConfiguration config = iter.next();
2277            if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2278                iter.remove();
2279            }
2280        }
2281        Collections.sort(networks, sScanListComparator);
2282        // Let's use the network list size - 1 as the highest priority and then go down from there.
2283        // So, the most frequently connected network has the highest priority now.
2284        int priority = networks.size() - 1;
2285        for (WifiConfiguration config : networks) {
2286            pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority));
2287            priority--;
2288        }
2289        return pnoList;
2290    }
2291
2292    /**
2293     * Retrieves a list of all the saved hidden networks for scans.
2294     *
2295     * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2296     * networks, this list will be truncated and we might end up not sending the networks
2297     * with the highest chance of connecting to the firmware.
2298     * So, re-sort the network list based on the frequency of connection to those networks
2299     * and whether it was last seen in the scan results.
2300     *
2301     * @return list of networks with updated priorities.
2302     */
2303    public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
2304        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2305        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2306        // Remove any permanently disabled networks or non hidden networks.
2307        Iterator<WifiConfiguration> iter = networks.iterator();
2308        while (iter.hasNext()) {
2309            WifiConfiguration config = iter.next();
2310            if (!config.hiddenSSID ||
2311                    config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2312                iter.remove();
2313            }
2314        }
2315        Collections.sort(networks, sScanListComparator);
2316        // Let's use the network list size - 1 as the highest priority and then go down from there.
2317        // So, the most frequently connected network has the highest priority now.
2318        int priority = networks.size() - 1;
2319        for (WifiConfiguration config : networks) {
2320            hiddenList.add(
2321                    new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
2322            priority--;
2323        }
2324        return hiddenList;
2325    }
2326
2327    /**
2328     * Check if the provided ephemeral network was deleted by the user or not.
2329     *
2330     * @param ssid caller must ensure that the SSID passed thru this API match
2331     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2332     * @return true if network was deleted, false otherwise.
2333     */
2334    public boolean wasEphemeralNetworkDeleted(String ssid) {
2335        return mDeletedEphemeralSSIDs.contains(ssid);
2336    }
2337
2338    /**
2339     * Disable an ephemeral SSID for the purpose of network selection.
2340     *
2341     * The only way to "un-disable it" is if the user create a network for that SSID and then
2342     * forget it.
2343     *
2344     * @param ssid caller must ensure that the SSID passed thru this API match
2345     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2346     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
2347     * disconnect if this is the current network.
2348     */
2349    public WifiConfiguration disableEphemeralNetwork(String ssid) {
2350        if (ssid == null) {
2351            return null;
2352        }
2353        WifiConfiguration foundConfig = null;
2354        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2355            if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) {
2356                foundConfig = config;
2357                break;
2358            }
2359        }
2360        mDeletedEphemeralSSIDs.add(ssid);
2361        Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
2362        if (foundConfig != null) {
2363            Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: "
2364                    + foundConfig.networkId);
2365        }
2366        return foundConfig;
2367    }
2368
2369    /**
2370     * Resets all sim networks state.
2371     */
2372    public void resetSimNetworks(boolean simPresent) {
2373        if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
2374        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2375            if (TelephonyUtil.isSimConfig(config)) {
2376                String currentIdentity = null;
2377                if (simPresent) {
2378                    currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config);
2379                }
2380                // Update the loaded config
2381                config.enterpriseConfig.setIdentity(currentIdentity);
2382                if (config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.PEAP) {
2383                    config.enterpriseConfig.setAnonymousIdentity("");
2384                }
2385            }
2386        }
2387        mSimPresent = simPresent;
2388    }
2389
2390    /**
2391     * Check if SIM is present.
2392     *
2393     * @return True if SIM is present, otherwise false.
2394     */
2395    public boolean isSimPresent() {
2396        return mSimPresent;
2397    }
2398
2399    /**
2400     * Any network using certificates to authenticate access requires unlocked key store; unless
2401     * the certificates can be stored with hardware encryption
2402     *
2403     * @return true if we need an unlocked keystore, false otherwise.
2404     */
2405    public boolean needsUnlockedKeyStore() {
2406        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2407            if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
2408                if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
2409                    return true;
2410                }
2411            }
2412        }
2413        return false;
2414    }
2415
2416    /**
2417     * Helper method to perform the following operations during user switch/unlock:
2418     * - Remove private networks of the old user.
2419     * - Load from the new user store file.
2420     * - Save the store files again to migrate any user specific networks from the shared store
2421     *   to user store.
2422     * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
2423     * should ensure that the stores are accessible before invocation.
2424     *
2425     * @param userId The identifier of the new foreground user, after the unlock or switch.
2426     */
2427    private void handleUserUnlockOrSwitch(int userId) {
2428        if (mVerboseLoggingEnabled) {
2429            Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
2430        }
2431        // Switch out the user store file.
2432        if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
2433            saveToStore(true);
2434            mPendingUnlockStoreRead = false;
2435        }
2436    }
2437
2438    /**
2439     * Handles the switch to a different foreground user:
2440     * - Flush the current state to the old user's store file.
2441     * - Switch the user specific store file.
2442     * - Reload the networks from the store files (shared & user).
2443     * - Write the store files to move any user specific private networks from shared store to user
2444     *   store.
2445     *
2446     * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked.
2447     *
2448     * @param userId The identifier of the new foreground user, after the switch.
2449     * @return List of network ID's of all the private networks of the old user which will be
2450     * removed from memory.
2451     */
2452    public Set<Integer> handleUserSwitch(int userId) {
2453        if (mVerboseLoggingEnabled) {
2454            Log.v(TAG, "Handling user switch for " + userId);
2455        }
2456        if (userId == mCurrentUserId) {
2457            Log.w(TAG, "User already in foreground " + userId);
2458            return new HashSet<>();
2459        }
2460        if (mPendingStoreRead) {
2461            Log.wtf(TAG, "Unexpected user switch before store is read!");
2462            return new HashSet<>();
2463        }
2464        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2465            saveToStore(true);
2466        }
2467        // Remove any private networks of the old user before switching the userId.
2468        Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId);
2469        mConfiguredNetworks.setNewUser(userId);
2470        mCurrentUserId = userId;
2471
2472        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2473            handleUserUnlockOrSwitch(mCurrentUserId);
2474        } else {
2475            // Cannot read data from new user's CE store file before they log-in.
2476            mPendingUnlockStoreRead = true;
2477            Log.i(TAG, "Waiting for user unlock to load from store");
2478        }
2479        return removedNetworkIds;
2480    }
2481
2482    /**
2483     * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
2484     * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
2485     *
2486     * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked.
2487     *
2488     * @param userId The identifier of the user that unlocked.
2489     */
2490    public void handleUserUnlock(int userId) {
2491        if (mVerboseLoggingEnabled) {
2492            Log.v(TAG, "Handling user unlock for " + userId);
2493        }
2494        if (mPendingStoreRead) {
2495            Log.w(TAG, "Ignore user unlock until store is read!");
2496            mDeferredUserUnlockRead = true;
2497            return;
2498        }
2499        if (userId == mCurrentUserId && mPendingUnlockStoreRead) {
2500            handleUserUnlockOrSwitch(mCurrentUserId);
2501        }
2502    }
2503
2504    /**
2505     * Handles the stop of foreground user. This is needed to write the store file to flush
2506     * out any pending data before the user's CE store storage is unavailable.
2507     *
2508     * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked.
2509     *
2510     * @param userId The identifier of the user that stopped.
2511     */
2512    public void handleUserStop(int userId) {
2513        if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2514            saveToStore(true);
2515            clearInternalData();
2516            mCurrentUserId = UserHandle.USER_SYSTEM;
2517        }
2518    }
2519
2520    /**
2521     * Helper method to clear internal databases.
2522     * This method clears the:
2523     *  - List of configured networks.
2524     *  - Map of scan detail caches.
2525     *  - List of deleted ephemeral networks.
2526     */
2527    private void clearInternalData() {
2528        mConfiguredNetworks.clear();
2529        mDeletedEphemeralSSIDs.clear();
2530        mScanDetailCaches.clear();
2531        clearLastSelectedNetwork();
2532    }
2533
2534    /**
2535     * Helper method to clear internal databases of the specified user.
2536     * This method clears the:
2537     *  - Private configured configured networks of the specified user.
2538     *  - Map of scan detail caches.
2539     *  - List of deleted ephemeral networks.
2540     *
2541     * @param userId The identifier of the current foreground user, before the switch.
2542     * @return List of network ID's of all the private networks of the old user which will be
2543     * removed from memory.
2544     */
2545    private Set<Integer> clearInternalUserData(int userId) {
2546        Set<Integer> removedNetworkIds = new HashSet<>();
2547        // Remove any private networks of the old user before switching the userId.
2548        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2549            if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2550                    config.creatorUid, mUserManager.getProfiles(userId))) {
2551                removedNetworkIds.add(config.networkId);
2552                mConfiguredNetworks.remove(config.networkId);
2553            }
2554        }
2555        mDeletedEphemeralSSIDs.clear();
2556        mScanDetailCaches.clear();
2557        clearLastSelectedNetwork();
2558        return removedNetworkIds;
2559    }
2560
2561    /**
2562     * Helper function to populate the internal (in-memory) data from the retrieved shared store
2563     * (file) data.
2564     *
2565     * @param configurations list of configurations retrieved from store.
2566     */
2567    private void loadInternalDataFromSharedStore(
2568            List<WifiConfiguration> configurations) {
2569        for (WifiConfiguration configuration : configurations) {
2570            configuration.networkId = mNextNetworkId++;
2571            if (mVerboseLoggingEnabled) {
2572                Log.v(TAG, "Adding network from shared store " + configuration.configKey());
2573            }
2574            mConfiguredNetworks.put(configuration);
2575        }
2576    }
2577
2578    /**
2579     * Helper function to populate the internal (in-memory) data from the retrieved user store
2580     * (file) data.
2581     *
2582     * @param configurations        list of configurations retrieved from store.
2583     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2584     *                              the user.
2585     */
2586    private void loadInternalDataFromUserStore(
2587            List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
2588        for (WifiConfiguration configuration : configurations) {
2589            configuration.networkId = mNextNetworkId++;
2590            if (mVerboseLoggingEnabled) {
2591                Log.v(TAG, "Adding network from user store " + configuration.configKey());
2592            }
2593            mConfiguredNetworks.put(configuration);
2594        }
2595        for (String ssid : deletedEphemeralSSIDs) {
2596            mDeletedEphemeralSSIDs.add(ssid);
2597        }
2598    }
2599
2600    /**
2601     * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
2602     * data.
2603     * This method:
2604     * 1. Clears all existing internal data.
2605     * 2. Sends out the networks changed broadcast after loading all the data.
2606     *
2607     * @param sharedConfigurations  list of  network configurations retrieved from shared store.
2608     * @param userConfigurations    list of  network configurations retrieved from user store.
2609     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2610     *                              the user.
2611     */
2612    private void loadInternalData(
2613            List<WifiConfiguration> sharedConfigurations,
2614            List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) {
2615        // Clear out all the existing in-memory lists and load the lists from what was retrieved
2616        // from the config store.
2617        clearInternalData();
2618        loadInternalDataFromSharedStore(sharedConfigurations);
2619        loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs);
2620        if (mConfiguredNetworks.sizeForAllUsers() == 0) {
2621            Log.w(TAG, "No stored networks found.");
2622        }
2623        sendConfiguredNetworksChangedBroadcast();
2624        mPendingStoreRead = false;
2625    }
2626
2627    /**
2628     * Migrate data from legacy store files. The function performs the following operations:
2629     * 1. Check if the legacy store files are present.
2630     * 2. If yes, read all the data from the store files.
2631     * 3. Save it to the new store files.
2632     * 4. Delete the legacy store file.
2633     *
2634     * @return true if migration was successful or not needed (fresh install), false if it failed.
2635     */
2636    public boolean migrateFromLegacyStore() {
2637        if (!mWifiConfigStoreLegacy.areStoresPresent()) {
2638            Log.d(TAG, "Legacy store files not found. No migration needed!");
2639            return true;
2640        }
2641        WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read();
2642        Log.d(TAG, "Reading from legacy store completed");
2643        loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(),
2644                storeData.getDeletedEphemeralSSIDs());
2645
2646        // Setup user store for the current user in case it have not setup yet, so that data
2647        // owned by the current user will be backed to the user store.
2648        if (mDeferredUserUnlockRead) {
2649            mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
2650            mDeferredUserUnlockRead = false;
2651        }
2652
2653        if (!saveToStore(true)) {
2654            return false;
2655        }
2656        mWifiConfigStoreLegacy.removeStores();
2657        Log.d(TAG, "Migration from legacy store completed");
2658        return true;
2659    }
2660
2661    /**
2662     * Read the config store and load the in-memory lists from the store data retrieved and sends
2663     * out the networks changed broadcast.
2664     *
2665     * This reads all the network configurations from:
2666     * 1. Shared WifiConfigStore.xml
2667     * 2. User WifiConfigStore.xml
2668     *
2669     * @return true on success or not needed (fresh install/pending legacy store migration),
2670     * false otherwise.
2671     */
2672    public boolean loadFromStore() {
2673        if (!mWifiConfigStore.areStoresPresent()) {
2674            Log.d(TAG, "New store files not found. No saved networks loaded!");
2675            if (!mWifiConfigStoreLegacy.areStoresPresent()) {
2676                // No legacy store files either, so reset the pending store read flag.
2677                mPendingStoreRead = false;
2678            }
2679            return true;
2680        }
2681        // If the user unlock comes in before we load from store, which means the user store have
2682        // not been setup yet for the current user.  Setup the user store before the read so that
2683        // configurations for the current user will also being loaded.
2684        if (mDeferredUserUnlockRead) {
2685            Log.i(TAG, "Handling user unlock before loading from store.");
2686            mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
2687            mDeferredUserUnlockRead = false;
2688        }
2689        try {
2690            mWifiConfigStore.read();
2691        } catch (IOException e) {
2692            Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
2693            return false;
2694        } catch (XmlPullParserException e) {
2695            Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
2696            return false;
2697        }
2698        loadInternalData(mNetworkListStoreData.getSharedConfigurations(),
2699                mNetworkListStoreData.getUserConfigurations(),
2700                mDeletedEphemeralSsidsStoreData.getSsidList());
2701        return true;
2702    }
2703
2704    /**
2705     * Read the user config store and load the in-memory lists from the store data retrieved and
2706     * sends out the networks changed broadcast.
2707     * This should be used for all user switches/unlocks to only load networks from the user
2708     * specific store and avoid reloading the shared networks.
2709     *
2710     * This reads all the network configurations from:
2711     * 1. User WifiConfigStore.xml
2712     *
2713     * @param userId The identifier of the foreground user.
2714     * @return true on success, false otherwise.
2715     */
2716    public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
2717        try {
2718            mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId));
2719        } catch (IOException e) {
2720            Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
2721            return false;
2722        } catch (XmlPullParserException e) {
2723            Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" +
2724                    "lost!", e);
2725            return false;
2726        }
2727        loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(),
2728                mDeletedEphemeralSsidsStoreData.getSsidList());
2729        return true;
2730    }
2731
2732    /**
2733     * Save the current snapshot of the in-memory lists to the config store.
2734     *
2735     * @param forceWrite Whether the write needs to be forced or not.
2736     * @return Whether the write was successful or not, this is applicable only for force writes.
2737     */
2738    public boolean saveToStore(boolean forceWrite) {
2739        ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
2740        ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
2741        // List of network IDs for legacy Passpoint configuration to be removed.
2742        List<Integer> legacyPasspointNetId = new ArrayList<>();
2743        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
2744            // Ignore ephemeral networks and non-legacy Passpoint configurations.
2745            if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
2746                continue;
2747            }
2748
2749            // Migrate the legacy Passpoint configurations owned by the current user to
2750            // {@link PasspointManager}.
2751            if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2752                        config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2753                legacyPasspointNetId.add(config.networkId);
2754                // Migrate the legacy Passpoint configuration and add it to PasspointManager.
2755                if (!PasspointManager.addLegacyPasspointConfig(config)) {
2756                    Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
2757                }
2758                // This will prevent adding |config| to the |sharedConfigurations|.
2759                continue;
2760            }
2761
2762            // We push all shared networks & private networks not belonging to the current
2763            // user to the shared store. Ideally, private networks for other users should
2764            // not even be in memory,
2765            // But, this logic is in place to deal with store migration from N to O
2766            // because all networks were previously stored in a central file. We cannot
2767            // write these private networks to the user specific store until the corresponding
2768            // user logs in.
2769            if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile(
2770                    config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2771                sharedConfigurations.add(config);
2772            } else {
2773                userConfigurations.add(config);
2774            }
2775        }
2776
2777        // Remove the configurations for migrated Passpoint configurations.
2778        for (int networkId : legacyPasspointNetId) {
2779            mConfiguredNetworks.remove(networkId);
2780        }
2781
2782        // Setup store data for write.
2783        mNetworkListStoreData.setSharedConfigurations(sharedConfigurations);
2784        mNetworkListStoreData.setUserConfigurations(userConfigurations);
2785        mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs);
2786
2787        try {
2788            mWifiConfigStore.write(forceWrite);
2789        } catch (IOException e) {
2790            Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
2791            return false;
2792        } catch (XmlPullParserException e) {
2793            Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
2794            return false;
2795        }
2796        return true;
2797    }
2798
2799    /**
2800     * Helper method for logging into local log buffer.
2801     */
2802    private void localLog(String s) {
2803        if (mLocalLog != null) {
2804            mLocalLog.log(s);
2805        }
2806    }
2807
2808    /**
2809     * Dump the local log buffer and other internal state of WifiConfigManager.
2810     */
2811    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2812        pw.println("Dump of WifiConfigManager");
2813        pw.println("WifiConfigManager - Log Begin ----");
2814        mLocalLog.dump(fd, pw, args);
2815        pw.println("WifiConfigManager - Log End ----");
2816        pw.println("WifiConfigManager - Configured networks Begin ----");
2817        for (WifiConfiguration network : getInternalConfiguredNetworks()) {
2818            pw.println(network);
2819        }
2820        pw.println("WifiConfigManager - Configured networks End ----");
2821        pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
2822        pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
2823    }
2824
2825    /**
2826     * Returns true if the given uid has permission to add, update or remove proxy settings
2827     */
2828    private boolean canModifyProxySettings(int uid) {
2829        final DevicePolicyManagerInternal dpmi =
2830                mWifiPermissionsWrapper.getDevicePolicyManagerInternal();
2831        final boolean isUidProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
2832                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
2833        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
2834                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
2835        final boolean hasConfigOverridePermission =
2836                mWifiPermissionsUtil.checkConfigOverridePermission(uid);
2837        // If |uid| corresponds to the device owner, allow all modifications.
2838        if (isUidDeviceOwner || isUidProfileOwner || hasConfigOverridePermission) {
2839            return true;
2840        }
2841        if (mVerboseLoggingEnabled) {
2842            Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
2843                    + " ConfigOverride=" + hasConfigOverridePermission
2844                    + " DeviceOwner=" + isUidDeviceOwner
2845                    + " ProfileOwner=" + isUidProfileOwner);
2846        }
2847        return false;
2848    }
2849
2850    /**
2851     * Set the saved network update event listener
2852     */
2853    public void setOnSavedNetworkUpdateListener(OnSavedNetworkUpdateListener listener) {
2854        mListener = listener;
2855    }
2856}
2857