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