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