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