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