WifiConfigManager.java revision cdd56a55709407c9efe2a24b4ed25666e2e8318e
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        NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
1002        if (!result.isSuccess()) {
1003            Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
1004            return result;
1005        }
1006        WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
1007        sendConfiguredNetworkChangedBroadcast(
1008                newConfig,
1009                result.isNewNetwork()
1010                        ? WifiManager.CHANGE_REASON_ADDED
1011                        : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1012        // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
1013        if (!config.ephemeral && !config.isPasspoint()) {
1014            saveToStore(true);
1015        }
1016        return result;
1017    }
1018
1019    /**
1020     * Removes the specified network configuration from our database.
1021     *
1022     * @param config provided WifiConfiguration object.
1023     * @return true if successful, false otherwise.
1024     */
1025    private boolean removeNetworkInternal(WifiConfiguration config) {
1026        if (mVerboseLoggingEnabled) {
1027            Log.v(TAG, "Removing network " + config.getPrintableSsid());
1028        }
1029        // Remove any associated enterprise keys for non-Passpoint networks.
1030        if (!config.isPasspoint() && config.enterpriseConfig != null
1031                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1032            mWifiKeyStore.removeKeys(config.enterpriseConfig);
1033        }
1034
1035        removeConnectChoiceFromAllNetworks(config.configKey());
1036        mConfiguredNetworks.remove(config.networkId);
1037        mScanDetailCaches.remove(config.networkId);
1038        // Stage the backup of the SettingsProvider package which backs this up.
1039        mBackupManagerProxy.notifyDataChanged();
1040
1041        localLog("removeNetworkInternal: removed config."
1042                + " netId=" + config.networkId
1043                + " configKey=" + config.configKey());
1044        return true;
1045    }
1046
1047    /**
1048     * Removes the specified network configuration from our database.
1049     *
1050     * @param networkId network ID of the provided network.
1051     * @param uid       UID of the app requesting the network deletion.
1052     * @return true if successful, false otherwise.
1053     */
1054    public boolean removeNetwork(int networkId, int uid) {
1055        if (!doesUidBelongToCurrentUser(uid)) {
1056            Log.e(TAG, "UID " + uid + " not visible to the current user");
1057            return false;
1058        }
1059        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1060        if (config == null) {
1061            return false;
1062        }
1063        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1064            Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1065                    + config.configKey());
1066            return false;
1067        }
1068        if (!removeNetworkInternal(config)) {
1069            Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1070            return false;
1071        }
1072        if (networkId == mLastSelectedNetworkId) {
1073            clearLastSelectedNetwork();
1074        }
1075        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
1076        // Unless the removed network is ephemeral or Passpoint, persist the network removal.
1077        if (!config.ephemeral && !config.isPasspoint()) {
1078            saveToStore(true);
1079        }
1080        return true;
1081    }
1082
1083    /**
1084     * Remove all networks associated with an application.
1085     *
1086     * @param app Application info of the package of networks to remove.
1087     * @return the {@link Set} of networks that were removed by this call. Networks which matched
1088     *         but failed to remove are omitted from this set.
1089     */
1090    public Set<Integer> removeNetworksForApp(ApplicationInfo app) {
1091        if (app == null || app.packageName == null) {
1092            return Collections.<Integer>emptySet();
1093        }
1094        Log.d(TAG, "Remove all networks for app " + app);
1095        Set<Integer> removedNetworks = new ArraySet<>();
1096        WifiConfiguration[] copiedConfigs =
1097                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1098        for (WifiConfiguration config : copiedConfigs) {
1099            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
1100                continue;
1101            }
1102            localLog("Removing network " + config.SSID
1103                    + ", application \"" + app.packageName + "\" uninstalled"
1104                    + " from user " + UserHandle.getUserId(app.uid));
1105            if (removeNetwork(config.networkId, mSystemUiUid)) {
1106                removedNetworks.add(config.networkId);
1107            }
1108        }
1109        return removedNetworks;
1110    }
1111
1112    /**
1113     * Remove all networks associated with a user.
1114     *
1115     * @param userId The identifier of the user which is being removed.
1116     * @return the {@link Set} of networks that were removed by this call. Networks which matched
1117     *         but failed to remove are omitted from this set.
1118     */
1119    Set<Integer> removeNetworksForUser(int userId) {
1120        Log.d(TAG, "Remove all networks for user " + userId);
1121        Set<Integer> removedNetworks = new ArraySet<>();
1122        WifiConfiguration[] copiedConfigs =
1123                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1124        for (WifiConfiguration config : copiedConfigs) {
1125            if (userId != UserHandle.getUserId(config.creatorUid)) {
1126                continue;
1127            }
1128            localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1129            if (removeNetwork(config.networkId, mSystemUiUid)) {
1130                removedNetworks.add(config.networkId);
1131            }
1132        }
1133        return removedNetworks;
1134    }
1135
1136    /**
1137     * Helper method to mark a network enabled for network selection.
1138     */
1139    private void setNetworkSelectionEnabled(NetworkSelectionStatus status) {
1140        status.setNetworkSelectionStatus(
1141                NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
1142        status.setDisableTime(
1143                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1144        status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1145
1146        // Clear out all the disable reason counters.
1147        status.clearDisableReasonCounter();
1148    }
1149
1150    /**
1151     * Helper method to mark a network temporarily disabled for network selection.
1152     */
1153    private void setNetworkSelectionTemporarilyDisabled(
1154            NetworkSelectionStatus status, int disableReason) {
1155        status.setNetworkSelectionStatus(
1156                NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
1157        // Only need a valid time filled in for temporarily disabled networks.
1158        status.setDisableTime(mClock.getElapsedSinceBootMillis());
1159        status.setNetworkSelectionDisableReason(disableReason);
1160    }
1161
1162    /**
1163     * Helper method to mark a network permanently disabled for network selection.
1164     */
1165    private void setNetworkSelectionPermanentlyDisabled(
1166            NetworkSelectionStatus status, int disableReason) {
1167        status.setNetworkSelectionStatus(
1168                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1169        status.setDisableTime(
1170                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1171        status.setNetworkSelectionDisableReason(disableReason);
1172    }
1173
1174    /**
1175     * Helper method to set the publicly exposed status for the network and send out the network
1176     * status change broadcast.
1177     */
1178    private void setNetworkStatus(WifiConfiguration config, int status) {
1179        config.status = status;
1180        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1181    }
1182
1183    /**
1184     * Sets a network's status (both internal and public) according to the update reason and
1185     * its current state.
1186     *
1187     * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
1188     * public {@link WifiConfiguration#status} field if the network is either enabled or
1189     * permanently disabled.
1190     *
1191     * @param config network to be updated.
1192     * @param reason reason code for update.
1193     * @return true if the input configuration has been updated, false otherwise.
1194     */
1195    private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
1196        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1197        if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
1198            Log.e(TAG, "Invalid Network disable reason " + reason);
1199            return false;
1200        }
1201        if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1202            setNetworkSelectionEnabled(networkStatus);
1203            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1204        } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
1205            setNetworkSelectionTemporarilyDisabled(networkStatus, reason);
1206        } else {
1207            setNetworkSelectionPermanentlyDisabled(networkStatus, reason);
1208            setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
1209        }
1210        localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
1211                + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
1212                + networkStatus.getNetworkDisableReasonString() + " at="
1213                + createDebugTimeStampString(mClock.getWallClockMillis()));
1214        saveToStore(false);
1215        return true;
1216    }
1217
1218    /**
1219     * Update a network's status (both internal and public) according to the update reason and
1220     * its current state.
1221     *
1222     * @param config network to be updated.
1223     * @param reason reason code for update.
1224     * @return true if the input configuration has been updated, false otherwise.
1225     */
1226    private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1227        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1228        if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1229            networkStatus.incrementDisableReasonCounter(reason);
1230            // For network disable reasons, we should only update the status if we cross the
1231            // threshold.
1232            int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
1233            int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
1234            if (disableReasonCounter < disableReasonThreshold) {
1235                if (mVerboseLoggingEnabled) {
1236                    Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
1237                            + " for reason "
1238                            + NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is "
1239                            + networkStatus.getDisableReasonCounter(reason) + " and threshold is "
1240                            + disableReasonThreshold);
1241                }
1242                return true;
1243            }
1244        }
1245        return setNetworkSelectionStatus(config, reason);
1246    }
1247
1248    /**
1249     * Update a network's status (both internal and public) according to the update reason and
1250     * its current state.
1251     *
1252     * Each network has 2 status:
1253     * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
1254     * for temporarily disabling a network for Network Selector.
1255     * 2. Status: This is the exposed status for a network. This is mostly set by
1256     * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
1257     * {@link WifiManager#disableNetwork(int)}.
1258     *
1259     * @param networkId network ID of the network that needs the update.
1260     * @param reason    reason to update the network.
1261     * @return true if the input configuration has been updated, false otherwise.
1262     */
1263    public boolean updateNetworkSelectionStatus(int networkId, int reason) {
1264        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1265        if (config == null) {
1266            return false;
1267        }
1268        return updateNetworkSelectionStatus(config, reason);
1269    }
1270
1271    /**
1272     * Update whether a network is currently not recommended by {@link RecommendedNetworkEvaluator}.
1273     *
1274     * @param networkId network ID of the network to be updated
1275     * @param notRecommended whether this network is not recommended
1276     * @return true if the network is updated, false otherwise
1277     */
1278    public boolean updateNetworkNotRecommended(int networkId, boolean notRecommended) {
1279        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1280        if (config == null) {
1281            return false;
1282        }
1283
1284        config.getNetworkSelectionStatus().setNotRecommended(notRecommended);
1285        localLog("updateNetworkRecommendation: configKey=" + config.configKey()
1286                + " recommended=" + notRecommended);
1287        saveToStore(false);
1288        return true;
1289    }
1290
1291    /**
1292     * Attempt to re-enable a network for network selection, if this network was either:
1293     * a) Previously temporarily disabled, but its disable timeout has expired, or
1294     * b) Previously disabled because of a user switch, but is now visible to the current
1295     * user.
1296     *
1297     * @param config configuration for the network to be re-enabled for network selection. The
1298     *               network corresponding to the config must be visible to the current user.
1299     * @return true if the network identified by {@param config} was re-enabled for qualified
1300     * network selection, false otherwise.
1301     */
1302    private boolean tryEnableNetwork(WifiConfiguration config) {
1303        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1304        if (networkStatus.isNetworkTemporaryDisabled()) {
1305            long timeDifferenceMs =
1306                    mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
1307            int disableReason = networkStatus.getNetworkSelectionDisableReason();
1308            long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
1309            if (timeDifferenceMs >= disableTimeoutMs) {
1310                return updateNetworkSelectionStatus(
1311                        config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1312            }
1313        } else if (networkStatus.isDisabledByReason(
1314                NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
1315            return updateNetworkSelectionStatus(
1316                    config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1317        }
1318        return false;
1319    }
1320
1321    /**
1322     * Attempt to re-enable a network for network selection, if this network was either:
1323     * a) Previously temporarily disabled, but its disable timeout has expired, or
1324     * b) Previously disabled because of a user switch, but is now visible to the current
1325     * user.
1326     *
1327     * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1328     * @return true if the network identified by {@param networkId} was re-enabled for qualified
1329     * network selection, false otherwise.
1330     */
1331    public boolean tryEnableNetwork(int networkId) {
1332        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1333        if (config == null) {
1334            return false;
1335        }
1336        return tryEnableNetwork(config);
1337    }
1338
1339    /**
1340     * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
1341     *
1342     * @param networkId     network ID of the network that needs the update.
1343     * @param disableOthers Whether to disable all other networks or not. This is used to indicate
1344     *                      that the app requested connection to a specific network.
1345     * @param uid           uid of the app requesting the update.
1346     * @return true if it succeeds, false otherwise
1347     */
1348    public boolean enableNetwork(int networkId, boolean disableOthers, int uid) {
1349        if (mVerboseLoggingEnabled) {
1350            Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
1351        }
1352        if (!doesUidBelongToCurrentUser(uid)) {
1353            Log.e(TAG, "UID " + uid + " not visible to the current user");
1354            return false;
1355        }
1356        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1357        if (config == null) {
1358            return false;
1359        }
1360        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1361            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1362                    + config.configKey());
1363            return false;
1364        }
1365        if (!updateNetworkSelectionStatus(
1366                networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) {
1367            return false;
1368        }
1369        if (disableOthers) {
1370            setLastSelectedNetwork(networkId);
1371        }
1372        return true;
1373    }
1374
1375    /**
1376     * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
1377     *
1378     * @param networkId network ID of the network that needs the update.
1379     * @param uid       uid of the app requesting the update.
1380     * @return true if it succeeds, false otherwise
1381     */
1382    public boolean disableNetwork(int networkId, int uid) {
1383        if (mVerboseLoggingEnabled) {
1384            Log.v(TAG, "Disabling network " + networkId);
1385        }
1386        if (!doesUidBelongToCurrentUser(uid)) {
1387            Log.e(TAG, "UID " + uid + " not visible to the current user");
1388            return false;
1389        }
1390        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1391        if (config == null) {
1392            return false;
1393        }
1394        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1395            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1396                    + config.configKey());
1397            return false;
1398        }
1399        if (!updateNetworkSelectionStatus(
1400                networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
1401            return false;
1402        }
1403        if (networkId == mLastSelectedNetworkId) {
1404            clearLastSelectedNetwork();
1405        }
1406        return true;
1407    }
1408
1409    /**
1410     * Checks if the |uid| has the necessary permission to force a connection to a network
1411     * and updates the last connected UID for the provided configuration.
1412     *
1413     * @param networkId network ID corresponding to the network.
1414     * @param uid       uid of the app requesting the connection.
1415     * @return true if |uid| has the necessary permission to trigger explicit connection to the
1416     * network, false otherwise.
1417     * Note: This returns true only for the system settings/sysui app which holds the
1418     * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let
1419     * any other app force connection to a network.
1420     */
1421    public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
1422        if (mVerboseLoggingEnabled) {
1423            Log.v(TAG, "Update network last connect UID for " + networkId);
1424        }
1425        if (!doesUidBelongToCurrentUser(uid)) {
1426            Log.e(TAG, "UID " + uid + " not visible to the current user");
1427            return false;
1428        }
1429        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1430        if (config == null) {
1431            return false;
1432        }
1433        if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) {
1434            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1435                    + config.configKey());
1436            return false;
1437        }
1438        config.lastConnectUid = uid;
1439        return true;
1440    }
1441
1442    /**
1443     * Updates a network configuration after a successful connection to it.
1444     *
1445     * This method updates the following WifiConfiguration elements:
1446     * 1. Set the |lastConnected| timestamp.
1447     * 2. Increment |numAssociation| counter.
1448     * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
1449     * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
1450     * 5. Sets the status of network as |CURRENT|.
1451     *
1452     * @param networkId network ID corresponding to the network.
1453     * @return true if the network was found, false otherwise.
1454     */
1455    public boolean updateNetworkAfterConnect(int networkId) {
1456        if (mVerboseLoggingEnabled) {
1457            Log.v(TAG, "Update network after connect for " + networkId);
1458        }
1459        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1460        if (config == null) {
1461            return false;
1462        }
1463        config.lastConnected = mClock.getWallClockMillis();
1464        config.numAssociation++;
1465        config.getNetworkSelectionStatus().clearDisableReasonCounter();
1466        config.getNetworkSelectionStatus().setHasEverConnected(true);
1467        setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
1468        saveToStore(false);
1469        return true;
1470    }
1471
1472    /**
1473     * Updates a network configuration after disconnection from it.
1474     *
1475     * This method updates the following WifiConfiguration elements:
1476     * 1. Set the |lastDisConnected| timestamp.
1477     * 2. Sets the status of network back to |ENABLED|.
1478     *
1479     * @param networkId network ID corresponding to the network.
1480     * @return true if the network was found, false otherwise.
1481     */
1482    public boolean updateNetworkAfterDisconnect(int networkId) {
1483        if (mVerboseLoggingEnabled) {
1484            Log.v(TAG, "Update network after disconnect for " + networkId);
1485        }
1486        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1487        if (config == null) {
1488            return false;
1489        }
1490        config.lastDisconnected = mClock.getWallClockMillis();
1491        // If the network hasn't been disabled, mark it back as
1492        // enabled after disconnection.
1493        if (config.status == WifiConfiguration.Status.CURRENT) {
1494            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1495        }
1496        saveToStore(false);
1497        return true;
1498    }
1499
1500    /**
1501     * Set default GW MAC address for the provided network.
1502     *
1503     * @param networkId  network ID corresponding to the network.
1504     * @param macAddress MAC address of the gateway to be set.
1505     * @return true if the network was found, false otherwise.
1506     */
1507    public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
1508        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1509        if (config == null) {
1510            return false;
1511        }
1512        config.defaultGwMacAddress = macAddress;
1513        return true;
1514    }
1515
1516    /**
1517     * Clear the {@link NetworkSelectionStatus#mCandidate},
1518     * {@link NetworkSelectionStatus#mCandidateScore} &
1519     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1520     *
1521     * This is invoked by Network Selector at the start of every selection procedure to clear all
1522     * configured networks' scan-result-candidates.
1523     *
1524     * @param networkId network ID corresponding to the network.
1525     * @return true if the network was found, false otherwise.
1526     */
1527    public boolean clearNetworkCandidateScanResult(int networkId) {
1528        if (mVerboseLoggingEnabled) {
1529            Log.v(TAG, "Clear network candidate scan result for " + networkId);
1530        }
1531        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1532        if (config == null) {
1533            return false;
1534        }
1535        config.getNetworkSelectionStatus().setCandidate(null);
1536        config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
1537        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
1538        return true;
1539    }
1540
1541    /**
1542     * Set the {@link NetworkSelectionStatus#mCandidate},
1543     * {@link NetworkSelectionStatus#mCandidateScore} &
1544     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1545     *
1546     * This is invoked by Network Selector when it sees a network during network selection procedure
1547     * to set the scan result candidate.
1548     *
1549     * @param networkId  network ID corresponding to the network.
1550     * @param scanResult Candidate ScanResult associated with this network.
1551     * @param score      Score assigned to the candidate.
1552     * @return true if the network was found, false otherwise.
1553     */
1554    public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) {
1555        if (mVerboseLoggingEnabled) {
1556            Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId);
1557        }
1558        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1559        if (config == null) {
1560            return false;
1561        }
1562        config.getNetworkSelectionStatus().setCandidate(scanResult);
1563        config.getNetworkSelectionStatus().setCandidateScore(score);
1564        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
1565        return true;
1566    }
1567
1568    /**
1569     * Iterate through all the saved networks and remove the provided configuration from the
1570     * {@link NetworkSelectionStatus#mConnectChoice} from them.
1571     *
1572     * This is invoked when a network is removed from our records.
1573     *
1574     * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
1575     */
1576    private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
1577        if (mVerboseLoggingEnabled) {
1578            Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
1579        }
1580        if (connectChoiceConfigKey == null) {
1581            return;
1582        }
1583        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1584            WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1585            String connectChoice = status.getConnectChoice();
1586            if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
1587                Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
1588                        + " : " + config.networkId);
1589                clearNetworkConnectChoice(config.networkId);
1590            }
1591        }
1592    }
1593
1594    /**
1595     * Clear the {@link NetworkSelectionStatus#mConnectChoice} &
1596     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1597     *
1598     * @param networkId network ID corresponding to the network.
1599     * @return true if the network was found, false otherwise.
1600     */
1601    public boolean clearNetworkConnectChoice(int networkId) {
1602        if (mVerboseLoggingEnabled) {
1603            Log.v(TAG, "Clear network connect choice for " + networkId);
1604        }
1605        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1606        if (config == null) {
1607            return false;
1608        }
1609        config.getNetworkSelectionStatus().setConnectChoice(null);
1610        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(
1611                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1612        saveToStore(false);
1613        return true;
1614    }
1615
1616    /**
1617     * Set the {@link NetworkSelectionStatus#mConnectChoice} &
1618     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1619     *
1620     * This is invoked by Network Selector when the user overrides the currently connected network
1621     * choice.
1622     *
1623     * @param networkId              network ID corresponding to the network.
1624     * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
1625     *                               this network.
1626     * @param timestamp              timestamp at which the choice was made.
1627     * @return true if the network was found, false otherwise.
1628     */
1629    public boolean setNetworkConnectChoice(
1630            int networkId, String connectChoiceConfigKey, long timestamp) {
1631        if (mVerboseLoggingEnabled) {
1632            Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
1633        }
1634        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1635        if (config == null) {
1636            return false;
1637        }
1638        config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
1639        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp);
1640        saveToStore(false);
1641        return true;
1642    }
1643
1644    /**
1645     * Increments the number of no internet access reports in the provided network.
1646     *
1647     * @param networkId network ID corresponding to the network.
1648     * @return true if the network was found, false otherwise.
1649     */
1650    public boolean incrementNetworkNoInternetAccessReports(int networkId) {
1651        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1652        if (config == null) {
1653            return false;
1654        }
1655        config.numNoInternetAccessReports++;
1656        return true;
1657    }
1658
1659    /**
1660     * Sets the internet access is validated or not in the provided network.
1661     *
1662     * @param networkId network ID corresponding to the network.
1663     * @param validated Whether access is validated or not.
1664     * @return true if the network was found, false otherwise.
1665     */
1666    public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
1667        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1668        if (config == null) {
1669            return false;
1670        }
1671        config.validatedInternetAccess = validated;
1672        config.numNoInternetAccessReports = 0;
1673        saveToStore(false);
1674        return true;
1675    }
1676
1677    /**
1678     * Sets whether the internet access is expected or not in the provided network.
1679     *
1680     * @param networkId network ID corresponding to the network.
1681     * @param expected  Whether access is expected or not.
1682     * @return true if the network was found, false otherwise.
1683     */
1684    public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
1685        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1686        if (config == null) {
1687            return false;
1688        }
1689        config.noInternetAccessExpected = expected;
1690        return true;
1691    }
1692
1693    /**
1694     * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
1695     * is done when either the corresponding network is either removed or disabled.
1696     */
1697    private void clearLastSelectedNetwork() {
1698        if (mVerboseLoggingEnabled) {
1699            Log.v(TAG, "Clearing last selected network");
1700        }
1701        mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1702        mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
1703    }
1704
1705    /**
1706     * Helper method to mark a network as the last selected one by an app/user. This is set
1707     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
1708     * This is used by network selector to assign a special bonus during network selection.
1709     */
1710    private void setLastSelectedNetwork(int networkId) {
1711        if (mVerboseLoggingEnabled) {
1712            Log.v(TAG, "Setting last selected network to " + networkId);
1713        }
1714        mLastSelectedNetworkId = networkId;
1715        mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
1716    }
1717
1718    /**
1719     * Retrieve the network Id corresponding to the last network that was explicitly selected by
1720     * an app/user.
1721     *
1722     * @return network Id corresponding to the last selected network.
1723     */
1724    public int getLastSelectedNetwork() {
1725        return mLastSelectedNetworkId;
1726    }
1727
1728    /**
1729     * Retrieve the configKey corresponding to the last network that was explicitly selected by
1730     * an app/user.
1731     *
1732     * @return network Id corresponding to the last selected network.
1733     */
1734    public String getLastSelectedNetworkConfigKey() {
1735        if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
1736            return "";
1737        }
1738        WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
1739        if (config == null) {
1740            return "";
1741        }
1742        return config.configKey();
1743    }
1744
1745    /**
1746     * Retrieve the time stamp at which a network was explicitly selected by an app/user.
1747     *
1748     * @return timestamp in milliseconds from boot when this was set.
1749     */
1750    public long getLastSelectedTimeStamp() {
1751        return mLastSelectedTimeStamp;
1752    }
1753
1754    /**
1755     * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
1756     * network.
1757     *
1758     * @param networkId network ID corresponding to the network.
1759     * @return existing {@link ScanDetailCache} entry if one exists or null.
1760     */
1761    public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
1762        return mScanDetailCaches.get(networkId);
1763    }
1764
1765    /**
1766     * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
1767     * the provided network.
1768     *
1769     * @param config configuration corresponding to the the network.
1770     * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
1771     * this network.
1772     */
1773    private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
1774        if (config == null) return null;
1775        ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
1776        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
1777            cache = new ScanDetailCache(
1778                    config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
1779            mScanDetailCaches.put(config.networkId, cache);
1780        }
1781        return cache;
1782    }
1783
1784    /**
1785     * Saves the provided ScanDetail into the corresponding scan detail cache entry
1786     * {@link #mScanDetailCaches} for the provided network.
1787     *
1788     * @param config     configuration corresponding to the the network.
1789     * @param scanDetail new scan detail instance to be saved into the cache.
1790     */
1791    private void saveToScanDetailCacheForNetwork(
1792            WifiConfiguration config, ScanDetail scanDetail) {
1793        ScanResult scanResult = scanDetail.getScanResult();
1794
1795        ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
1796        if (scanDetailCache == null) {
1797            Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
1798            return;
1799        }
1800
1801        // Adding a new BSSID
1802        ScanResult result = scanDetailCache.get(scanResult.BSSID);
1803        if (result != null) {
1804            // transfer the black list status
1805            scanResult.blackListTimestamp = result.blackListTimestamp;
1806            scanResult.numIpConfigFailures = result.numIpConfigFailures;
1807            scanResult.numConnection = result.numConnection;
1808        }
1809        if (config.ephemeral) {
1810            // For an ephemeral Wi-Fi config, the ScanResult should be considered
1811            // untrusted.
1812            scanResult.untrusted = true;
1813        }
1814
1815        // Add the scan detail to this network's scan detail cache.
1816        scanDetailCache.put(scanDetail);
1817
1818        // Since we added a scan result to this configuration, re-attempt linking.
1819        // TODO: Do we really need to do this after every scan result?
1820        attemptNetworkLinking(config);
1821    }
1822
1823    /**
1824     * Retrieves a saved network corresponding to the provided scan detail if one exists.
1825     *
1826     * @param scanDetail ScanDetail instance  to use for looking up the network.
1827     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1828     * null if none exists.
1829     */
1830    private WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) {
1831        ScanResult scanResult = scanDetail.getScanResult();
1832        if (scanResult == null) {
1833            Log.e(TAG, "No scan result found in scan detail");
1834            return null;
1835        }
1836        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
1837            if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) {
1838                if (mVerboseLoggingEnabled) {
1839                    Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
1840                            + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
1841                }
1842                return config;
1843            }
1844        }
1845        return null;
1846    }
1847
1848    /**
1849     * Retrieves a saved network corresponding to the provided scan detail if one exists and caches
1850     * the provided |scanDetail| into the corresponding scan detail cache entry
1851     * {@link #mScanDetailCaches} for the retrieved network.
1852     *
1853     * @param scanDetail input a scanDetail from the scan result
1854     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1855     * null if none exists.
1856     */
1857    public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) {
1858        WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
1859        if (network == null) {
1860            return null;
1861        }
1862        saveToScanDetailCacheForNetwork(network, scanDetail);
1863        // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
1864        // Information Element (IE), into the associated WifiConfigurations. Most of the
1865        // time there is no TIM IE in the scan result (Probe Response instead of Beacon
1866        // Frame), these scanResult DTIM's are negative and ignored.
1867        // Used for metrics collection.
1868        if (scanDetail.getNetworkDetail() != null
1869                && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
1870            network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
1871        }
1872        return createExternalWifiConfiguration(network, true);
1873    }
1874
1875    /**
1876     * Update the scan detail cache associated with current connected network with latest
1877     * RSSI value in the provided WifiInfo.
1878     * This is invoked when we get an RSSI poll update after connection.
1879     *
1880     * @param info WifiInfo instance pointing to the current connected network.
1881     */
1882    public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
1883        WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
1884        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
1885        if (config != null && scanDetailCache != null) {
1886            ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
1887            if (scanDetail != null) {
1888                ScanResult result = scanDetail.getScanResult();
1889                long previousSeen = result.seen;
1890                int previousRssi = result.level;
1891                // Update the scan result
1892                scanDetail.setSeen();
1893                result.level = info.getRssi();
1894                // Average the RSSI value
1895                result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS);
1896                if (mVerboseLoggingEnabled) {
1897                    Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
1898                            + " BSSID=" + result.BSSID
1899                            + " RSSI=" + result.level
1900                            + " for " + config.configKey());
1901                }
1902            }
1903        }
1904    }
1905
1906    /**
1907     * Save the ScanDetail to the ScanDetailCache of the given network.  This is used
1908     * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching
1909     * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
1910     *
1911     * @param networkId The ID of the network to save ScanDetail to
1912     * @param scanDetail The ScanDetail to cache
1913     */
1914    public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
1915        WifiConfiguration network = getInternalConfiguredNetwork(networkId);
1916        if (network == null) {
1917            return;
1918        }
1919        saveToScanDetailCacheForNetwork(network, scanDetail);
1920    }
1921
1922    /**
1923     * Helper method to check if the 2 provided networks can be linked or not.
1924     * Networks are considered for linking if:
1925     * 1. Share the same GW MAC address.
1926     * 2. Scan results for the networks have AP's with MAC address which differ only in the last
1927     * nibble.
1928     *
1929     * @param network1         WifiConfiguration corresponding to network 1.
1930     * @param network2         WifiConfiguration corresponding to network 2.
1931     * @param scanDetailCache1 ScanDetailCache entry for network 1.
1932     * @param scanDetailCache1 ScanDetailCache entry for network 2.
1933     * @return true if the networks should be linked, false if the networks should be unlinked.
1934     */
1935    private boolean shouldNetworksBeLinked(
1936            WifiConfiguration network1, WifiConfiguration network2,
1937            ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
1938        // TODO (b/30706406): Link networks only with same passwords if the
1939        // |mOnlyLinkSameCredentialConfigurations| flag is set.
1940        if (mOnlyLinkSameCredentialConfigurations) {
1941            if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
1942                if (mVerboseLoggingEnabled) {
1943                    Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch");
1944                }
1945                return false;
1946            }
1947        }
1948        if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
1949            // If both default GW are known, link only if they are equal
1950            if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) {
1951                if (mVerboseLoggingEnabled) {
1952                    Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
1953                            + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
1954                }
1955                return true;
1956            }
1957        } else {
1958            // We do not know BOTH default gateways hence we will try to link
1959            // hoping that WifiConfigurations are indeed behind the same gateway.
1960            // once both WifiConfiguration have been tried and thus once both default gateways
1961            // are known we will revisit the choice of linking them.
1962            if (scanDetailCache1 != null && scanDetailCache2 != null) {
1963                for (String abssid : scanDetailCache1.keySet()) {
1964                    for (String bbssid : scanDetailCache2.keySet()) {
1965                        if (abssid.regionMatches(
1966                                true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
1967                            // If first 16 ASCII characters of BSSID matches,
1968                            // we assume this is a DBDC.
1969                            if (mVerboseLoggingEnabled) {
1970                                Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
1971                                        + network2.SSID + " and " + network1.SSID
1972                                        + " bssida " + abssid + " bssidb " + bbssid);
1973                            }
1974                            return true;
1975                        }
1976                    }
1977                }
1978            }
1979        }
1980        return false;
1981    }
1982
1983    /**
1984     * Helper methods to link 2 networks together.
1985     *
1986     * @param network1 WifiConfiguration corresponding to network 1.
1987     * @param network2 WifiConfiguration corresponding to network 2.
1988     */
1989    private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
1990        if (mVerboseLoggingEnabled) {
1991            Log.v(TAG, "linkNetworks will link " + network2.configKey()
1992                    + " and " + network1.configKey());
1993        }
1994        if (network2.linkedConfigurations == null) {
1995            network2.linkedConfigurations = new HashMap<>();
1996        }
1997        if (network1.linkedConfigurations == null) {
1998            network1.linkedConfigurations = new HashMap<>();
1999        }
2000        // TODO (b/30638473): This needs to become a set instead of map, but it will need
2001        // public interface changes and need some migration of existing store data.
2002        network2.linkedConfigurations.put(network1.configKey(), 1);
2003        network1.linkedConfigurations.put(network2.configKey(), 1);
2004    }
2005
2006    /**
2007     * Helper methods to unlink 2 networks from each other.
2008     *
2009     * @param network1 WifiConfiguration corresponding to network 1.
2010     * @param network2 WifiConfiguration corresponding to network 2.
2011     */
2012    private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2013        if (network2.linkedConfigurations != null
2014                && (network2.linkedConfigurations.get(network1.configKey()) != null)) {
2015            if (mVerboseLoggingEnabled) {
2016                Log.v(TAG, "unlinkNetworks un-link " + network1.configKey()
2017                        + " from " + network2.configKey());
2018            }
2019            network2.linkedConfigurations.remove(network1.configKey());
2020        }
2021        if (network1.linkedConfigurations != null
2022                && (network1.linkedConfigurations.get(network2.configKey()) != null)) {
2023            if (mVerboseLoggingEnabled) {
2024                Log.v(TAG, "unlinkNetworks un-link " + network2.configKey()
2025                        + " from " + network1.configKey());
2026            }
2027            network1.linkedConfigurations.remove(network2.configKey());
2028        }
2029    }
2030
2031    /**
2032     * This method runs through all the saved networks and checks if the provided network can be
2033     * linked with any of them.
2034     *
2035     * @param config WifiConfiguration object corresponding to the network that needs to be
2036     *               checked for potential links.
2037     */
2038    private void attemptNetworkLinking(WifiConfiguration config) {
2039        // Only link WPA_PSK config.
2040        if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2041            return;
2042        }
2043        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2044        // Ignore configurations with large number of BSSIDs.
2045        if (scanDetailCache != null
2046                && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2047            return;
2048        }
2049        for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
2050            if (linkConfig.configKey().equals(config.configKey())) {
2051                continue;
2052            }
2053            if (linkConfig.ephemeral) {
2054                continue;
2055            }
2056            // Network Selector will be allowed to dynamically jump from a linked configuration
2057            // to another, hence only link configurations that have WPA_PSK security type.
2058            if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2059                continue;
2060            }
2061            ScanDetailCache linkScanDetailCache =
2062                    getScanDetailCacheForNetwork(linkConfig.networkId);
2063            // Ignore configurations with large number of BSSIDs.
2064            if (linkScanDetailCache != null
2065                    && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2066                continue;
2067            }
2068            // Check if the networks should be linked/unlinked.
2069            if (shouldNetworksBeLinked(
2070                    config, linkConfig, scanDetailCache, linkScanDetailCache)) {
2071                linkNetworks(config, linkConfig);
2072            } else {
2073                unlinkNetworks(config, linkConfig);
2074            }
2075        }
2076    }
2077
2078    /**
2079     * Helper method to fetch list of channels for a network from the associated ScanResult's cache
2080     * and add it to the provided channel as long as the size of the set is less than
2081     * |maxChannelSetSize|.
2082     *
2083     * @param channelSet        Channel set holding all the channels for the network.
2084     * @param scanDetailCache   ScanDetailCache entry associated with the network.
2085     * @param nowInMillis       current timestamp to be used for age comparison.
2086     * @param ageInMillis       only consider scan details whose timestamps are earlier than this
2087     *                          value.
2088     * @param maxChannelSetSize Maximum number of channels to be added to the set.
2089     * @return false if the list is full, true otherwise.
2090     */
2091    private boolean addToChannelSetForNetworkFromScanDetailCache(
2092            Set<Integer> channelSet, ScanDetailCache scanDetailCache,
2093            long nowInMillis, long ageInMillis, int maxChannelSetSize) {
2094        if (scanDetailCache != null && scanDetailCache.size() > 0) {
2095            for (ScanDetail scanDetail : scanDetailCache.values()) {
2096                ScanResult result = scanDetail.getScanResult();
2097                boolean valid = (nowInMillis - result.seen) < ageInMillis;
2098                if (mVerboseLoggingEnabled) {
2099                    Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq "
2100                            + result.frequency + " age " + (nowInMillis - result.seen)
2101                            + " ?=" + valid);
2102                }
2103                if (valid) {
2104                    channelSet.add(result.frequency);
2105                }
2106                if (channelSet.size() >= maxChannelSetSize) {
2107                    return false;
2108                }
2109            }
2110        }
2111        return true;
2112    }
2113
2114    /**
2115     * Retrieve a set of channels on which AP's for the provided network was seen using the
2116     * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial
2117     * scans for the currently connected network.
2118     *
2119     * @param networkId       network ID corresponding to the network.
2120     * @param ageInMillis     only consider scan details whose timestamps are earlier than this value.
2121     * @param homeChannelFreq frequency of the currently connected network.
2122     * @return Set containing the frequencies on which this network was found, null if the network
2123     * was not found or there are no associated scan details in the cache.
2124     */
2125    public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis,
2126                                                                int homeChannelFreq) {
2127        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2128        if (config == null) {
2129            return null;
2130        }
2131        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId);
2132        if (scanDetailCache == null && config.linkedConfigurations == null) {
2133            Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId);
2134            return null;
2135        }
2136        if (mVerboseLoggingEnabled) {
2137            StringBuilder dbg = new StringBuilder();
2138            dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ")
2139                    .append(ageInMillis)
2140                    .append(" for ")
2141                    .append(config.configKey())
2142                    .append(" max ")
2143                    .append(mMaxNumActiveChannelsForPartialScans);
2144            if (scanDetailCache != null) {
2145                dbg.append(" bssids " + scanDetailCache.size());
2146            }
2147            if (config.linkedConfigurations != null) {
2148                dbg.append(" linked " + config.linkedConfigurations.size());
2149            }
2150            Log.v(TAG, dbg.toString());
2151        }
2152        Set<Integer> channelSet = new HashSet<>();
2153
2154        // First add the currently connected network channel.
2155        if (homeChannelFreq > 0) {
2156            channelSet.add(homeChannelFreq);
2157            if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) {
2158                return channelSet;
2159            }
2160        }
2161
2162        long nowInMillis = mClock.getWallClockMillis();
2163
2164        // Then get channels for the network.
2165        if (!addToChannelSetForNetworkFromScanDetailCache(
2166                channelSet, scanDetailCache, nowInMillis, ageInMillis,
2167                mMaxNumActiveChannelsForPartialScans)) {
2168            return channelSet;
2169        }
2170
2171        // Lastly get channels for linked networks.
2172        if (config.linkedConfigurations != null) {
2173            for (String configKey : config.linkedConfigurations.keySet()) {
2174                WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey);
2175                if (linkedConfig == null) {
2176                    continue;
2177                }
2178                ScanDetailCache linkedScanDetailCache =
2179                        getScanDetailCacheForNetwork(linkedConfig.networkId);
2180                if (!addToChannelSetForNetworkFromScanDetailCache(
2181                        channelSet, linkedScanDetailCache, nowInMillis, ageInMillis,
2182                        mMaxNumActiveChannelsForPartialScans)) {
2183                    break;
2184                }
2185            }
2186        }
2187        return channelSet;
2188    }
2189
2190    /**
2191     * Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
2192     *
2193     * PNO network list sent to the firmware has limited size. If there are a lot of saved
2194     * networks, this list will be truncated and we might end up not sending the networks
2195     * with the highest chance of connecting to the firmware.
2196     * So, re-sort the network list based on the frequency of connection to those networks
2197     * and whether it was last seen in the scan results.
2198     *
2199     * TODO (b/30399964): Recalculate the list whenever network status changes.
2200     * @return list of networks with updated priorities.
2201     */
2202    public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
2203        List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
2204        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2205        // Remove any permanently disabled networks.
2206        Iterator<WifiConfiguration> iter = networks.iterator();
2207        while (iter.hasNext()) {
2208            WifiConfiguration config = iter.next();
2209            if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2210                iter.remove();
2211            }
2212        }
2213        Collections.sort(networks, sScanListComparator);
2214        // Let's use the network list size - 1 as the highest priority and then go down from there.
2215        // So, the most frequently connected network has the highest priority now.
2216        int priority = networks.size() - 1;
2217        for (WifiConfiguration config : networks) {
2218            pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority));
2219            priority--;
2220        }
2221        return pnoList;
2222    }
2223
2224    /**
2225     * Retrieves a list of all the saved hidden networks for scans.
2226     *
2227     * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2228     * networks, this list will be truncated and we might end up not sending the networks
2229     * with the highest chance of connecting to the firmware.
2230     * So, re-sort the network list based on the frequency of connection to those networks
2231     * and whether it was last seen in the scan results.
2232     *
2233     * @return list of networks with updated priorities.
2234     */
2235    public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
2236        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2237        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2238        // Remove any permanently disabled networks or non hidden networks.
2239        Iterator<WifiConfiguration> iter = networks.iterator();
2240        while (iter.hasNext()) {
2241            WifiConfiguration config = iter.next();
2242            if (!config.hiddenSSID ||
2243                    config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2244                iter.remove();
2245            }
2246        }
2247        Collections.sort(networks, sScanListComparator);
2248        // Let's use the network list size - 1 as the highest priority and then go down from there.
2249        // So, the most frequently connected network has the highest priority now.
2250        int priority = networks.size() - 1;
2251        for (WifiConfiguration config : networks) {
2252            hiddenList.add(
2253                    new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
2254            priority--;
2255        }
2256        return hiddenList;
2257    }
2258
2259    /**
2260     * Check if the provided ephemeral network was deleted by the user or not.
2261     *
2262     * @param ssid caller must ensure that the SSID passed thru this API match
2263     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2264     * @return true if network was deleted, false otherwise.
2265     */
2266    public boolean wasEphemeralNetworkDeleted(String ssid) {
2267        return mDeletedEphemeralSSIDs.contains(ssid);
2268    }
2269
2270    /**
2271     * Disable an ephemeral SSID for the purpose of network selection.
2272     *
2273     * The only way to "un-disable it" is if the user create a network for that SSID and then
2274     * forget it.
2275     *
2276     * @param ssid caller must ensure that the SSID passed thru this API match
2277     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2278     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
2279     * disconnect if this is the current network.
2280     */
2281    public WifiConfiguration disableEphemeralNetwork(String ssid) {
2282        if (ssid == null) {
2283            return null;
2284        }
2285        WifiConfiguration foundConfig = null;
2286        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2287            if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) {
2288                foundConfig = config;
2289                break;
2290            }
2291        }
2292        mDeletedEphemeralSSIDs.add(ssid);
2293        Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
2294        if (foundConfig != null) {
2295            Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: "
2296                    + foundConfig.networkId);
2297        }
2298        return foundConfig;
2299    }
2300
2301    /**
2302     * Resets all sim networks state.
2303     */
2304    public void resetSimNetworks() {
2305        if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
2306        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2307            if (TelephonyUtil.isSimConfig(config)) {
2308                String currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config);
2309                // Update the loaded config
2310                config.enterpriseConfig.setIdentity(currentIdentity);
2311                config.enterpriseConfig.setAnonymousIdentity("");
2312            }
2313        }
2314    }
2315
2316    /**
2317     * Any network using certificates to authenticate access requires unlocked key store; unless
2318     * the certificates can be stored with hardware encryption
2319     *
2320     * @return true if we need an unlocked keystore, false otherwise.
2321     */
2322    public boolean needsUnlockedKeyStore() {
2323        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2324            if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
2325                if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
2326                    return true;
2327                }
2328            }
2329        }
2330        return false;
2331    }
2332
2333    /**
2334     * Helper method to perform the following operations during user switch/unlock:
2335     * - Remove private networks of the old user.
2336     * - Load from the new user store file.
2337     * - Save the store files again to migrate any user specific networks from the shared store
2338     *   to user store.
2339     * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
2340     * should ensure that the stores are accessible before invocation.
2341     *
2342     * @param userId The identifier of the new foreground user, after the unlock or switch.
2343     */
2344    private void handleUserUnlockOrSwitch(int userId) {
2345        if (mVerboseLoggingEnabled) {
2346            Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
2347        }
2348        // Switch out the user store file.
2349        if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
2350            saveToStore(true);
2351            mPendingUnlockStoreRead = false;
2352        }
2353    }
2354
2355    /**
2356     * Handles the switch to a different foreground user:
2357     * - Flush the current state to the old user's store file.
2358     * - Switch the user specific store file.
2359     * - Reload the networks from the store files (shared & user).
2360     * - Write the store files to move any user specific private networks from shared store to user
2361     *   store.
2362     *
2363     * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked.
2364     *
2365     * @param userId The identifier of the new foreground user, after the switch.
2366     * @return List of network ID's of all the private networks of the old user which will be
2367     * removed from memory.
2368     */
2369    public Set<Integer> handleUserSwitch(int userId) {
2370        if (mVerboseLoggingEnabled) {
2371            Log.v(TAG, "Handling user switch for " + userId);
2372        }
2373        if (userId == mCurrentUserId) {
2374            Log.w(TAG, "User already in foreground " + userId);
2375            return new HashSet<>();
2376        }
2377        if (mPendingStoreRead) {
2378            Log.wtf(TAG, "Unexpected user switch before store is read!");
2379            return new HashSet<>();
2380        }
2381        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2382            saveToStore(true);
2383        }
2384        // Remove any private networks of the old user before switching the userId.
2385        Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId);
2386        mConfiguredNetworks.setNewUser(userId);
2387        mCurrentUserId = userId;
2388
2389        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2390            handleUserUnlockOrSwitch(mCurrentUserId);
2391        } else {
2392            // Cannot read data from new user's CE store file before they log-in.
2393            mPendingUnlockStoreRead = true;
2394            Log.i(TAG, "Waiting for user unlock to load from store");
2395        }
2396        return removedNetworkIds;
2397    }
2398
2399    /**
2400     * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
2401     * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
2402     *
2403     * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked.
2404     *
2405     * @param userId The identifier of the user that unlocked.
2406     */
2407    public void handleUserUnlock(int userId) {
2408        if (mVerboseLoggingEnabled) {
2409            Log.v(TAG, "Handling user unlock for " + userId);
2410        }
2411        if (mPendingStoreRead) {
2412            Log.w(TAG, "Ignore user unlock until store is read!");
2413            mDeferredUserUnlockRead = true;
2414            return;
2415        }
2416        if (userId == mCurrentUserId && mPendingUnlockStoreRead) {
2417            handleUserUnlockOrSwitch(mCurrentUserId);
2418        }
2419    }
2420
2421    /**
2422     * Handles the stop of foreground user. This is needed to write the store file to flush
2423     * out any pending data before the user's CE store storage is unavailable.
2424     *
2425     * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked.
2426     *
2427     * @param userId The identifier of the user that stopped.
2428     */
2429    public void handleUserStop(int userId) {
2430        if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2431            saveToStore(true);
2432            clearInternalData();
2433            mCurrentUserId = UserHandle.USER_SYSTEM;
2434        }
2435    }
2436
2437    /**
2438     * Helper method to clear internal databases.
2439     * This method clears the:
2440     *  - List of configured networks.
2441     *  - Map of scan detail caches.
2442     *  - List of deleted ephemeral networks.
2443     */
2444    private void clearInternalData() {
2445        mConfiguredNetworks.clear();
2446        mDeletedEphemeralSSIDs.clear();
2447        mScanDetailCaches.clear();
2448        clearLastSelectedNetwork();
2449    }
2450
2451    /**
2452     * Helper method to clear internal databases of the specified user.
2453     * This method clears the:
2454     *  - Private configured configured networks of the specified user.
2455     *  - Map of scan detail caches.
2456     *  - List of deleted ephemeral networks.
2457     *
2458     * @param userId The identifier of the current foreground user, before the switch.
2459     * @return List of network ID's of all the private networks of the old user which will be
2460     * removed from memory.
2461     */
2462    private Set<Integer> clearInternalUserData(int userId) {
2463        Set<Integer> removedNetworkIds = new HashSet<>();
2464        // Remove any private networks of the old user before switching the userId.
2465        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2466            if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2467                    config.creatorUid, mUserManager.getProfiles(userId))) {
2468                removedNetworkIds.add(config.networkId);
2469                mConfiguredNetworks.remove(config.networkId);
2470            }
2471        }
2472        mDeletedEphemeralSSIDs.clear();
2473        mScanDetailCaches.clear();
2474        clearLastSelectedNetwork();
2475        return removedNetworkIds;
2476    }
2477
2478    /**
2479     * Helper function to populate the internal (in-memory) data from the retrieved shared store
2480     * (file) data.
2481     *
2482     * @param configurations list of configurations retrieved from store.
2483     */
2484    private void loadInternalDataFromSharedStore(
2485            List<WifiConfiguration> configurations) {
2486        for (WifiConfiguration configuration : configurations) {
2487            configuration.networkId = mNextNetworkId++;
2488            if (mVerboseLoggingEnabled) {
2489                Log.v(TAG, "Adding network from shared store " + configuration.configKey());
2490            }
2491            mConfiguredNetworks.put(configuration);
2492        }
2493    }
2494
2495    /**
2496     * Helper function to populate the internal (in-memory) data from the retrieved user store
2497     * (file) data.
2498     *
2499     * @param configurations        list of configurations retrieved from store.
2500     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2501     *                              the user.
2502     */
2503    private void loadInternalDataFromUserStore(
2504            List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
2505        for (WifiConfiguration configuration : configurations) {
2506            configuration.networkId = mNextNetworkId++;
2507            if (mVerboseLoggingEnabled) {
2508                Log.v(TAG, "Adding network from user store " + configuration.configKey());
2509            }
2510            mConfiguredNetworks.put(configuration);
2511        }
2512        for (String ssid : deletedEphemeralSSIDs) {
2513            mDeletedEphemeralSSIDs.add(ssid);
2514        }
2515    }
2516
2517    /**
2518     * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
2519     * data.
2520     * This method:
2521     * 1. Clears all existing internal data.
2522     * 2. Sends out the networks changed broadcast after loading all the data.
2523     *
2524     * @param sharedConfigurations  list of  network configurations retrieved from shared store.
2525     * @param userConfigurations    list of  network configurations retrieved from user store.
2526     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2527     *                              the user.
2528     */
2529    private void loadInternalData(
2530            List<WifiConfiguration> sharedConfigurations,
2531            List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) {
2532        // Clear out all the existing in-memory lists and load the lists from what was retrieved
2533        // from the config store.
2534        clearInternalData();
2535        loadInternalDataFromSharedStore(sharedConfigurations);
2536        loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs);
2537        if (mConfiguredNetworks.sizeForAllUsers() == 0) {
2538            Log.w(TAG, "No stored networks found.");
2539        }
2540        sendConfiguredNetworksChangedBroadcast();
2541        mPendingStoreRead = false;
2542    }
2543
2544    /**
2545     * Migrate data from legacy store files. The function performs the following operations:
2546     * 1. Check if the legacy store files are present.
2547     * 2. If yes, read all the data from the store files.
2548     * 3. Save it to the new store files.
2549     * 4. Delete the legacy store file.
2550     *
2551     * @return true if migration was successful or not needed (fresh install), false if it failed.
2552     */
2553    public boolean migrateFromLegacyStore() {
2554        if (!mWifiConfigStoreLegacy.areStoresPresent()) {
2555            Log.d(TAG, "Legacy store files not found. No migration needed!");
2556            return true;
2557        }
2558        WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read();
2559        Log.d(TAG, "Reading from legacy store completed");
2560        loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(),
2561                storeData.getDeletedEphemeralSSIDs());
2562
2563        // Setup user store for the current user in case it have not setup yet, so that data
2564        // owned by the current user will be backed to the user store.
2565        if (mDeferredUserUnlockRead) {
2566            mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
2567            mDeferredUserUnlockRead = false;
2568        }
2569
2570        if (!saveToStore(true)) {
2571            return false;
2572        }
2573        mWifiConfigStoreLegacy.removeStores();
2574        Log.d(TAG, "Migration from legacy store completed");
2575        return true;
2576    }
2577
2578    /**
2579     * Read the config store and load the in-memory lists from the store data retrieved and sends
2580     * out the networks changed broadcast.
2581     *
2582     * This reads all the network configurations from:
2583     * 1. Shared WifiConfigStore.xml
2584     * 2. User WifiConfigStore.xml
2585     *
2586     * @return true on success or not needed (fresh install/pending legacy store migration),
2587     * false otherwise.
2588     */
2589    public boolean loadFromStore() {
2590        if (!mWifiConfigStore.areStoresPresent()) {
2591            Log.d(TAG, "New store files not found. No saved networks loaded!");
2592            if (!mWifiConfigStoreLegacy.areStoresPresent()) {
2593                // No legacy store files either, so reset the pending store read flag.
2594                mPendingStoreRead = false;
2595            }
2596            return true;
2597        }
2598        // If the user unlock comes in before we load from store, which means the user store have
2599        // not been setup yet for the current user.  Setup the user store before the read so that
2600        // configurations for the current user will also being loaded.
2601        if (mDeferredUserUnlockRead) {
2602            Log.i(TAG, "Handling user unlock before loading from store.");
2603            mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
2604            mDeferredUserUnlockRead = false;
2605        }
2606        try {
2607            mWifiConfigStore.read();
2608        } catch (IOException e) {
2609            Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
2610            return false;
2611        } catch (XmlPullParserException e) {
2612            Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
2613            return false;
2614        }
2615        loadInternalData(mNetworkListStoreData.getSharedConfigurations(),
2616                mNetworkListStoreData.getUserConfigurations(),
2617                mDeletedEphemeralSsidsStoreData.getSsidList());
2618        return true;
2619    }
2620
2621    /**
2622     * Read the user config store and load the in-memory lists from the store data retrieved and
2623     * sends out the networks changed broadcast.
2624     * This should be used for all user switches/unlocks to only load networks from the user
2625     * specific store and avoid reloading the shared networks.
2626     *
2627     * This reads all the network configurations from:
2628     * 1. User WifiConfigStore.xml
2629     *
2630     * @param userId The identifier of the foreground user.
2631     * @return true on success, false otherwise.
2632     */
2633    public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
2634        try {
2635            mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId));
2636        } catch (IOException e) {
2637            Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
2638            return false;
2639        } catch (XmlPullParserException e) {
2640            Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" +
2641                    "lost!", e);
2642            return false;
2643        }
2644        loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(),
2645                mDeletedEphemeralSsidsStoreData.getSsidList());
2646        return true;
2647    }
2648
2649    /**
2650     * Save the current snapshot of the in-memory lists to the config store.
2651     *
2652     * @param forceWrite Whether the write needs to be forced or not.
2653     * @return Whether the write was successful or not, this is applicable only for force writes.
2654     */
2655    public boolean saveToStore(boolean forceWrite) {
2656        ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
2657        ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
2658        // List of network IDs for legacy Passpoint configuration to be removed.
2659        List<Integer> legacyPasspointNetId = new ArrayList<>();
2660        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
2661            // Ignore ephemeral networks and non-legacy Passpoint configurations.
2662            if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
2663                continue;
2664            }
2665
2666            // Migrate the legacy Passpoint configurations owned by the current user to
2667            // {@link PasspointManager}.
2668            if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2669                        config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2670                legacyPasspointNetId.add(config.networkId);
2671                // Migrate the legacy Passpoint configuration and add it to PasspointManager.
2672                if (!PasspointManager.addLegacyPasspointConfig(config)) {
2673                    Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
2674                }
2675                // This will prevent adding |config| to the |sharedConfigurations|.
2676                continue;
2677            }
2678
2679            // We push all shared networks & private networks not belonging to the current
2680            // user to the shared store. Ideally, private networks for other users should
2681            // not even be in memory,
2682            // But, this logic is in place to deal with store migration from N to O
2683            // because all networks were previously stored in a central file. We cannot
2684            // write these private networks to the user specific store until the corresponding
2685            // user logs in.
2686            if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile(
2687                    config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2688                sharedConfigurations.add(config);
2689            } else {
2690                userConfigurations.add(config);
2691            }
2692        }
2693
2694        // Remove the configurations for migrated Passpoint configurations.
2695        for (int networkId : legacyPasspointNetId) {
2696            mConfiguredNetworks.remove(networkId);
2697        }
2698
2699        // Setup store data for write.
2700        mNetworkListStoreData.setSharedConfigurations(sharedConfigurations);
2701        mNetworkListStoreData.setUserConfigurations(userConfigurations);
2702        mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs);
2703
2704        try {
2705            mWifiConfigStore.write(forceWrite);
2706        } catch (IOException e) {
2707            Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
2708            return false;
2709        } catch (XmlPullParserException e) {
2710            Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
2711            return false;
2712        }
2713        return true;
2714    }
2715
2716    /**
2717     * Helper method for logging into local log buffer.
2718     */
2719    private void localLog(String s) {
2720        if (mLocalLog != null) {
2721            mLocalLog.log(s);
2722        }
2723    }
2724
2725    /**
2726     * Dump the local log buffer and other internal state of WifiConfigManager.
2727     */
2728    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2729        pw.println("Dump of WifiConfigManager");
2730        pw.println("WifiConfigManager - Log Begin ----");
2731        mLocalLog.dump(fd, pw, args);
2732        pw.println("WifiConfigManager - Log End ----");
2733        pw.println("WifiConfigManager - Configured networks Begin ----");
2734        for (WifiConfiguration network : getInternalConfiguredNetworks()) {
2735            pw.println(network);
2736        }
2737        pw.println("WifiConfigManager - Configured networks End ----");
2738        pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
2739        pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
2740    }
2741
2742    /**
2743     * Returns true if the given uid has permission to add, update or remove proxy settings
2744     */
2745    private boolean canModifyProxySettings(int uid) {
2746        final DevicePolicyManagerInternal dpmi =
2747                mWifiPermissionsWrapper.getDevicePolicyManagerInternal();
2748        final boolean isUidProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
2749                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
2750        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
2751                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
2752        final boolean hasConfigOverridePermission =
2753                mWifiPermissionsUtil.checkConfigOverridePermission(uid);
2754        // If |uid| corresponds to the device owner, allow all modifications.
2755        if (isUidDeviceOwner || isUidProfileOwner || hasConfigOverridePermission) {
2756            return true;
2757        }
2758        if (mVerboseLoggingEnabled) {
2759            Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
2760                    + " ConfigOverride=" + hasConfigOverridePermission
2761                    + " DeviceOwner=" + isUidDeviceOwner
2762                    + " ProfileOwner=" + isUidProfileOwner);
2763        }
2764        return false;
2765    }
2766}
2767