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