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