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