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