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