• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.ActivityManager;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ApplicationInfo;
27 import android.net.IpConfiguration;
28 import android.net.MacAddress;
29 import android.net.ProxyInfo;
30 import android.net.StaticIpConfiguration;
31 import android.net.wifi.ScanResult;
32 import android.net.wifi.SecurityParams;
33 import android.net.wifi.WifiConfiguration;
34 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
35 import android.net.wifi.WifiEnterpriseConfig;
36 import android.net.wifi.WifiInfo;
37 import android.net.wifi.WifiManager;
38 import android.net.wifi.WifiScanner;
39 import android.os.Process;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.provider.Settings;
43 import android.telephony.SubscriptionManager;
44 import android.telephony.TelephonyManager;
45 import android.text.TextUtils;
46 import android.util.ArraySet;
47 import android.util.LocalLog;
48 import android.util.Log;
49 import android.util.Pair;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.net.module.util.MacAddressUtils;
53 import com.android.server.wifi.hotspot2.PasspointManager;
54 import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent;
55 import com.android.server.wifi.util.LruConnectionTracker;
56 import com.android.server.wifi.util.MissingCounterTimerLockList;
57 import com.android.server.wifi.util.WifiPermissionsUtil;
58 import com.android.wifi.resources.R;
59 
60 import org.xmlpull.v1.XmlPullParserException;
61 
62 import java.io.FileDescriptor;
63 import java.io.IOException;
64 import java.io.PrintWriter;
65 import java.util.ArrayList;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.Comparator;
69 import java.util.HashMap;
70 import java.util.HashSet;
71 import java.util.List;
72 import java.util.Map;
73 import java.util.Set;
74 import java.util.stream.Collectors;
75 
76 /**
77  * This class provides the APIs to manage configured Wi-Fi networks.
78  * It deals with the following:
79  * - Maintaining a list of configured networks for quick access.
80  * - Persisting the configurations to store when required.
81  * - Supporting WifiManager Public API calls:
82  *   > addOrUpdateNetwork()
83  *   > removeNetwork()
84  *   > enableNetwork()
85  *   > disableNetwork()
86  * - Handle user switching on multi-user devices.
87  *
88  * All network configurations retrieved from this class are copies of the original configuration
89  * stored in the internal database. So, any updates to the retrieved configuration object are
90  * meaningless and will not be reflected in the original database.
91  * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored
92  * in the internal database. Any configuration updates should be triggered with appropriate helper
93  * methods of this class using the configuration's unique networkId.
94  *
95  * NOTE: These API's are not thread safe and should only be used from the main Wifi thread.
96  */
97 public class WifiConfigManager {
98     /**
99      * String used to mask passwords to public interface.
100      */
101     @VisibleForTesting
102     public static final String PASSWORD_MASK = "*";
103 
104     /**
105      * Interface for other modules to listen to the network updated events.
106      * Note: Credentials are masked to avoid accidentally sending credentials outside the stack.
107      * Use WifiConfigManager#getConfiguredNetworkWithPassword() to retrieve credentials.
108      */
109     public interface OnNetworkUpdateListener {
110         /**
111          * Invoked on network being added.
112          */
onNetworkAdded(@onNull WifiConfiguration config)113         default void onNetworkAdded(@NonNull WifiConfiguration config) { };
114         /**
115          * Invoked on network being enabled.
116          */
onNetworkEnabled(@onNull WifiConfiguration config)117         default void onNetworkEnabled(@NonNull WifiConfiguration config) { };
118         /**
119          * Invoked on network being permanently disabled.
120          */
onNetworkPermanentlyDisabled(@onNull WifiConfiguration config, int disableReason)121         default void onNetworkPermanentlyDisabled(@NonNull WifiConfiguration config,
122                 int disableReason) { };
123         /**
124          * Invoked on network being removed.
125          */
onNetworkRemoved(@onNull WifiConfiguration config)126         default void onNetworkRemoved(@NonNull WifiConfiguration config) { };
127         /**
128          * Invoked on network being temporarily disabled.
129          */
onNetworkTemporarilyDisabled(@onNull WifiConfiguration config, int disableReason)130         default void onNetworkTemporarilyDisabled(@NonNull WifiConfiguration config,
131                 int disableReason) { };
132         /**
133          * Invoked on network being updated.
134          *
135          * @param newConfig Updated WifiConfiguration object.
136          * @param oldConfig Prev WifiConfiguration object.
137          */
onNetworkUpdated( @onNull WifiConfiguration newConfig, @NonNull WifiConfiguration oldConfig)138         default void onNetworkUpdated(
139                 @NonNull WifiConfiguration newConfig, @NonNull WifiConfiguration oldConfig) { };
140 
141         /**
142          * Invoked when user connect choice is set.
143          * @param networks List of network profiles to set user connect choice.
144          * @param choiceKey Network key {@link WifiConfiguration#getProfileKey()}
145          *                  corresponding to the network which the user chose.
146          * @param rssi the signal strength of the user selected network
147          */
onConnectChoiceSet(@onNull List<WifiConfiguration> networks, String choiceKey, int rssi)148         default void onConnectChoiceSet(@NonNull List<WifiConfiguration> networks,
149                 String choiceKey, int rssi) { }
150 
151         /**
152          * Invoked when user connect choice is removed.
153          * @param choiceKey The network profile key of the user connect choice that was removed.
154          */
onConnectChoiceRemoved(String choiceKey)155         default void onConnectChoiceRemoved(String choiceKey){ }
156     }
157     /**
158      * Max size of scan details to cache in {@link #mScanDetailCaches}.
159      */
160     @VisibleForTesting
161     public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192;
162     /**
163      * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds
164      * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some
165      * buffer time before the next eviction.
166      */
167     @VisibleForTesting
168     public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128;
169     /**
170      * Link networks only if they have less than this number of scan cache entries.
171      */
172     @VisibleForTesting
173     public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6;
174     /**
175      * Link networks only if the bssid in scan results for the networks match in the first
176      * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7"
177      */
178     @VisibleForTesting
179     public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16;
180     /**
181      * Log tag for this class.
182      */
183     private static final String TAG = "WifiConfigManager";
184     /**
185      * Maximum age of scan results that can be used for averaging out RSSI value.
186      */
187     private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
188 
189     /**
190      * Enforce a minimum time to wait after the last disconnect to generate a new randomized MAC,
191      * since IPv6 networks don't provide the DHCP lease duration.
192      * 4 hours.
193      */
194     @VisibleForTesting
195     protected static final long ENHANCED_MAC_WAIT_AFTER_DISCONNECT_MS = 4 * 60 * 60 * 1000;
196     @VisibleForTesting
197     protected static final long ENHANCED_MAC_REFRESH_MS_MIN = 30 * 60 * 1000; // 30 minutes
198     @VisibleForTesting
199     protected static final long ENHANCED_MAC_REFRESH_MS_MAX = 24 * 60 * 60 * 1000; // 24 hours
200 
201     private static final MacAddress DEFAULT_MAC_ADDRESS =
202             MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
203 
204     private static final String VRRP_MAC_ADDRESS_PREFIX = "00:00:5E:00:01";
205 
206     /**
207      * Expiration timeout for user disconnect network. (1 hour)
208      */
209     @VisibleForTesting
210     public static final long USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS = (long) 1000 * 60 * 60;
211 
212     @VisibleForTesting
213     public static final int SCAN_RESULT_MISSING_COUNT_THRESHOLD = 1;
214     @VisibleForTesting
215     protected static final String ENHANCED_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG =
216             "enhanced_mac_randomization_force_enabled";
217     private static final int NON_CARRIER_MERGED_NETWORKS_SCAN_CACHE_QUERY_DURATION_MS =
218             10 * 60 * 1000; // 10 minutes
219 
220     /**
221      * General sorting algorithm of all networks for scanning purposes:
222      * Place the configurations in ascending order of their AgeIndex. AgeIndex is based on most
223      * recently connected order. The lower the more recently connected.
224      * If networks have the same AgeIndex, place the configurations with
225      * |lastSeenInQualifiedNetworkSelection| set first.
226      */
227     private final WifiConfigurationUtil.WifiConfigurationComparator mScanListComparator =
228             new WifiConfigurationUtil.WifiConfigurationComparator() {
229                 @Override
230                 public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
231                     int indexA = mLruConnectionTracker.getAgeIndexOfNetwork(a);
232                     int indexB = mLruConnectionTracker.getAgeIndexOfNetwork(b);
233                     if (indexA != indexB) {
234                         return Integer.compare(indexA, indexB);
235                     } else {
236                         boolean isConfigALastSeen =
237                                 a.getNetworkSelectionStatus()
238                                         .getSeenInLastQualifiedNetworkSelection();
239                         boolean isConfigBLastSeen =
240                                 b.getNetworkSelectionStatus()
241                                         .getSeenInLastQualifiedNetworkSelection();
242                         return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
243                     }
244                 }
245             };
246 
247     /**
248      * List of external dependencies for WifiConfigManager.
249      */
250     private final Context mContext;
251     private final Clock mClock;
252     private final UserManager mUserManager;
253     private final BackupManagerProxy mBackupManagerProxy;
254     private final WifiKeyStore mWifiKeyStore;
255     private final WifiConfigStore mWifiConfigStore;
256     private final WifiPermissionsUtil mWifiPermissionsUtil;
257     private final MacAddressUtil mMacAddressUtil;
258     private final WifiMetrics mWifiMetrics;
259     private final WifiBlocklistMonitor mWifiBlocklistMonitor;
260     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
261     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
262     private final WifiScoreCard mWifiScoreCard;
263     // Keep order of network connection.
264     private final LruConnectionTracker mLruConnectionTracker;
265     private final BuildProperties mBuildProperties;
266 
267     /**
268      * Local log used for debugging any WifiConfigManager issues.
269      */
270     private final LocalLog mLocalLog;
271     /**
272      * Map of configured networks with network id as the key.
273      */
274     private final ConfigurationMap mConfiguredNetworks;
275     /**
276      * Stores a map of NetworkId to ScanDetailCache.
277      */
278     private final Map<Integer, ScanDetailCache> mScanDetailCaches;
279     /**
280      * Framework keeps a list of networks that where temporarily disabled by user,
281      * framework knows not to autoconnect again even if the app/scorer recommends it.
282      * Network will be based on FQDN for passpoint and SSID for non-passpoint.
283      * List will be deleted when Wifi turn off, device restart or network settings reset.
284      * Also when user manfully select to connect network will unblock that network.
285      */
286     private final MissingCounterTimerLockList<String> mUserTemporarilyDisabledList;
287     private final NonCarrierMergedNetworksStatusTracker mNonCarrierMergedNetworksStatusTracker;
288 
289 
290     /**
291      * Framework keeps a mapping from configKey to the randomized MAC address so that
292      * when a user forgets a network and thne adds it back, the same randomized MAC address
293      * will get used.
294      */
295     private final Map<String, String> mRandomizedMacAddressMapping;
296 
297     /**
298      * Store the network update listeners.
299      */
300     private final Set<OnNetworkUpdateListener> mListeners;
301 
302     private final FrameworkFacade mFrameworkFacade;
303     private final DeviceConfigFacade mDeviceConfigFacade;
304 
305     /**
306      * Verbose logging flag. Toggled by developer options.
307      */
308     private boolean mVerboseLoggingEnabled = false;
309     /**
310      * Current logged in user ID.
311      */
312     private int mCurrentUserId = UserHandle.SYSTEM.getIdentifier();
313     /**
314      * Flag to indicate that the new user's store has not yet been read since user switch.
315      * Initialize this flag to |true| to trigger a read on the first user unlock after
316      * bootup.
317      */
318     private boolean mPendingUnlockStoreRead = true;
319     /**
320      * Flag to indicate if we have performed a read from store at all. This is used to gate
321      * any user unlock/switch operations until we read the store (Will happen if wifi is disabled
322      * when user updates from N to O).
323      */
324     private boolean mPendingStoreRead = true;
325     /**
326      * Flag to indicate if the user unlock was deferred until the store load occurs.
327      */
328     private boolean mDeferredUserUnlockRead = false;
329     /**
330      * This is keeping track of the next network ID to be assigned. Any new networks will be
331      * assigned |mNextNetworkId| as network ID.
332      */
333     private int mNextNetworkId = 0;
334     /**
335      * This is used to remember which network was selected successfully last by an app. This is set
336      * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
337      * This is the only way for an app to request connection to a specific network using the
338      * {@link WifiManager} API's.
339      */
340     private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
341     private long mLastSelectedTimeStamp =
342             WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
343 
344     // Store data for network list and deleted ephemeral SSID list.  Used for serializing
345     // parsing data to/from the config store.
346     private final NetworkListSharedStoreData mNetworkListSharedStoreData;
347     private final NetworkListUserStoreData mNetworkListUserStoreData;
348     private final RandomizedMacStoreData mRandomizedMacStoreData;
349 
350     /**
351      * Create new instance of WifiConfigManager.
352      */
WifiConfigManager( Context context, Clock clock, UserManager userManager, WifiCarrierInfoManager wifiCarrierInfoManager, WifiKeyStore wifiKeyStore, WifiConfigStore wifiConfigStore, WifiPermissionsUtil wifiPermissionsUtil, MacAddressUtil macAddressUtil, WifiMetrics wifiMetrics, WifiBlocklistMonitor wifiBlocklistMonitor, WifiLastResortWatchdog wifiLastResortWatchdog, NetworkListSharedStoreData networkListSharedStoreData, NetworkListUserStoreData networkListUserStoreData, RandomizedMacStoreData randomizedMacStoreData, FrameworkFacade frameworkFacade, DeviceConfigFacade deviceConfigFacade, WifiScoreCard wifiScoreCard, LruConnectionTracker lruConnectionTracker, BuildProperties buildProperties)353     WifiConfigManager(
354             Context context,
355             Clock clock,
356             UserManager userManager,
357             WifiCarrierInfoManager wifiCarrierInfoManager,
358             WifiKeyStore wifiKeyStore,
359             WifiConfigStore wifiConfigStore,
360             WifiPermissionsUtil wifiPermissionsUtil,
361             MacAddressUtil macAddressUtil,
362             WifiMetrics wifiMetrics,
363             WifiBlocklistMonitor wifiBlocklistMonitor,
364             WifiLastResortWatchdog wifiLastResortWatchdog,
365             NetworkListSharedStoreData networkListSharedStoreData,
366             NetworkListUserStoreData networkListUserStoreData,
367             RandomizedMacStoreData randomizedMacStoreData,
368             FrameworkFacade frameworkFacade,
369             DeviceConfigFacade deviceConfigFacade,
370             WifiScoreCard wifiScoreCard,
371             LruConnectionTracker lruConnectionTracker,
372             BuildProperties buildProperties) {
373         mContext = context;
374         mClock = clock;
375         mUserManager = userManager;
376         mBackupManagerProxy = new BackupManagerProxy();
377         mWifiCarrierInfoManager = wifiCarrierInfoManager;
378         mWifiKeyStore = wifiKeyStore;
379         mWifiConfigStore = wifiConfigStore;
380         mWifiPermissionsUtil = wifiPermissionsUtil;
381         mWifiMetrics = wifiMetrics;
382         mWifiBlocklistMonitor = wifiBlocklistMonitor;
383         mWifiLastResortWatchdog = wifiLastResortWatchdog;
384         mWifiScoreCard = wifiScoreCard;
385 
386         mConfiguredNetworks = new ConfigurationMap(userManager);
387         mScanDetailCaches = new HashMap<>(16, 0.75f);
388         mUserTemporarilyDisabledList =
389                 new MissingCounterTimerLockList<>(SCAN_RESULT_MISSING_COUNT_THRESHOLD, mClock);
390         mNonCarrierMergedNetworksStatusTracker = new NonCarrierMergedNetworksStatusTracker(mClock);
391         mRandomizedMacAddressMapping = new HashMap<>();
392         mListeners = new ArraySet<>();
393 
394         // Register store data for network list and deleted ephemeral SSIDs.
395         mNetworkListSharedStoreData = networkListSharedStoreData;
396         mNetworkListUserStoreData = networkListUserStoreData;
397         mRandomizedMacStoreData = randomizedMacStoreData;
398         mWifiConfigStore.registerStoreData(mNetworkListSharedStoreData);
399         mWifiConfigStore.registerStoreData(mNetworkListUserStoreData);
400         mWifiConfigStore.registerStoreData(mRandomizedMacStoreData);
401 
402         mFrameworkFacade = frameworkFacade;
403         mDeviceConfigFacade = deviceConfigFacade;
404 
405         mLocalLog = new LocalLog(
406                 context.getSystemService(ActivityManager.class).isLowRamDevice() ? 128 : 256);
407         mMacAddressUtil = macAddressUtil;
408         mLruConnectionTracker = lruConnectionTracker;
409         mBuildProperties = buildProperties;
410     }
411 
412     /**
413      * Update the cellular data availability of the default data SIM.
414      */
onCellularConnectivityChanged(@ifiDataStall.CellularDataStatusCode int status)415     public void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status) {
416         localLog("onCellularConnectivityChanged:" + status);
417         if (status == WifiDataStall.CELLULAR_DATA_NOT_AVAILABLE) {
418             stopRestrictingAutoJoinToSubscriptionId();
419         }
420     }
421 
422     /**
423      * Determine if the framework should perform enhanced MAC randomization when connecting
424      * to the SSID or FQDN in the input WifiConfiguration.
425      * @param config
426      * @return
427      */
shouldUseEnhancedRandomization(WifiConfiguration config)428     public boolean shouldUseEnhancedRandomization(WifiConfiguration config) {
429         if (!isMacRandomizationSupported()
430                 || config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE) {
431             return false;
432         }
433 
434         // Use enhanced randomization if it's forced on by dev option
435         if (mFrameworkFacade.getIntegerSetting(mContext,
436                 ENHANCED_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG, 0) == 1) {
437             return true;
438         }
439 
440         // use enhanced or persistent randomization if configured to do so.
441         if (config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NON_PERSISTENT) {
442             return true;
443         }
444         if (config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
445             return false;
446         }
447 
448         // otherwise the wifi frameworks should decide automatically
449         if (config.getIpConfiguration().getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
450             return false;
451         }
452         if (config.isOpenNetwork() && shouldEnableEnhancedRandomizationOnOpenNetwork(config)) {
453             return true;
454         }
455         if (config.isPasspoint()) {
456             return isNetworkOptInForEnhancedRandomization(config.FQDN);
457         } else {
458             return isNetworkOptInForEnhancedRandomization(config.SSID);
459         }
460     }
461 
shouldEnableEnhancedRandomizationOnOpenNetwork(WifiConfiguration config)462     private boolean shouldEnableEnhancedRandomizationOnOpenNetwork(WifiConfiguration config) {
463         if (!mDeviceConfigFacade.allowEnhancedMacRandomizationOnOpenSsids()
464                 && !mContext.getResources().getBoolean(
465                         R.bool.config_wifiAllowEnhancedMacRandomizationOnOpenSsids)) {
466             return false;
467         }
468         return config.getNetworkSelectionStatus().hasEverConnected()
469                 && config.getNetworkSelectionStatus().hasNeverDetectedCaptivePortal();
470     }
471 
isNetworkOptInForEnhancedRandomization(String ssidOrFqdn)472     private boolean isNetworkOptInForEnhancedRandomization(String ssidOrFqdn) {
473         Set<String> perDeviceSsidBlocklist = new ArraySet<>(mContext.getResources().getStringArray(
474                 R.array.config_wifi_aggressive_randomization_ssid_blocklist));
475         if (mDeviceConfigFacade.getAggressiveMacRandomizationSsidBlocklist().contains(ssidOrFqdn)
476                 || perDeviceSsidBlocklist.contains(ssidOrFqdn)) {
477             return false;
478         }
479         Set<String> perDeviceSsidAllowlist = new ArraySet<>(mContext.getResources().getStringArray(
480                 R.array.config_wifi_aggressive_randomization_ssid_allowlist));
481         return mDeviceConfigFacade.getAggressiveMacRandomizationSsidAllowlist().contains(ssidOrFqdn)
482                 || perDeviceSsidAllowlist.contains(ssidOrFqdn);
483     }
484 
485     @VisibleForTesting
getRandomizedMacAddressMappingSize()486     protected int getRandomizedMacAddressMappingSize() {
487         return mRandomizedMacAddressMapping.size();
488     }
489 
490     /**
491      * The persistent randomized MAC address is locally generated for each SSID and does not
492      * change until factory reset of the device. In the initial Q release the per-SSID randomized
493      * MAC is saved on the device, but in an update the storing of randomized MAC is removed.
494      * Instead, the randomized MAC is calculated directly from the SSID and a on device secret.
495      * For backward compatibility, this method first checks the device storage for saved
496      * randomized MAC. If it is not found or the saved MAC is invalid then it will calculate the
497      * randomized MAC directly.
498      *
499      * In the future as devices launched on Q no longer get supported, this method should get
500      * simplified to return the calculated MAC address directly.
501      * @param config the WifiConfiguration to obtain MAC address for.
502      * @return persistent MAC address for this WifiConfiguration
503      */
504     @VisibleForTesting
getPersistentMacAddress(WifiConfiguration config)505     public MacAddress getPersistentMacAddress(WifiConfiguration config) {
506         // mRandomizedMacAddressMapping had been the location to save randomized MAC addresses.
507         String persistentMacString = mRandomizedMacAddressMapping.get(
508                 config.getNetworkKey());
509         // Use the MAC address stored in the storage if it exists and is valid. Otherwise
510         // use the MAC address calculated from a hash function as the persistent MAC.
511         if (persistentMacString != null) {
512             try {
513                 return MacAddress.fromString(persistentMacString);
514             } catch (IllegalArgumentException e) {
515                 Log.e(TAG, "Error creating randomized MAC address from stored value.");
516                 mRandomizedMacAddressMapping.remove(config.getNetworkKey());
517             }
518         }
519         MacAddress result = mMacAddressUtil.calculatePersistentMac(config.getNetworkKey(),
520                 mMacAddressUtil.obtainMacRandHashFunction(Process.WIFI_UID));
521         if (result == null) {
522             result = mMacAddressUtil.calculatePersistentMac(config.getNetworkKey(),
523                     mMacAddressUtil.obtainMacRandHashFunction(Process.WIFI_UID));
524         }
525         if (result == null) {
526             Log.wtf(TAG, "Failed to generate MAC address from KeyStore even after retrying. "
527                     + "Using locally generated MAC address instead.");
528             result = config.getRandomizedMacAddress();
529             if (DEFAULT_MAC_ADDRESS.equals(result)) {
530                 result = MacAddressUtils.createRandomUnicastAddress();
531             }
532         }
533         return result;
534     }
535 
536     /**
537      * Sets the randomized MAC expiration time based on the DHCP lease duration.
538      * This should be called every time DHCP lease information is obtained.
539      */
updateRandomizedMacExpireTime(WifiConfiguration config, long dhcpLeaseSeconds)540     public void updateRandomizedMacExpireTime(WifiConfiguration config, long dhcpLeaseSeconds) {
541         WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId);
542         if (internalConfig == null) {
543             return;
544         }
545         long expireDurationMs = (dhcpLeaseSeconds & 0xffffffffL) * 1000;
546         expireDurationMs = Math.max(ENHANCED_MAC_REFRESH_MS_MIN, expireDurationMs);
547         expireDurationMs = Math.min(ENHANCED_MAC_REFRESH_MS_MAX, expireDurationMs);
548         internalConfig.randomizedMacExpirationTimeMs = mClock.getWallClockMillis()
549                 + expireDurationMs;
550     }
551 
setRandomizedMacAddress(WifiConfiguration config, MacAddress mac)552     private void setRandomizedMacAddress(WifiConfiguration config, MacAddress mac) {
553         config.setRandomizedMacAddress(mac);
554         config.randomizedMacLastModifiedTimeMs = mClock.getWallClockMillis();
555     }
556 
557     /**
558      * Obtain the persistent MAC address by first reading from an internal database. If non exists
559      * then calculate the persistent MAC using HMAC-SHA256.
560      * Finally set the randomized MAC of the configuration to the randomized MAC obtained.
561      * @param config the WifiConfiguration to make the update
562      * @return the persistent MacAddress or null if the operation is unsuccessful
563      */
setRandomizedMacToPersistentMac(WifiConfiguration config)564     private MacAddress setRandomizedMacToPersistentMac(WifiConfiguration config) {
565         MacAddress persistentMac = getPersistentMacAddress(config);
566         if (persistentMac == null || persistentMac.equals(config.getRandomizedMacAddress())) {
567             return persistentMac;
568         }
569         WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId);
570         setRandomizedMacAddress(internalConfig, persistentMac);
571         return persistentMac;
572     }
573 
574     /**
575      * This method is called before connecting to a network that has "enhanced randomization"
576      * enabled, and will re-randomize the MAC address if needed.
577      * @param config the WifiConfiguration to make the update
578      * @return the updated MacAddress
579      */
updateRandomizedMacIfNeeded(WifiConfiguration config)580     private MacAddress updateRandomizedMacIfNeeded(WifiConfiguration config) {
581         boolean shouldUpdateMac = config.randomizedMacExpirationTimeMs
582                 < mClock.getWallClockMillis() || mClock.getWallClockMillis()
583                 - config.randomizedMacLastModifiedTimeMs >= ENHANCED_MAC_REFRESH_MS_MAX;
584         if (!shouldUpdateMac) {
585             return config.getRandomizedMacAddress();
586         }
587         WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId);
588         setRandomizedMacAddress(internalConfig, MacAddressUtils.createRandomUnicastAddress());
589         return internalConfig.getRandomizedMacAddress();
590     }
591 
592     /**
593      * Returns the randomized MAC address that should be used for this WifiConfiguration.
594      * This API may return a randomized MAC different from the persistent randomized MAC if
595      * the WifiConfiguration is configured for enhanced MAC randomization.
596      * @param config
597      * @return MacAddress
598      */
getRandomizedMacAndUpdateIfNeeded(WifiConfiguration config)599     public MacAddress getRandomizedMacAndUpdateIfNeeded(WifiConfiguration config) {
600         MacAddress mac = shouldUseEnhancedRandomization(config)
601                 ? updateRandomizedMacIfNeeded(config)
602                 : setRandomizedMacToPersistentMac(config);
603         return mac;
604     }
605 
606     /**
607      * Enable/disable verbose logging in WifiConfigManager & its helper classes.
608      */
enableVerboseLogging(boolean verbose)609     public void enableVerboseLogging(boolean verbose) {
610         mVerboseLoggingEnabled = verbose;
611         mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
612         mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
613         mWifiBlocklistMonitor.enableVerboseLogging(mVerboseLoggingEnabled);
614     }
615 
616     /**
617      * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
618      * is needed when the network configurations are being requested via the public WifiManager
619      * API's.
620      * This currently masks the following elements: psk, wepKeys & enterprise config password.
621      */
maskPasswordsInWifiConfiguration(WifiConfiguration configuration)622     private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
623         if (!TextUtils.isEmpty(configuration.preSharedKey)) {
624             configuration.preSharedKey = PASSWORD_MASK;
625         }
626         if (configuration.wepKeys != null) {
627             for (int i = 0; i < configuration.wepKeys.length; i++) {
628                 if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
629                     configuration.wepKeys[i] = PASSWORD_MASK;
630                 }
631             }
632         }
633         if (configuration.enterpriseConfig != null && !TextUtils.isEmpty(
634                 configuration.enterpriseConfig.getPassword())) {
635             configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
636         }
637     }
638 
639     /**
640      * Helper method to mask randomized MAC address from the provided WifiConfiguration Object.
641      * This is needed when the network configurations are being requested via the public
642      * WifiManager API's. This method puts "02:00:00:00:00:00" as the MAC address.
643      * @param configuration WifiConfiguration to hide the MAC address
644      */
maskRandomizedMacAddressInWifiConfiguration(WifiConfiguration configuration)645     private void maskRandomizedMacAddressInWifiConfiguration(WifiConfiguration configuration) {
646         setRandomizedMacAddress(configuration, DEFAULT_MAC_ADDRESS);
647     }
648 
649     /**
650      * Helper method to create a copy of the provided internal WifiConfiguration object to be
651      * passed to external modules.
652      *
653      * @param configuration provided WifiConfiguration object.
654      * @param maskPasswords Mask passwords or not.
655      * @param targetUid Target UID for MAC address reading: -1 = mask all, 0 = mask none, >0 =
656      *                  mask all but the targetUid (carrier app).
657      * @return Copy of the WifiConfiguration object.
658      */
createExternalWifiConfiguration( WifiConfiguration configuration, boolean maskPasswords, int targetUid)659     private WifiConfiguration createExternalWifiConfiguration(
660             WifiConfiguration configuration, boolean maskPasswords, int targetUid) {
661         WifiConfiguration network = new WifiConfiguration(configuration);
662         if (maskPasswords) {
663             maskPasswordsInWifiConfiguration(network);
664         }
665         if (targetUid != Process.WIFI_UID && targetUid != Process.SYSTEM_UID
666                 && targetUid != configuration.creatorUid) {
667             maskRandomizedMacAddressInWifiConfiguration(network);
668         }
669         if (!isMacRandomizationSupported()) {
670             network.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
671         }
672         return network;
673     }
674 
675     /**
676      * Returns whether MAC randomization is supported on this device.
677      * @param config
678      * @return
679      */
isMacRandomizationSupported()680     private boolean isMacRandomizationSupported() {
681         return mContext.getResources().getBoolean(
682                 R.bool.config_wifi_connected_mac_randomization_supported);
683     }
684 
685     /**
686      * Fetch the list of currently configured networks maintained in WifiConfigManager.
687      *
688      * This retrieves a copy of the internal configurations maintained by WifiConfigManager and
689      * should be used for any public interfaces.
690      *
691      * @param savedOnly     Retrieve only saved networks.
692      * @param maskPasswords Mask passwords or not.
693      * @param targetUid Target UID for MAC address reading: -1 (Invalid UID) = mask all,
694      *                  WIFI||SYSTEM = mask none, <other> = mask all but the targetUid (carrier
695      *                  app).
696      * @return List of WifiConfiguration objects representing the networks.
697      */
getConfiguredNetworks( boolean savedOnly, boolean maskPasswords, int targetUid)698     private List<WifiConfiguration> getConfiguredNetworks(
699             boolean savedOnly, boolean maskPasswords, int targetUid) {
700         List<WifiConfiguration> networks = new ArrayList<>();
701         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
702             if (savedOnly && (config.ephemeral || config.isPasspoint())) {
703                 continue;
704             }
705             networks.add(createExternalWifiConfiguration(config, maskPasswords, targetUid));
706         }
707         return networks;
708     }
709 
710     /**
711      * Retrieves the list of all configured networks with passwords masked.
712      *
713      * @return List of WifiConfiguration objects representing the networks.
714      */
getConfiguredNetworks()715     public List<WifiConfiguration> getConfiguredNetworks() {
716         return getConfiguredNetworks(false, true, Process.WIFI_UID);
717     }
718 
719     /**
720      * Retrieves the list of all configured networks with the passwords in plaintext.
721      *
722      * WARNING: Don't use this to pass network configurations to external apps. Should only be
723      * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
724      * TODO: Need to understand the current use case of this API.
725      *
726      * @return List of WifiConfiguration objects representing the networks.
727      */
getConfiguredNetworksWithPasswords()728     public List<WifiConfiguration> getConfiguredNetworksWithPasswords() {
729         return getConfiguredNetworks(false, false, Process.WIFI_UID);
730     }
731 
732     /**
733      * Retrieves the list of all configured networks with the passwords masked.
734      *
735      * @return List of WifiConfiguration objects representing the networks.
736      */
getSavedNetworks(int targetUid)737     public List<WifiConfiguration> getSavedNetworks(int targetUid) {
738         return getConfiguredNetworks(true, true, targetUid);
739     }
740 
741     /**
742      * Retrieves the configured network corresponding to the provided networkId with password
743      * masked.
744      *
745      * @param networkId networkId of the requested network.
746      * @return WifiConfiguration object if found, null otherwise.
747      */
getConfiguredNetwork(int networkId)748     public WifiConfiguration getConfiguredNetwork(int networkId) {
749         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
750         if (config == null) {
751             return null;
752         }
753         // Create a new configuration object with the passwords masked to send out to the external
754         // world.
755         return createExternalWifiConfiguration(config, true, Process.WIFI_UID);
756     }
757 
758     /**
759      * Retrieves the configured network corresponding to the provided config key with password
760      * masked.
761      *
762      * @param configKey configKey of the requested network.
763      * @return WifiConfiguration object if found, null otherwise.
764      */
getConfiguredNetwork(String configKey)765     public WifiConfiguration getConfiguredNetwork(String configKey) {
766         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
767         if (config == null) {
768             return null;
769         }
770         // Create a new configuration object with the passwords masked to send out to the external
771         // world.
772         return createExternalWifiConfiguration(config, true, Process.WIFI_UID);
773     }
774 
775     /**
776      * Retrieves the configured network corresponding to the provided networkId with password
777      * in plaintext.
778      *
779      * WARNING: Don't use this to pass network configurations to external apps. Should only be
780      * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
781      *
782      * @param networkId networkId of the requested network.
783      * @return WifiConfiguration object if found, null otherwise.
784      */
getConfiguredNetworkWithPassword(int networkId)785     public WifiConfiguration getConfiguredNetworkWithPassword(int networkId) {
786         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
787         if (config == null) {
788             return null;
789         }
790         // Create a new configuration object without the passwords masked to send out to the
791         // external world.
792         return createExternalWifiConfiguration(config, false, Process.WIFI_UID);
793     }
794 
795     /**
796      * Retrieves the configured network corresponding to the provided networkId
797      * without any masking.
798      *
799      * WARNING: Don't use this to pass network configurations except in the wifi stack, when
800      * there is a need for passwords and randomized MAC address.
801      *
802      * @param networkId networkId of the requested network.
803      * @return Copy of WifiConfiguration object if found, null otherwise.
804      */
getConfiguredNetworkWithoutMasking(int networkId)805     public WifiConfiguration getConfiguredNetworkWithoutMasking(int networkId) {
806         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
807         if (config == null) {
808             return null;
809         }
810         return new WifiConfiguration(config);
811     }
812 
813     /**
814      * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
815      * the networks in our database.
816      */
getInternalConfiguredNetworks()817     private Collection<WifiConfiguration> getInternalConfiguredNetworks() {
818         return mConfiguredNetworks.valuesForCurrentUser();
819     }
820 
getInternalConfiguredNetworkByUpgradableType( WifiConfiguration config)821     private WifiConfiguration getInternalConfiguredNetworkByUpgradableType(
822             WifiConfiguration config) {
823         WifiConfiguration internalConfig = null;
824         int securityType = config.getDefaultSecurityParams().getSecurityType();
825         WifiConfiguration possibleExistingConfig = new WifiConfiguration(config);
826         switch (securityType) {
827             case WifiConfiguration.SECURITY_TYPE_PSK:
828                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
829                 break;
830             case WifiConfiguration.SECURITY_TYPE_SAE:
831                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
832                 break;
833             case WifiConfiguration.SECURITY_TYPE_EAP:
834                 possibleExistingConfig.setSecurityParams(
835                         WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
836                 break;
837             case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
838                 possibleExistingConfig.setSecurityParams(
839                         WifiConfiguration.SECURITY_TYPE_EAP);
840                 break;
841             case WifiConfiguration.SECURITY_TYPE_OPEN:
842                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
843                 break;
844             case WifiConfiguration.SECURITY_TYPE_OWE:
845                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
846                 break;
847             default:
848                 return null;
849         }
850         internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(
851                 possibleExistingConfig.getProfileKey());
852         return internalConfig;
853     }
854 
855     /**
856      * Helper method to retrieve the internal WifiConfiguration object corresponding to the
857      * provided configuration in our database.
858      * This first attempts to find the network using the provided network ID in configuration,
859      * else it attempts to find a matching configuration using the configKey.
860      */
getInternalConfiguredNetwork(WifiConfiguration config)861     private WifiConfiguration getInternalConfiguredNetwork(WifiConfiguration config) {
862         WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
863         if (internalConfig != null) {
864             return internalConfig;
865         }
866         internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(
867                 config.getProfileKey());
868         if (internalConfig != null) {
869             return internalConfig;
870         }
871         internalConfig = getInternalConfiguredNetworkByUpgradableType(config);
872         if (internalConfig == null) {
873             Log.e(TAG, "Cannot find network with networkId " + config.networkId
874                     + " or configKey " + config.getProfileKey()
875                     + " or upgradable security type check");
876         }
877         return internalConfig;
878     }
879 
880     /**
881      * Helper method to retrieve the internal WifiConfiguration object corresponding to the
882      * provided network ID in our database.
883      */
getInternalConfiguredNetwork(int networkId)884     private WifiConfiguration getInternalConfiguredNetwork(int networkId) {
885         if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
886             return null;
887         }
888         WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId);
889         if (internalConfig == null) {
890             Log.e(TAG, "Cannot find network with networkId " + networkId);
891         }
892         return internalConfig;
893     }
894 
895     /**
896      * Helper method to retrieve the internal WifiConfiguration object corresponding to the
897      * provided configKey in our database.
898      */
getInternalConfiguredNetwork(String configKey)899     private WifiConfiguration getInternalConfiguredNetwork(String configKey) {
900         WifiConfiguration internalConfig =
901                 mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey);
902         if (internalConfig == null) {
903             Log.e(TAG, "Cannot find network with configKey " + configKey);
904         }
905         return internalConfig;
906     }
907 
908     /**
909      * Method to send out the configured networks change broadcast when network configurations
910      * changed.
911      *
912      * In Android R we stopped sending out WifiConfiguration due to user privacy concerns.
913      * Thus, no matter how many networks changed,
914      * {@link WifiManager#EXTRA_MULTIPLE_NETWORKS_CHANGED} is always set to true, and
915      * {@link WifiManager#EXTRA_WIFI_CONFIGURATION} is always null.
916      *
917      * @param reason  The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
918      *                WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
919      */
sendConfiguredNetworkChangedBroadcast(int reason)920     private void sendConfiguredNetworkChangedBroadcast(int reason) {
921         Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
922         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
923         intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
924         intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
925         mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.ACCESS_WIFI_STATE);
926     }
927 
928     /**
929      * Checks if |uid| has permission to modify the provided configuration.
930      *
931      * @param config         WifiConfiguration object corresponding to the network to be modified.
932      * @param uid            UID of the app requesting the modification.
933      * @param packageName    Package name of the app requesting the modification.
934      */
canModifyNetwork(WifiConfiguration config, int uid, @Nullable String packageName)935     private boolean canModifyNetwork(WifiConfiguration config, int uid,
936             @Nullable String packageName) {
937         // System internals can always update networks; they're typically only
938         // making meteredHint or meteredOverride changes
939         if (uid == Process.SYSTEM_UID) {
940             return true;
941         }
942 
943         // Passpoint configurations are generated and managed by PasspointManager. They can be
944         // added by either PasspointNetworkNominator (for auto connection) or Settings app
945         // (for manual connection), and need to be removed once the connection is completed.
946         // Since it is "owned" by us, so always allow us to modify them.
947         if (config.isPasspoint() && uid == Process.WIFI_UID) {
948             return true;
949         }
950 
951         // EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
952         // by authenticator back to the WifiConfiguration object.
953         // Since it is "owned" by us, so always allow us to modify them.
954         if (config.enterpriseConfig != null
955                 && uid == Process.WIFI_UID
956                 && config.enterpriseConfig.isAuthenticationSimBased()) {
957             return true;
958         }
959 
960         // TODO: ideally package should not be null here (and hence we wouldn't need the
961         // isDeviceOwner(uid) method), but it would require changing  many methods to pass the
962         // package name around (for example, all methods called by
963         // WifiServiceImpl.triggerConnectAndReturnStatus(netId, callingUid)
964         final boolean isDeviceOwner = packageName == null
965                 ? mWifiPermissionsUtil.isDeviceOwner(uid)
966                 : mWifiPermissionsUtil.isDeviceOwner(uid, packageName);
967 
968         // If |uid| corresponds to the device owner, allow all modifications.
969         if (isDeviceOwner) {
970             return true;
971         }
972 
973         final boolean isCreator = (config.creatorUid == uid);
974 
975         // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
976         final boolean isConfigEligibleForLockdown =
977                 mWifiPermissionsUtil.isDeviceOwner(config.creatorUid, config.creatorName);
978         if (!isConfigEligibleForLockdown) {
979             // App that created the network or settings app (i.e user) has permission to
980             // modify the network.
981             return isCreator
982                     || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
983                     || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid);
984         }
985 
986         final ContentResolver resolver = mContext.getContentResolver();
987         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
988                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
989         return !isLockdownFeatureEnabled
990                 // If not locked down, settings app (i.e user) has permission to modify the network.
991                 && (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
992                 || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid));
993 
994     }
995 
mergeSecurityParamsListWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)996     private void mergeSecurityParamsListWithInternalWifiConfiguration(
997             WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
998         // If not set, just copy over all list.
999         if (internalConfig.getSecurityParamsList().isEmpty()) {
1000             internalConfig.setSecurityParams(externalConfig.getSecurityParamsList());
1001             return;
1002         }
1003 
1004         WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary(externalConfig);
1005 
1006         // An external caller is only allowed to set one type manually.
1007         // As a result, only default type matters.
1008         // There might be 3 cases:
1009         // 1. Existing config with new upgradable type config,
1010         //    ex. PSK/SAE config with SAE config.
1011         // 2. Existing configuration with downgradable type config,
1012         //    ex. SAE config with PSK config.
1013         // 3. The new type is not a compatible type of existing config.
1014         //    ex. Open config with PSK config.
1015         //    This might happen when updating a config via network ID directly.
1016         int oldType = internalConfig.getDefaultSecurityParams().getSecurityType();
1017         int newType = externalConfig.getDefaultSecurityParams().getSecurityType();
1018         if (oldType != newType) {
1019             if (internalConfig.isSecurityType(newType)) {
1020                 internalConfig.setSecurityParamsIsAddedByAutoUpgrade(newType, false);
1021             } else if (externalConfig.isSecurityType(oldType)) {
1022                 internalConfig.setSecurityParams(newType);
1023                 internalConfig.addSecurityParams(oldType);
1024             } else {
1025                 internalConfig.setSecurityParams(externalConfig.getSecurityParamsList());
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Copy over public elements from an external WifiConfiguration object to the internal
1032      * configuration object if element has been set in the provided external WifiConfiguration.
1033      * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
1034      * for every update.
1035      *
1036      * This method updates all elements that are common to both network addition & update.
1037      * The following fields of {@link WifiConfiguration} are not copied from external configs:
1038      *  > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
1039      *  > status - The status needs to be explicitly updated using
1040      *             {@link WifiManager#enableNetwork(int, boolean)} or
1041      *             {@link WifiManager#disableNetwork(int)}.
1042      *
1043      * @param internalConfig WifiConfiguration object in our internal map.
1044      * @param externalConfig WifiConfiguration object provided from the external API.
1045      */
mergeWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)1046     private void mergeWithInternalWifiConfiguration(
1047             WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
1048         if (externalConfig.SSID != null) {
1049             internalConfig.SSID = externalConfig.SSID;
1050         }
1051         if (externalConfig.BSSID != null) {
1052             internalConfig.BSSID = externalConfig.BSSID.toLowerCase();
1053         }
1054         internalConfig.hiddenSSID = externalConfig.hiddenSSID;
1055 
1056         if (externalConfig.preSharedKey != null
1057                 && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
1058             internalConfig.preSharedKey = externalConfig.preSharedKey;
1059         }
1060         // Modify only wep keys are present in the provided configuration. This is a little tricky
1061         // because there is no easy way to tell if the app is actually trying to null out the
1062         // existing keys or not.
1063         if (externalConfig.wepKeys != null) {
1064             boolean hasWepKey = false;
1065             for (int i = 0; i < internalConfig.wepKeys.length; i++) {
1066                 if (externalConfig.wepKeys[i] != null
1067                         && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
1068                     internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
1069                     hasWepKey = true;
1070                 }
1071             }
1072             if (hasWepKey) {
1073                 internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
1074             }
1075         }
1076         if (externalConfig.FQDN != null) {
1077             internalConfig.FQDN = externalConfig.FQDN;
1078         }
1079         if (externalConfig.providerFriendlyName != null) {
1080             internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
1081         }
1082         if (externalConfig.roamingConsortiumIds != null) {
1083             internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone();
1084         }
1085 
1086         mergeSecurityParamsListWithInternalWifiConfiguration(internalConfig, externalConfig);
1087 
1088         // Copy over the |IpConfiguration| parameters if set.
1089         if (externalConfig.getIpConfiguration() != null) {
1090             IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
1091             if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
1092                 internalConfig.setIpAssignment(ipAssignment);
1093                 if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
1094                     internalConfig.setStaticIpConfiguration(
1095                             new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
1096                 }
1097             }
1098             IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
1099             if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
1100                 internalConfig.setProxySettings(proxySettings);
1101                 if (proxySettings == IpConfiguration.ProxySettings.PAC
1102                         || proxySettings == IpConfiguration.ProxySettings.STATIC) {
1103                     internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
1104                 }
1105             }
1106         }
1107 
1108         // Copy over the |WifiEnterpriseConfig| parameters if set.
1109         if (externalConfig.enterpriseConfig != null) {
1110             internalConfig.enterpriseConfig.copyFromExternal(
1111                     externalConfig.enterpriseConfig, PASSWORD_MASK);
1112         }
1113 
1114         // Copy over any metered information.
1115         internalConfig.meteredHint = externalConfig.meteredHint;
1116         internalConfig.meteredOverride = externalConfig.meteredOverride;
1117 
1118         internalConfig.trusted = externalConfig.trusted;
1119         internalConfig.oemPaid = externalConfig.oemPaid;
1120         internalConfig.oemPrivate = externalConfig.oemPrivate;
1121         internalConfig.carrierMerged = externalConfig.carrierMerged;
1122 
1123         // Copy over macRandomizationSetting
1124         internalConfig.macRandomizationSetting = externalConfig.macRandomizationSetting;
1125         internalConfig.carrierId = externalConfig.carrierId;
1126         internalConfig.isHomeProviderNetwork = externalConfig.isHomeProviderNetwork;
1127         internalConfig.subscriptionId = externalConfig.subscriptionId;
1128         internalConfig.getNetworkSelectionStatus()
1129                 .setConnectChoice(externalConfig.getNetworkSelectionStatus().getConnectChoice());
1130         internalConfig.getNetworkSelectionStatus().setConnectChoiceRssi(
1131                 externalConfig.getNetworkSelectionStatus().getConnectChoiceRssi());
1132     }
1133 
1134     /**
1135      * Set all the exposed defaults in the newly created WifiConfiguration object.
1136      * These fields have a default value advertised in our public documentation. The only exception
1137      * is the hidden |IpConfiguration| parameters, these have a default value even though they're
1138      * hidden.
1139      *
1140      * @param configuration provided WifiConfiguration object.
1141      */
setDefaultsInWifiConfiguration(WifiConfiguration configuration)1142     private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
1143         configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
1144         configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
1145 
1146         configuration.status = WifiConfiguration.Status.DISABLED;
1147         configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
1148                 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1149         configuration.getNetworkSelectionStatus().setNetworkSelectionDisableReason(
1150                 NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
1151     }
1152 
1153     /**
1154      * Create a new internal WifiConfiguration object by copying over parameters from the provided
1155      * external configuration and set defaults for the appropriate parameters.
1156      *
1157      * @param externalConfig WifiConfiguration object provided from the external API.
1158      * @return New WifiConfiguration object with parameters merged from the provided external
1159      * configuration.
1160      */
createNewInternalWifiConfigurationFromExternal( WifiConfiguration externalConfig, int uid, @Nullable String packageName)1161     private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
1162             WifiConfiguration externalConfig, int uid, @Nullable String packageName) {
1163         WifiConfiguration newInternalConfig = new WifiConfiguration();
1164 
1165         // First allocate a new network ID for the configuration.
1166         newInternalConfig.networkId = mNextNetworkId++;
1167 
1168         // First set defaults in the new configuration created.
1169         setDefaultsInWifiConfiguration(newInternalConfig);
1170 
1171         // Convert legacy fields to new security params
1172         externalConfig.convertLegacyFieldsToSecurityParamsIfNeeded();
1173 
1174         // Copy over all the public elements from the provided configuration.
1175         mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
1176 
1177         // Copy over the hidden configuration parameters. These are the only parameters used by
1178         // system apps to indicate some property about the network being added.
1179         // These are only copied over for network additions and ignored for network updates.
1180         newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
1181         newInternalConfig.ephemeral = externalConfig.ephemeral;
1182         newInternalConfig.osu = externalConfig.osu;
1183         newInternalConfig.fromWifiNetworkSuggestion = externalConfig.fromWifiNetworkSuggestion;
1184         newInternalConfig.fromWifiNetworkSpecifier = externalConfig.fromWifiNetworkSpecifier;
1185         newInternalConfig.useExternalScores = externalConfig.useExternalScores;
1186         newInternalConfig.shared = externalConfig.shared;
1187         newInternalConfig.updateIdentifier = externalConfig.updateIdentifier;
1188         newInternalConfig.setPasspointUniqueId(externalConfig.getPasspointUniqueId());
1189 
1190         // Add debug information for network addition.
1191         newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
1192         newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
1193                 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid);
1194         newInternalConfig.lastUpdated = mClock.getWallClockMillis();
1195         newInternalConfig.numRebootsSinceLastUse = 0;
1196         initRandomizedMacForInternalConfig(newInternalConfig);
1197         return newInternalConfig;
1198     }
1199 
1200     /**
1201      * Create a new internal WifiConfiguration object by copying over parameters from the provided
1202      * external configuration to a copy of the existing internal WifiConfiguration object.
1203      *
1204      * @param internalConfig WifiConfiguration object in our internal map.
1205      * @param externalConfig WifiConfiguration object provided from the external API.
1206      * @return Copy of existing WifiConfiguration object with parameters merged from the provided
1207      * configuration.
1208      */
updateExistingInternalWifiConfigurationFromExternal( WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid, @Nullable String packageName)1209     private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
1210             WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid,
1211             @Nullable String packageName) {
1212         WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
1213 
1214         // Copy over all the public elements from the provided configuration.
1215         mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
1216 
1217         // Add debug information for network update.
1218         newInternalConfig.lastUpdateUid = uid;
1219         newInternalConfig.lastUpdateName =
1220                 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid);
1221         newInternalConfig.lastUpdated = mClock.getWallClockMillis();
1222         newInternalConfig.numRebootsSinceLastUse = 0;
1223         return newInternalConfig;
1224     }
1225 
logUserActionEvents(WifiConfiguration before, WifiConfiguration after)1226     private void logUserActionEvents(WifiConfiguration before, WifiConfiguration after) {
1227         // Logs changes in meteredOverride.
1228         if (before.meteredOverride != after.meteredOverride) {
1229             mWifiMetrics.logUserActionEvent(
1230                     WifiMetrics.convertMeteredOverrideEnumToUserActionEventType(
1231                             after.meteredOverride),
1232                     after.networkId);
1233         }
1234 
1235         // Logs changes in macRandomizationSetting.
1236         if (before.macRandomizationSetting != after.macRandomizationSetting) {
1237             mWifiMetrics.logUserActionEvent(
1238                     after.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE
1239                             ? UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF
1240                             : UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_ON,
1241                     after.networkId);
1242         }
1243     }
1244 
1245     /**
1246      * Add a network or update a network configuration to our database.
1247      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1248      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1249      *
1250      * @param config provided WifiConfiguration object.
1251      * @param uid UID of the app requesting the network addition/modification.
1252      * @param packageName Package name of the app requesting the network addition/modification.
1253      * @return NetworkUpdateResult object representing status of the update.
1254      */
addOrUpdateNetworkInternal(WifiConfiguration config, int uid, @Nullable String packageName)1255     private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid,
1256                                                            @Nullable String packageName) {
1257         if (mVerboseLoggingEnabled) {
1258             Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
1259         }
1260         WifiConfiguration newInternalConfig = null;
1261 
1262         // First check if we already have a network with the provided network id or configKey.
1263         WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
1264         // No existing network found. So, potentially a network add.
1265         if (existingInternalConfig == null) {
1266             if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
1267                 Log.e(TAG, "Cannot add network with invalid config");
1268                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1269             }
1270             newInternalConfig =
1271                     createNewInternalWifiConfigurationFromExternal(config, uid, packageName);
1272             // Since the original config provided may have had an empty
1273             // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
1274             // network with the the same configkey.
1275             existingInternalConfig =
1276                     getInternalConfiguredNetwork(newInternalConfig.getProfileKey());
1277         }
1278         // Existing network found. So, a network update.
1279         if (existingInternalConfig != null) {
1280             if (!WifiConfigurationUtil.validate(
1281                     config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
1282                 Log.e(TAG, "Cannot update network with invalid config");
1283                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1284             }
1285             // Check for the app's permission before we let it update this network.
1286             if (!canModifyNetwork(existingInternalConfig, uid, packageName)) {
1287                 Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1288                         + config.getProfileKey());
1289                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1290             }
1291             if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1292                     && !config.isPasspoint()) {
1293                 logUserActionEvents(existingInternalConfig, config);
1294             }
1295             newInternalConfig =
1296                     updateExistingInternalWifiConfigurationFromExternal(
1297                             existingInternalConfig, config, uid, packageName);
1298         }
1299 
1300         if (!WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary(newInternalConfig)) {
1301             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1302         }
1303 
1304         // Only add networks with proxy settings if the user has permission to
1305         if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
1306                 && !canModifyProxySettings(uid, packageName)) {
1307             Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
1308                     + config.getProfileKey() + ". Must have NETWORK_SETTINGS,"
1309                     + " or be device or profile owner.");
1310             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1311         }
1312 
1313         if (WifiConfigurationUtil.hasMacRandomizationSettingsChanged(existingInternalConfig,
1314                 newInternalConfig) && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1315                 && !mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)
1316                 && !(newInternalConfig.isPasspoint() && uid == newInternalConfig.creatorUid)
1317                 && !config.fromWifiNetworkSuggestion) {
1318             Log.e(TAG, "UID " + uid + " does not have permission to modify MAC randomization "
1319                     + "Settings " + config.getProfileKey() + ". Must have "
1320                     + "NETWORK_SETTINGS or NETWORK_SETUP_WIZARD or be the creator adding or "
1321                     + "updating a passpoint network.");
1322             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1323         }
1324 
1325         // Update the keys for saved enterprise networks. For Passpoint, the certificates
1326         // and keys are installed at the time the provider is installed. For suggestion enterprise
1327         // network the certificates and keys are installed at the time the suggestion is added
1328         if (!config.isPasspoint() && !config.fromWifiNetworkSuggestion && config.isEnterprise()) {
1329             if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
1330                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1331             }
1332         }
1333 
1334         boolean newNetwork = (existingInternalConfig == null);
1335         // This is needed to inform IpClient about any IP configuration changes.
1336         boolean hasIpChanged =
1337                 newNetwork || WifiConfigurationUtil.hasIpChanged(
1338                         existingInternalConfig, newInternalConfig);
1339         boolean hasProxyChanged =
1340                 newNetwork || WifiConfigurationUtil.hasProxyChanged(
1341                         existingInternalConfig, newInternalConfig);
1342         // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
1343         boolean hasCredentialChanged =
1344                 newNetwork || WifiConfigurationUtil.hasCredentialChanged(
1345                         existingInternalConfig, newInternalConfig);
1346         if (hasCredentialChanged) {
1347             newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
1348         }
1349 
1350         // Add it to our internal map. This will replace any existing network configuration for
1351         // updates.
1352         try {
1353             if (null != existingInternalConfig) {
1354                 mConfiguredNetworks.remove(existingInternalConfig.networkId);
1355             }
1356             mConfiguredNetworks.put(newInternalConfig);
1357         } catch (IllegalArgumentException e) {
1358             Log.e(TAG, "Failed to add network to config map", e);
1359             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1360         }
1361         if (removeExcessNetworks()) {
1362             if (mConfiguredNetworks.getForAllUsers(newInternalConfig.networkId) == null) {
1363                 Log.e(TAG, "Cannot add network because number of configured networks is maxed.");
1364                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1365             }
1366         }
1367 
1368         // Only re-enable network: 1. add or update user saved network; 2. add or update a user
1369         // saved passpoint network framework consider it is a new network.
1370         if (!newInternalConfig.fromWifiNetworkSuggestion
1371                 && (!newInternalConfig.isPasspoint() || newNetwork)) {
1372             userEnabledNetwork(newInternalConfig.networkId);
1373         }
1374 
1375         // Stage the backup of the SettingsProvider package which backs this up.
1376         mBackupManagerProxy.notifyDataChanged();
1377 
1378         NetworkUpdateResult result = new NetworkUpdateResult(
1379                 newInternalConfig.networkId,
1380                 hasIpChanged,
1381                 hasProxyChanged,
1382                 hasCredentialChanged,
1383                 newNetwork);
1384 
1385         localLog("addOrUpdateNetworkInternal: added/updated config."
1386                 + " netId=" + newInternalConfig.networkId
1387                 + " configKey=" + newInternalConfig.getProfileKey()
1388                 + " uid=" + Integer.toString(newInternalConfig.creatorUid)
1389                 + " name=" + newInternalConfig.creatorName);
1390         return result;
1391     }
1392 
1393     /**
1394      * Add a network or update a network configuration to our database.
1395      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1396      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1397      *
1398      * @param config provided WifiConfiguration object.
1399      * @param uid UID of the app requesting the network addition/modification.
1400      * @param packageName Package name of the app requesting the network addition/modification.
1401      * @return NetworkUpdateResult object representing status of the update.
1402      */
addOrUpdateNetwork(WifiConfiguration config, int uid, @Nullable String packageName)1403     public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid,
1404                                                   @Nullable String packageName) {
1405         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1406             Log.e(TAG, "UID " + uid + " not visible to the current user");
1407             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1408         }
1409         if (config == null) {
1410             Log.e(TAG, "Cannot add/update network with null config");
1411             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1412         }
1413         if (mPendingStoreRead) {
1414             Log.e(TAG, "Cannot add/update network before store is read!");
1415             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1416         }
1417         config.convertLegacyFieldsToSecurityParamsIfNeeded();
1418         WifiConfiguration existingConfig = getInternalConfiguredNetwork(config);
1419         if (!config.isEphemeral()) {
1420             // Removes the existing ephemeral network if it exists to add this configuration.
1421             if (existingConfig != null && existingConfig.isEphemeral()) {
1422                 // In this case, new connection for this config won't happen because same
1423                 // network is already registered as an ephemeral network.
1424                 // Clear the Ephemeral Network to address the situation.
1425                 removeNetwork(
1426                         existingConfig.networkId, existingConfig.creatorUid, config.creatorName);
1427             }
1428         }
1429 
1430         NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid, packageName);
1431         if (!result.isSuccess()) {
1432             Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
1433             return result;
1434         }
1435         WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
1436         sendConfiguredNetworkChangedBroadcast(
1437                 result.isNewNetwork()
1438                         ? WifiManager.CHANGE_REASON_ADDED
1439                         : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1440         // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
1441         if (!config.ephemeral && !config.isPasspoint()) {
1442             saveToStore(true);
1443         }
1444 
1445         for (OnNetworkUpdateListener listener : mListeners) {
1446             if (result.isNewNetwork()) {
1447                 listener.onNetworkAdded(
1448                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID));
1449             } else {
1450                 listener.onNetworkUpdated(
1451                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID),
1452                         createExternalWifiConfiguration(existingConfig, true, Process.WIFI_UID));
1453             }
1454         }
1455         return result;
1456     }
1457 
1458     /**
1459      * Add a network or update a network configuration to our database.
1460      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1461      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1462      *
1463      * @param config provided WifiConfiguration object.
1464      * @param uid    UID of the app requesting the network addition/modification.
1465      * @return NetworkUpdateResult object representing status of the update.
1466      */
addOrUpdateNetwork(WifiConfiguration config, int uid)1467     public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
1468         return addOrUpdateNetwork(config, uid, null);
1469     }
1470 
1471     /**
1472      * Increments the number of reboots since last use for each configuration.
1473      *
1474      * @see {@link WifiConfiguration#numRebootsSinceLastUse}
1475      */
incrementNumRebootsSinceLastUse()1476     public void incrementNumRebootsSinceLastUse() {
1477         getInternalConfiguredNetworks().forEach(config -> config.numRebootsSinceLastUse++);
1478         saveToStore(false);
1479     }
1480 
1481     /**
1482      * Removes excess networks in case the number of saved networks exceeds the max limit
1483      * specified in config_wifiMaxNumWifiConfigurations.
1484      *
1485      * Configs are removed in ascending order of
1486      *     1. Non-carrier networks before carrier networks
1487      *     2. Non-connected networks before connected networks.
1488      *     3. Deletion priority {@see WifiConfiguration#getDeletionPriority()}
1489      *     4. Last use/creation/update time (lastUpdated/lastConnected or numRebootsSinceLastUse)
1490      *     5. Open and OWE networks before networks with other security types.
1491      *     6. Number of associations
1492      *
1493      * @return {@code true} if networks were removed, {@code false} otherwise.
1494      */
removeExcessNetworks()1495     private boolean removeExcessNetworks() {
1496         final int maxNumConfigs = mContext.getResources().getInteger(
1497                 R.integer.config_wifiMaxNumWifiConfigurations);
1498         if (maxNumConfigs < 0) {
1499             // Max number of saved networks not specified.
1500             return false;
1501         }
1502 
1503         List<WifiConfiguration> savedNetworks = getSavedNetworks(Process.WIFI_UID);
1504         final int numExcessNetworks = savedNetworks.size() - maxNumConfigs;
1505         if (numExcessNetworks <= 0) {
1506             return false;
1507         }
1508 
1509         List<WifiConfiguration> configsToDelete = savedNetworks
1510                 .stream()
1511                 .sorted(Comparator.comparing((WifiConfiguration config) -> config.carrierId
1512                         == TelephonyManager.UNKNOWN_CARRIER_ID)
1513                         .thenComparing((WifiConfiguration config) -> config.status
1514                                 == WifiConfiguration.Status.CURRENT)
1515                         .thenComparing((WifiConfiguration config) -> config.getDeletionPriority())
1516                         .thenComparing((WifiConfiguration config) -> -config.numRebootsSinceLastUse)
1517                         .thenComparing((WifiConfiguration config) ->
1518                                 Math.max(config.lastConnected, config.lastUpdated))
1519                         .thenComparing((WifiConfiguration config) -> {
1520                             try {
1521                                 int authType = config.getAuthType();
1522                                 return !(authType == WifiConfiguration.KeyMgmt.NONE
1523                                         || authType == WifiConfiguration.KeyMgmt.OWE);
1524                             } catch (IllegalStateException e) {
1525                                 // An invalid keymgmt configuration should be pruned first.
1526                                 return false;
1527                             }
1528                         })
1529                         .thenComparing((WifiConfiguration config) -> config.numAssociation))
1530                 .limit(numExcessNetworks)
1531                 .collect(Collectors.toList());
1532         for (WifiConfiguration config : configsToDelete) {
1533             mConfiguredNetworks.remove(config.networkId);
1534             localLog("removeExcessNetworks: removed config."
1535                     + " netId=" + config.networkId
1536                     + " configKey=" + config.getProfileKey());
1537         }
1538         return true;
1539     }
1540 
1541     /**
1542      * Removes the specified network configuration from our database.
1543      *
1544      * @param config provided WifiConfiguration object.
1545      * @param uid UID of the app requesting the network deletion.
1546      * @return true if successful, false otherwise.
1547      */
removeNetworkInternal(WifiConfiguration config, int uid)1548     private boolean removeNetworkInternal(WifiConfiguration config, int uid) {
1549         if (mVerboseLoggingEnabled) {
1550             Log.v(TAG, "Removing network " + config.getPrintableSsid());
1551         }
1552         // Remove any associated enterprise keys for saved enterprise networks. Passpoint network
1553         // will remove the enterprise keys when provider is uninstalled. Suggestion enterprise
1554         // networks will remove the enterprise keys when suggestion is removed.
1555         if (!config.fromWifiNetworkSuggestion && !config.isPasspoint() && config.isEnterprise()) {
1556             mWifiKeyStore.removeKeys(config.enterpriseConfig);
1557         }
1558 
1559         // Do not remove the user choice when passpoint or suggestion networks are removed from
1560         // WifiConfigManager. Will remove that when profile is deleted from PassointManager or
1561         // WifiNetworkSuggestionsManager.
1562         if (!config.isPasspoint() && !config.fromWifiNetworkSuggestion) {
1563             removeConnectChoiceFromAllNetworks(config.getProfileKey());
1564         }
1565         mConfiguredNetworks.remove(config.networkId);
1566         mScanDetailCaches.remove(config.networkId);
1567         // Stage the backup of the SettingsProvider package which backs this up.
1568         mBackupManagerProxy.notifyDataChanged();
1569         mWifiBlocklistMonitor.handleNetworkRemoved(config.SSID);
1570 
1571         localLog("removeNetworkInternal: removed config."
1572                 + " netId=" + config.networkId
1573                 + " configKey=" + config.getProfileKey()
1574                 + " uid=" + Integer.toString(uid)
1575                 + " name=" + mContext.getPackageManager().getNameForUid(uid));
1576         return true;
1577     }
1578 
1579     /**
1580      * Removes the specified network configuration from our database.
1581      *
1582      * @param networkId network ID of the provided network.
1583      * @param uid       UID of the app requesting the network deletion.
1584      * @return true if successful, false otherwise.
1585      */
removeNetwork(int networkId, int uid, String packageName)1586     public boolean removeNetwork(int networkId, int uid, String packageName) {
1587         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1588             Log.e(TAG, "UID " + uid + " not visible to the current user");
1589             return false;
1590         }
1591         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1592         if (config == null) {
1593             return false;
1594         }
1595         if (!canModifyNetwork(config, uid, packageName)) {
1596             Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1597                     + config.getProfileKey());
1598             return false;
1599         }
1600         if (!removeNetworkInternal(config, uid)) {
1601             Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1602             return false;
1603         }
1604         if (networkId == mLastSelectedNetworkId) {
1605             clearLastSelectedNetwork();
1606         }
1607         if (!config.ephemeral && !config.isPasspoint()) {
1608             mLruConnectionTracker.removeNetwork(config);
1609         }
1610         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED);
1611         // Unless the removed network is ephemeral or Passpoint, persist the network removal.
1612         if (!config.ephemeral && !config.isPasspoint()) {
1613             saveToStore(true);
1614         }
1615         for (OnNetworkUpdateListener listener : mListeners) {
1616             listener.onNetworkRemoved(
1617                     createExternalWifiConfiguration(config, true, Process.WIFI_UID));
1618         }
1619         return true;
1620     }
1621 
getCreatorPackageName(WifiConfiguration config)1622     private String getCreatorPackageName(WifiConfiguration config) {
1623         String creatorName = config.creatorName;
1624         // getNameForUid (Stored in WifiConfiguration.creatorName) returns a concatenation of name
1625         // and uid for shared UIDs ("name:uid").
1626         if (!creatorName.contains(":")) {
1627             return creatorName; // regular app not using shared UID.
1628         }
1629         // Separate the package name from the string for app using shared UID.
1630         return creatorName.substring(0, creatorName.indexOf(":"));
1631     }
1632 
1633     /**
1634      * Remove all networks associated with an application.
1635      *
1636      * @param app Application info of the package of networks to remove.
1637      * @return the {@link Set} of networks that were removed by this call. Networks which matched
1638      *         but failed to remove are omitted from this set.
1639      */
removeNetworksForApp(ApplicationInfo app)1640     public Set<Integer> removeNetworksForApp(ApplicationInfo app) {
1641         if (app == null || app.packageName == null) {
1642             return Collections.<Integer>emptySet();
1643         }
1644         Log.d(TAG, "Remove all networks for app " + app);
1645         Set<Integer> removedNetworks = new ArraySet<>();
1646         WifiConfiguration[] copiedConfigs =
1647                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1648         for (WifiConfiguration config : copiedConfigs) {
1649             if (app.uid != config.creatorUid
1650                     || !app.packageName.equals(getCreatorPackageName(config))) {
1651                 continue;
1652             }
1653             localLog("Removing network " + config.SSID
1654                     + ", application \"" + app.packageName + "\" uninstalled"
1655                     + " from user " + UserHandle.getUserHandleForUid(app.uid));
1656             if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) {
1657                 removedNetworks.add(config.networkId);
1658             }
1659         }
1660         return removedNetworks;
1661     }
1662 
1663     /**
1664      * Remove all networks associated with a user.
1665      *
1666      * @param userId The identifier of the user which is being removed.
1667      * @return the {@link Set} of networks that were removed by this call. Networks which matched
1668      *         but failed to remove are omitted from this set.
1669      */
removeNetworksForUser(int userId)1670     Set<Integer> removeNetworksForUser(int userId) {
1671         Log.d(TAG, "Remove all networks for user " + userId);
1672         Set<Integer> removedNetworks = new ArraySet<>();
1673         WifiConfiguration[] copiedConfigs =
1674                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1675         for (WifiConfiguration config : copiedConfigs) {
1676             if (userId != UserHandle.getUserHandleForUid(config.creatorUid).getIdentifier()) {
1677                 continue;
1678             }
1679             localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1680             if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) {
1681                 removedNetworks.add(config.networkId);
1682             }
1683         }
1684         return removedNetworks;
1685     }
1686 
1687     /**
1688      * Iterates through the internal list of configured networks and removes any ephemeral or
1689      * passpoint network configurations which are transient in nature.
1690      *
1691      * @return true if a network was removed, false otherwise.
1692      */
removeAllEphemeralOrPasspointConfiguredNetworks()1693     public boolean removeAllEphemeralOrPasspointConfiguredNetworks() {
1694         if (mVerboseLoggingEnabled) {
1695             Log.v(TAG, "Removing all passpoint or ephemeral configured networks");
1696         }
1697         boolean didRemove = false;
1698         WifiConfiguration[] copiedConfigs =
1699                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1700         for (WifiConfiguration config : copiedConfigs) {
1701             if (config.isPasspoint()) {
1702                 Log.d(TAG, "Removing passpoint network config " + config.getProfileKey());
1703                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1704                 didRemove = true;
1705             } else if (config.ephemeral) {
1706                 Log.d(TAG, "Removing ephemeral network config " + config.getProfileKey());
1707                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1708                 didRemove = true;
1709             }
1710         }
1711         return didRemove;
1712     }
1713 
1714     /**
1715      * Removes the suggestion network configuration matched with WifiConfiguration provided.
1716      * @param suggestion WifiConfiguration for suggestion which needs to remove
1717      * @return true if a network was removed, false otherwise.
1718      */
removeSuggestionConfiguredNetwork(@onNull WifiConfiguration suggestion)1719     public boolean removeSuggestionConfiguredNetwork(@NonNull WifiConfiguration suggestion) {
1720         WifiConfiguration config = getInternalConfiguredNetwork(
1721                 suggestion.getProfileKey());
1722         if (config != null && config.ephemeral && config.fromWifiNetworkSuggestion) {
1723             Log.d(TAG, "Removing suggestion network config " + config.getProfileKey());
1724             return removeNetwork(config.networkId, suggestion.creatorUid, suggestion.creatorName);
1725         }
1726         return false;
1727     }
1728 
1729     /**
1730      * Removes the passpoint network configuration matched with {@code configKey} provided.
1731      *
1732      * @param configKey Config Key for the corresponding passpoint.
1733      * @return true if a network was removed, false otherwise.
1734      */
removePasspointConfiguredNetwork(@onNull String configKey)1735     public boolean removePasspointConfiguredNetwork(@NonNull String configKey) {
1736         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
1737         if (config != null && config.isPasspoint()) {
1738             Log.d(TAG, "Removing passpoint network config " + config.getProfileKey());
1739             return removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1740         }
1741         return false;
1742     }
1743 
1744     /**
1745      * Removes all save networks configurations not created by the caller.
1746      *
1747      * @param callerUid the uid of the caller
1748      * @return {@code true} if at least one network is removed.
1749      */
removeNonCallerConfiguredNetwork(int callerUid)1750     public boolean removeNonCallerConfiguredNetwork(int callerUid) {
1751         if (mVerboseLoggingEnabled) {
1752             Log.v(TAG, "removeNonCallerConfiguredNetwork caller = " + callerUid);
1753         }
1754         boolean didRemove = false;
1755         WifiConfiguration[] copiedConfigs =
1756                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1757         for (WifiConfiguration config : copiedConfigs) {
1758             if (config.creatorUid != callerUid) {
1759                 Log.d(TAG, "Removing non-caller network config " + config.getProfileKey());
1760                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1761                 didRemove = true;
1762             }
1763         }
1764         return didRemove;
1765     }
1766 
1767     /**
1768      * Check whether a network belong to a known list of networks that may not support randomized
1769      * MAC.
1770      * @param networkId
1771      * @return true if the network is in the hotlist and MAC randomization is enabled.
1772      */
isInFlakyRandomizationSsidHotlist(int networkId)1773     public boolean isInFlakyRandomizationSsidHotlist(int networkId) {
1774         WifiConfiguration config = getConfiguredNetwork(networkId);
1775         return config != null
1776                 && config.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_NONE
1777                 && mDeviceConfigFacade.getRandomizationFlakySsidHotlist().contains(config.SSID);
1778     }
1779 
1780     /**
1781      * Helper method to set the publicly exposed status for the network and send out the network
1782      * status change broadcast.
1783      */
setNetworkStatus(WifiConfiguration config, int status)1784     private void setNetworkStatus(WifiConfiguration config, int status) {
1785         config.status = status;
1786         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1787     }
1788 
1789     /**
1790      * Update a network's status (both internal and public) according to the update reason and
1791      * its current state.
1792      *
1793      * Each network has 2 status:
1794      * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
1795      * for temporarily disabling a network for Network Selector.
1796      * 2. Status: This is the exposed status for a network. This is mostly set by
1797      * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
1798      * {@link WifiManager#disableNetwork(int)}.
1799      *
1800      * @param networkId network ID of the network that needs the update.
1801      * @param reason    reason to update the network.
1802      * @return true if the input configuration has been updated, false otherwise.
1803      */
updateNetworkSelectionStatus(int networkId, int reason)1804     public boolean updateNetworkSelectionStatus(int networkId, int reason) {
1805         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1806         if (config == null) {
1807             return false;
1808         }
1809         return updateNetworkSelectionStatus(config, reason);
1810     }
1811 
updateNetworkSelectionStatus(WifiConfiguration config, int reason)1812     private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1813         int prevNetworkSelectionStatus = config.getNetworkSelectionStatus()
1814                 .getNetworkSelectionStatus();
1815         if (!mWifiBlocklistMonitor.updateNetworkSelectionStatus(config, reason)) {
1816             return false;
1817         }
1818         int newNetworkSelectionStatus = config.getNetworkSelectionStatus()
1819                 .getNetworkSelectionStatus();
1820         if (prevNetworkSelectionStatus != newNetworkSelectionStatus) {
1821             sendNetworkSelectionStatusChangedUpdate(config, newNetworkSelectionStatus, reason);
1822             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1823         }
1824         saveToStore(false);
1825         return true;
1826     }
1827 
sendNetworkSelectionStatusChangedUpdate(WifiConfiguration config, int newNetworkSelectionStatus, int disableReason)1828     private void sendNetworkSelectionStatusChangedUpdate(WifiConfiguration config,
1829             int newNetworkSelectionStatus, int disableReason) {
1830         switch (newNetworkSelectionStatus) {
1831             case NetworkSelectionStatus.NETWORK_SELECTION_ENABLED:
1832                 for (OnNetworkUpdateListener listener : mListeners) {
1833                     listener.onNetworkEnabled(
1834                             createExternalWifiConfiguration(config, true, Process.WIFI_UID));
1835                 }
1836                 break;
1837             case NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED:
1838                 for (OnNetworkUpdateListener listener : mListeners) {
1839                     listener.onNetworkTemporarilyDisabled(
1840                             createExternalWifiConfiguration(config, true, Process.WIFI_UID),
1841                             disableReason);
1842                 }
1843                 break;
1844             case NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED:
1845                 for (OnNetworkUpdateListener listener : mListeners) {
1846                     WifiConfiguration configForListener = new WifiConfiguration(config);
1847                     listener.onNetworkPermanentlyDisabled(
1848                             createExternalWifiConfiguration(config, true, Process.WIFI_UID),
1849                             disableReason);
1850                 }
1851                 break;
1852             default:
1853                 // all cases covered
1854         }
1855     }
1856 
1857     /**
1858      * Re-enable all temporary disabled configured networks.
1859      */
enableTemporaryDisabledNetworks()1860     public void enableTemporaryDisabledNetworks() {
1861         mWifiBlocklistMonitor.clearBssidBlocklist();
1862         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
1863             if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
1864                 updateNetworkSelectionStatus(config,
1865                         NetworkSelectionStatus.DISABLED_NONE);
1866             }
1867         }
1868     }
1869 
1870     /**
1871      * Attempt to re-enable a network for network selection, if this network was either:
1872      * a) Previously temporarily disabled, but its disable timeout has expired, or
1873      * b) Previously disabled because of a user switch, but is now visible to the current
1874      * user.
1875      *
1876      * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1877      * @return true if the network identified by {@param networkId} was re-enabled for qualified
1878      * network selection, false otherwise.
1879      */
tryEnableNetwork(int networkId)1880     public boolean tryEnableNetwork(int networkId) {
1881         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1882         if (config == null) {
1883             return false;
1884         }
1885         if (mWifiBlocklistMonitor.shouldEnableNetwork(config)) {
1886             return updateNetworkSelectionStatus(config, NetworkSelectionStatus.DISABLED_NONE);
1887         }
1888         return false;
1889     }
1890 
1891     /**
1892      * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
1893      *
1894      * @param networkId     network ID of the network that needs the update.
1895      * @param disableOthers Whether to disable all other networks or not. This is used to indicate
1896      *                      that the app requested connection to a specific network.
1897      * @param uid           uid of the app requesting the update.
1898      * @return true if it succeeds, false otherwise
1899      */
enableNetwork(int networkId, boolean disableOthers, int uid, String packageName)1900     public boolean enableNetwork(int networkId, boolean disableOthers, int uid,
1901                                  String packageName) {
1902         if (mVerboseLoggingEnabled) {
1903             Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
1904         }
1905         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1906             Log.e(TAG, "UID " + uid + " not visible to the current user");
1907             return false;
1908         }
1909         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1910         if (config == null) {
1911             return false;
1912         }
1913         // Set the "last selected" flag even if the app does not have permissions to modify this
1914         // network config. Apps are allowed to connect to networks even if they don't have
1915         // permission to modify it.
1916         if (disableOthers) {
1917             setLastSelectedNetwork(networkId);
1918         }
1919         if (!canModifyNetwork(config, uid, packageName)) {
1920             Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1921                     + config.getProfileKey());
1922             return false;
1923         }
1924         if (!updateNetworkSelectionStatus(
1925                 networkId, WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)) {
1926             return false;
1927         }
1928         saveToStore(true);
1929         return true;
1930     }
1931 
1932     /**
1933      * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
1934      *
1935      * @param networkId network ID of the network that needs the update.
1936      * @param uid       uid of the app requesting the update.
1937      * @return true if it succeeds, false otherwise
1938      */
disableNetwork(int networkId, int uid, String packageName)1939     public boolean disableNetwork(int networkId, int uid, String packageName) {
1940         if (mVerboseLoggingEnabled) {
1941             Log.v(TAG, "Disabling network " + networkId);
1942         }
1943         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1944             Log.e(TAG, "UID " + uid + " not visible to the current user");
1945             return false;
1946         }
1947         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1948         if (config == null) {
1949             return false;
1950         }
1951         // Reset the "last selected" flag even if the app does not have permissions to modify this
1952         // network config.
1953         if (networkId == mLastSelectedNetworkId) {
1954             clearLastSelectedNetwork();
1955         }
1956         if (!canModifyNetwork(config, uid, packageName)) {
1957             Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1958                     + config.getProfileKey());
1959             return false;
1960         }
1961         if (!updateNetworkSelectionStatus(
1962                 networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
1963             return false;
1964         }
1965         saveToStore(true);
1966         return true;
1967     }
1968 
1969     /**
1970      * Changes the user's choice to allow auto-join using the
1971      * {@link WifiManager#allowAutojoin(int, boolean)} API.
1972      *
1973      * @param networkId network ID of the network that needs the update.
1974      * @param choice the choice to allow auto-join or not
1975      * @return true if it succeeds, false otherwise
1976      */
allowAutojoin(int networkId, boolean choice)1977     public boolean allowAutojoin(int networkId, boolean choice) {
1978         if (mVerboseLoggingEnabled) {
1979             Log.v(TAG, "Setting allowAutojoin to " + choice + " for netId " + networkId);
1980         }
1981         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1982         if (config == null) {
1983             Log.e(TAG, "allowAutojoin: Supplied networkId " + networkId
1984                     + " has no matching config");
1985             return false;
1986         }
1987 
1988         config.allowAutojoin = choice;
1989         if (!choice) {
1990             removeConnectChoiceFromAllNetworks(config.getProfileKey());
1991             clearConnectChoiceInternal(config);
1992         }
1993         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1994         if (!config.ephemeral) {
1995             saveToStore(true);
1996         }
1997         return true;
1998     }
1999 
2000     /**
2001      * Updates the last connected UID for the provided configuration.
2002      *
2003      * @param networkId network ID corresponding to the network.
2004      * @param uid       uid of the app requesting the connection.
2005      * @return true if the network was found, false otherwise.
2006      */
updateLastConnectUid(int networkId, int uid)2007     private boolean updateLastConnectUid(int networkId, int uid) {
2008         if (mVerboseLoggingEnabled) {
2009             Log.v(TAG, "Update network last connect UID for " + networkId);
2010         }
2011         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
2012             Log.e(TAG, "UID " + uid + " not visible to the current user");
2013             return false;
2014         }
2015         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2016         if (config == null) {
2017             return false;
2018         }
2019         config.lastConnectUid = uid;
2020         return true;
2021     }
2022 
2023     /**
2024      * Updates a network configuration after a successful connection to it.
2025      *
2026      * This method updates the following WifiConfiguration elements:
2027      * 1. Set the |lastConnected| timestamp.
2028      * 2. Increment |numAssociation| counter.
2029      * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
2030      * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
2031      * 5. Sets the status of network as |CURRENT|.
2032      *
2033      * @param networkId network ID corresponding to the network.
2034      * @param shouldSetUserConnectChoice setup user connect choice on this network.
2035      * @param rssi signal strength of the connected network.
2036      * @return true if the network was found, false otherwise.
2037      */
updateNetworkAfterConnect(int networkId, boolean shouldSetUserConnectChoice, int rssi)2038     public boolean updateNetworkAfterConnect(int networkId, boolean shouldSetUserConnectChoice,
2039             int rssi) {
2040         if (mVerboseLoggingEnabled) {
2041             Log.v(TAG, "Update network after connect for " + networkId);
2042         }
2043         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2044         if (config == null) {
2045             return false;
2046         }
2047 
2048         // Only record connection order for non-passpoint from user saved or suggestion.
2049         if (!config.isPasspoint() && (config.fromWifiNetworkSuggestion || !config.ephemeral)) {
2050             mLruConnectionTracker.addNetwork(config);
2051         }
2052         if (shouldSetUserConnectChoice) {
2053             setUserConnectChoice(config.networkId, rssi);
2054         }
2055         config.lastConnected = mClock.getWallClockMillis();
2056         config.numRebootsSinceLastUse = 0;
2057         config.numAssociation++;
2058         config.getNetworkSelectionStatus().clearDisableReasonCounter();
2059         config.getNetworkSelectionStatus().setHasEverConnected(true);
2060         setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
2061         saveToStore(false);
2062         return true;
2063     }
2064 
2065     /**
2066      * Set captive portal to be detected for this network.
2067      * @param networkId
2068      */
noteCaptivePortalDetected(int networkId)2069     public void noteCaptivePortalDetected(int networkId) {
2070         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2071         if (config != null) {
2072             config.getNetworkSelectionStatus().setHasNeverDetectedCaptivePortal(false);
2073         }
2074     }
2075 
2076     /**
2077      * Updates a network configuration after disconnection from it.
2078      *
2079      * This method updates the following WifiConfiguration elements:
2080      * 1. Set the |lastDisConnected| timestamp.
2081      * 2. Sets the status of network back to |ENABLED|.
2082      *
2083      * @param networkId network ID corresponding to the network.
2084      * @return true if the network was found, false otherwise.
2085      */
updateNetworkAfterDisconnect(int networkId)2086     public boolean updateNetworkAfterDisconnect(int networkId) {
2087         if (mVerboseLoggingEnabled) {
2088             Log.v(TAG, "Update network after disconnect for " + networkId);
2089         }
2090         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2091         if (config == null) {
2092             return false;
2093         }
2094         config.lastDisconnected = mClock.getWallClockMillis();
2095         config.randomizedMacExpirationTimeMs = Math.max(config.randomizedMacExpirationTimeMs,
2096                 config.lastDisconnected + ENHANCED_MAC_WAIT_AFTER_DISCONNECT_MS);
2097         // If the network hasn't been disabled, mark it back as
2098         // enabled after disconnection.
2099         if (config.status == WifiConfiguration.Status.CURRENT) {
2100             setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
2101         }
2102         saveToStore(false);
2103         return true;
2104     }
2105 
2106     /**
2107      * Set default GW MAC address for the provided network.
2108      *
2109      * @param networkId network ID corresponding to the network.
2110      * @param macAddress MAC address of the gateway to be set.
2111      * @return true if the network was found, false otherwise.
2112      */
setNetworkDefaultGwMacAddress(int networkId, String macAddress)2113     public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
2114         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2115         if (config == null) {
2116             return false;
2117         }
2118         config.defaultGwMacAddress = macAddress;
2119         return true;
2120     }
2121 
2122     /**
2123      * Clear the {@link NetworkSelectionStatus#mCandidate},
2124      * {@link NetworkSelectionStatus#mCandidateScore} &
2125      * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
2126      *
2127      * This is invoked by Network Selector at the start of every selection procedure to clear all
2128      * configured networks' scan-result-candidates.
2129      *
2130      * @param networkId network ID corresponding to the network.
2131      * @return true if the network was found, false otherwise.
2132      */
clearNetworkCandidateScanResult(int networkId)2133     public boolean clearNetworkCandidateScanResult(int networkId) {
2134         if (mVerboseLoggingEnabled) {
2135             Log.v(TAG, "Clear network candidate scan result for " + networkId);
2136         }
2137         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2138         if (config == null) {
2139             return false;
2140         }
2141         config.getNetworkSelectionStatus().setCandidate(null);
2142         config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
2143         config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
2144         config.getNetworkSelectionStatus().setCandidateSecurityParams(null);
2145         return true;
2146     }
2147 
2148     /**
2149      * Set the {@link NetworkSelectionStatus#mCandidate},
2150      * {@link NetworkSelectionStatus#mCandidateScore} &
2151      * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
2152      *
2153      * This is invoked by Network Selector when it sees a network during network selection procedure
2154      * to set the scan result candidate.
2155      *
2156      * @param networkId  network ID corresponding to the network.
2157      * @param scanResult Candidate ScanResult associated with this network.
2158      * @param score      Score assigned to the candidate.
2159      * @param params     Security params for this candidate.
2160      * @return true if the network was found, false otherwise.
2161      */
setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score, SecurityParams params)2162     public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score,
2163             SecurityParams params) {
2164         if (mVerboseLoggingEnabled) {
2165             Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId
2166                     + " with security params " + params);
2167         }
2168         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2169         if (config == null) {
2170             Log.e(TAG, "Cannot find network for " + networkId);
2171             return false;
2172         }
2173         config.getNetworkSelectionStatus().setCandidate(scanResult);
2174         config.getNetworkSelectionStatus().setCandidateScore(score);
2175         config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
2176         config.getNetworkSelectionStatus().setCandidateSecurityParams(params);
2177         return true;
2178     }
2179 
2180     /**
2181      * Iterate through all the saved networks and remove the provided configuration from the
2182      * {@link NetworkSelectionStatus#mConnectChoice} from them.
2183      *
2184      * This is invoked when a network is removed from our records.
2185      *
2186      * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
2187      */
removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey)2188     public void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
2189         if (mVerboseLoggingEnabled) {
2190             Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
2191         }
2192         if (connectChoiceConfigKey == null) {
2193             return;
2194         }
2195         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2196             WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
2197             String connectChoice = status.getConnectChoice();
2198             if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
2199                 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
2200                         + " : " + config.networkId);
2201                 clearConnectChoiceInternal(config);
2202             }
2203         }
2204         for (OnNetworkUpdateListener listener : mListeners) {
2205             listener.onConnectChoiceRemoved(connectChoiceConfigKey);
2206         }
2207     }
2208 
2209     /**
2210      * Increments the number of no internet access reports in the provided network.
2211      *
2212      * @param networkId network ID corresponding to the network.
2213      * @return true if the network was found, false otherwise.
2214      */
incrementNetworkNoInternetAccessReports(int networkId)2215     public boolean incrementNetworkNoInternetAccessReports(int networkId) {
2216         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2217         if (config == null) {
2218             return false;
2219         }
2220         config.numNoInternetAccessReports++;
2221         config.validatedInternetAccess = false;
2222         return true;
2223     }
2224 
2225     /**
2226      * Sets the internet access is validated or not in the provided network.
2227      *
2228      * @param networkId network ID corresponding to the network.
2229      * @param validated Whether access is validated or not.
2230      * @return true if the network was found, false otherwise.
2231      */
setNetworkValidatedInternetAccess(int networkId, boolean validated)2232     public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
2233         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2234         if (config == null) {
2235             return false;
2236         }
2237         config.validatedInternetAccess = validated;
2238         config.numNoInternetAccessReports = 0;
2239         saveToStore(false);
2240         return true;
2241     }
2242 
2243     /**
2244      * Sets whether the internet access is expected or not in the provided network.
2245      *
2246      * @param networkId network ID corresponding to the network.
2247      * @param expected  Whether access is expected or not.
2248      * @return true if the network was found, false otherwise.
2249      */
setNetworkNoInternetAccessExpected(int networkId, boolean expected)2250     public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
2251         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2252         if (config == null) {
2253             return false;
2254         }
2255         config.noInternetAccessExpected = expected;
2256         return true;
2257     }
2258 
2259     /**
2260      * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
2261      * is done when either the corresponding network is either removed or disabled.
2262      */
clearLastSelectedNetwork()2263     public void clearLastSelectedNetwork() {
2264         if (mVerboseLoggingEnabled) {
2265             Log.v(TAG, "Clearing last selected network");
2266         }
2267         mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2268         mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
2269     }
2270 
2271     /**
2272      * Helper method to mark a network as the last selected one by an app/user. This is set
2273      * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
2274      * This is used by network selector to assign a special bonus during network selection.
2275      */
setLastSelectedNetwork(int networkId)2276     private void setLastSelectedNetwork(int networkId) {
2277         if (mVerboseLoggingEnabled) {
2278             Log.v(TAG, "Setting last selected network to " + networkId);
2279         }
2280         mLastSelectedNetworkId = networkId;
2281         mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
2282     }
2283 
2284     /**
2285      * Retrieve the network Id corresponding to the last network that was explicitly selected by
2286      * an app/user.
2287      *
2288      * @return network Id corresponding to the last selected network.
2289      */
getLastSelectedNetwork()2290     public int getLastSelectedNetwork() {
2291         return mLastSelectedNetworkId;
2292     }
2293 
2294     /**
2295      * Retrieve the configKey corresponding to the last network that was explicitly selected by
2296      * an app/user.
2297      *
2298      * @return network Id corresponding to the last selected network.
2299      */
getLastSelectedNetworkConfigKey()2300     public String getLastSelectedNetworkConfigKey() {
2301         if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
2302             return "";
2303         }
2304         WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
2305         if (config == null) {
2306             return "";
2307         }
2308         return config.getProfileKey();
2309     }
2310 
2311     /**
2312      * Retrieve the time stamp at which a network was explicitly selected by an app/user.
2313      *
2314      * @return timestamp in milliseconds from boot when this was set.
2315      */
getLastSelectedTimeStamp()2316     public long getLastSelectedTimeStamp() {
2317         return mLastSelectedTimeStamp;
2318     }
2319 
2320     /**
2321      * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
2322      * network.
2323      *
2324      * @param networkId network ID corresponding to the network.
2325      * @return existing {@link ScanDetailCache} entry if one exists or null.
2326      */
getScanDetailCacheForNetwork(int networkId)2327     public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
2328         return mScanDetailCaches.get(networkId);
2329     }
2330 
2331     /**
2332      * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
2333      * the provided network.
2334      *
2335      * @param config configuration corresponding to the the network.
2336      * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
2337      * this network.
2338      */
getOrCreateScanDetailCacheForNetwork(WifiConfiguration config)2339     private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
2340         if (config == null) return null;
2341         ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
2342         if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
2343             cache = new ScanDetailCache(
2344                     config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
2345             mScanDetailCaches.put(config.networkId, cache);
2346         }
2347         return cache;
2348     }
2349 
2350     /**
2351      * Saves the provided ScanDetail into the corresponding scan detail cache entry
2352      * {@link #mScanDetailCaches} for the provided network.
2353      *
2354      * @param config     configuration corresponding to the the network.
2355      * @param scanDetail new scan detail instance to be saved into the cache.
2356      */
saveToScanDetailCacheForNetwork( WifiConfiguration config, ScanDetail scanDetail)2357     private void saveToScanDetailCacheForNetwork(
2358             WifiConfiguration config, ScanDetail scanDetail) {
2359         ScanResult scanResult = scanDetail.getScanResult();
2360 
2361         WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID);
2362         network.addFrequency(scanResult.frequency);
2363         ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
2364         if (scanDetailCache == null) {
2365             Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
2366             return;
2367         }
2368 
2369         // Adding a new BSSID
2370         if (config.ephemeral) {
2371             // For an ephemeral Wi-Fi config, the ScanResult should be considered
2372             // untrusted.
2373             scanResult.untrusted = true;
2374         }
2375 
2376         // Add the scan detail to this network's scan detail cache.
2377         scanDetailCache.put(scanDetail);
2378     }
2379 
2380     /**
2381      * Retrieves a configured network corresponding to the provided scan detail if one exists.
2382      *
2383      * @param scanDetail ScanDetail instance  to use for looking up the network.
2384      * @return WifiConfiguration object representing the network corresponding to the scanDetail,
2385      * null if none exists.
2386      */
getSavedNetworkForScanDetail(ScanDetail scanDetail)2387     public WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) {
2388         ScanResult scanResult = scanDetail.getScanResult();
2389         if (scanResult == null) {
2390             Log.e(TAG, "No scan result found in scan detail");
2391             return null;
2392         }
2393         return getSavedNetworkForScanResult(scanResult);
2394     }
2395 
2396     /**
2397      * Retrieves a configured network corresponding to the provided scan result if one exists.
2398      *
2399      * @param scanResult ScanResult instance to use for looking up the network.
2400      * @return WifiConfiguration object representing the network corresponding to the scanResult,
2401      * null if none exists.
2402      */
getSavedNetworkForScanResult(@onNull ScanResult scanResult)2403     public WifiConfiguration getSavedNetworkForScanResult(@NonNull ScanResult scanResult) {
2404         WifiConfiguration config = null;
2405         try {
2406             config = mConfiguredNetworks.getByScanResultForCurrentUser(scanResult);
2407         } catch (IllegalArgumentException e) {
2408             Log.e(TAG, "Failed to lookup network from config map", e);
2409         }
2410         if (config != null) {
2411             if (mVerboseLoggingEnabled) {
2412                 Log.v(TAG, "getSavedNetworkFromScanResult Found " + config.getProfileKey()
2413                         + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
2414             }
2415         }
2416         return config;
2417     }
2418 
2419     /**
2420      * Caches the provided |scanDetail| into the corresponding scan detail cache entry
2421      * {@link #mScanDetailCaches} for the retrieved network.
2422      *
2423      * @param scanDetail input a scanDetail from the scan result
2424      */
updateScanDetailCacheFromScanDetailForSavedNetwork(ScanDetail scanDetail)2425     public void updateScanDetailCacheFromScanDetailForSavedNetwork(ScanDetail scanDetail) {
2426         WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
2427         if (network == null) {
2428             return;
2429         }
2430         saveToScanDetailCacheForNetwork(network, scanDetail);
2431     }
2432     /**
2433      * Retrieves a configured network corresponding to the provided scan detail if one exists and
2434      * caches the provided |scanDetail| into the corresponding scan detail cache entry
2435      * {@link #mScanDetailCaches} for the retrieved network.
2436      *
2437      * @param scanDetail input a scanDetail from the scan result
2438      * @return WifiConfiguration object representing the network corresponding to the scanDetail,
2439      * null if none exists.
2440      */
getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail)2441     public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) {
2442         WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
2443         if (network == null) {
2444             return null;
2445         }
2446         saveToScanDetailCacheForNetwork(network, scanDetail);
2447         // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
2448         // Information Element (IE), into the associated WifiConfigurations. Most of the
2449         // time there is no TIM IE in the scan result (Probe Response instead of Beacon
2450         // Frame), these scanResult DTIM's are negative and ignored.
2451         // Used for metrics collection.
2452         if (scanDetail.getNetworkDetail() != null
2453                 && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
2454             network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
2455         }
2456         return createExternalWifiConfiguration(network, true, Process.WIFI_UID);
2457     }
2458 
2459     /**
2460      * Update the scan detail cache associated with current connected network with latest
2461      * RSSI value in the provided WifiInfo.
2462      * This is invoked when we get an RSSI poll update after connection.
2463      *
2464      * @param info WifiInfo instance pointing to the current connected network.
2465      */
updateScanDetailCacheFromWifiInfo(WifiInfo info)2466     public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
2467         WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
2468         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
2469         if (config != null && scanDetailCache != null) {
2470             ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
2471             if (scanDetail != null) {
2472                 ScanResult result = scanDetail.getScanResult();
2473                 long previousSeen = result.seen;
2474                 int previousRssi = result.level;
2475                 // Update the scan result
2476                 scanDetail.setSeen();
2477                 result.level = info.getRssi();
2478                 // Average the RSSI value
2479                 long maxAge = SCAN_RESULT_MAXIMUM_AGE_MS;
2480                 long age = result.seen - previousSeen;
2481                 if (previousSeen > 0 && age > 0 && age < maxAge / 2) {
2482                     // Average the RSSI with previously seen instances of this scan result
2483                     double alpha = 0.5 - (double) age / (double) maxAge;
2484                     result.level = (int) ((double) result.level * (1 - alpha)
2485                                         + (double) previousRssi * alpha);
2486                 }
2487                 if (mVerboseLoggingEnabled) {
2488                     Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
2489                             + " BSSID=" + result.BSSID
2490                             + " RSSI=" + result.level
2491                             + " for " + config.getProfileKey());
2492                 }
2493             }
2494         }
2495     }
2496 
2497     /**
2498      * Save the ScanDetail to the ScanDetailCache of the given network.  This is used
2499      * by {@link PasspointNetworkNominator} for caching
2500      * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
2501      *
2502      * @param networkId The ID of the network to save ScanDetail to
2503      * @param scanDetail The ScanDetail to cache
2504      */
updateScanDetailForNetwork(int networkId, ScanDetail scanDetail)2505     public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
2506         WifiConfiguration network = getInternalConfiguredNetwork(networkId);
2507         if (network == null) {
2508             return;
2509         }
2510         saveToScanDetailCacheForNetwork(network, scanDetail);
2511     }
2512 
2513     /**
2514      * Helper method to check if the 2 provided networks can be linked or not.
2515      * Networks are considered for linking if:
2516      * 1. Share the same GW MAC address.
2517      * 2. Scan results for the networks have AP's with MAC address which differ only in the last
2518      * nibble.
2519      *
2520      * @param network1         WifiConfiguration corresponding to network 1.
2521      * @param network2         WifiConfiguration corresponding to network 2.
2522      * @param scanDetailCache1 ScanDetailCache entry for network 1.
2523      * @param scanDetailCache1 ScanDetailCache entry for network 2.
2524      * @return true if the networks should be linked, false if the networks should be unlinked.
2525      */
shouldNetworksBeLinked( WifiConfiguration network1, WifiConfiguration network2, ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2)2526     private boolean shouldNetworksBeLinked(
2527             WifiConfiguration network1, WifiConfiguration network2,
2528             ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
2529         // Check if networks should not be linked due to credential mismatch
2530         if (mContext.getResources().getBoolean(
2531                 R.bool.config_wifi_only_link_same_credential_configurations)) {
2532             if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
2533                 return false;
2534             }
2535         }
2536 
2537         // Skip VRRP MAC addresses since they are likely to correspond to different networks even if
2538         // they match.
2539         if ((network1.defaultGwMacAddress != null && network1.defaultGwMacAddress
2540                 .regionMatches(true, 0, VRRP_MAC_ADDRESS_PREFIX, 0,
2541                         VRRP_MAC_ADDRESS_PREFIX.length()))
2542                 || (network2.defaultGwMacAddress != null && network2.defaultGwMacAddress
2543                 .regionMatches(true, 0, VRRP_MAC_ADDRESS_PREFIX, 0,
2544                         VRRP_MAC_ADDRESS_PREFIX.length()))) {
2545             return false;
2546         }
2547 
2548         // Check if networks should be linked due to default gateway match
2549         if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
2550             // If both default GW are known, link only if they are equal
2551             if (network1.defaultGwMacAddress.equalsIgnoreCase(network2.defaultGwMacAddress)) {
2552                 if (mVerboseLoggingEnabled) {
2553                     Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
2554                             + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
2555                 }
2556                 return true;
2557             }
2558             return false;
2559         }
2560 
2561         // We do not know BOTH default gateways yet, but if the first 16 ASCII characters of BSSID
2562         // match then we can assume this is a DBDC with the same gateway. Once both gateways become
2563         // known, we will unlink the networks if it turns out the gateways are actually different.
2564         if (!mContext.getResources().getBoolean(
2565                 R.bool.config_wifiAllowLinkingUnknownDefaultGatewayConfigurations)) {
2566             return false;
2567         }
2568         if (scanDetailCache1 != null && scanDetailCache2 != null) {
2569             for (String abssid : scanDetailCache1.keySet()) {
2570                 for (String bbssid : scanDetailCache2.keySet()) {
2571                     if (abssid.regionMatches(
2572                             true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
2573                         if (mVerboseLoggingEnabled) {
2574                             Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
2575                                     + network2.SSID + " and " + network1.SSID
2576                                     + " bssida " + abssid + " bssidb " + bbssid);
2577                         }
2578                         return true;
2579                     }
2580                 }
2581             }
2582         }
2583         return false;
2584     }
2585 
2586     /**
2587      * Helper methods to link 2 networks together.
2588      *
2589      * @param network1 WifiConfiguration corresponding to network 1.
2590      * @param network2 WifiConfiguration corresponding to network 2.
2591      */
linkNetworks(WifiConfiguration network1, WifiConfiguration network2)2592     private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2593         if (mVerboseLoggingEnabled) {
2594             Log.v(TAG, "linkNetworks will link " + network2.getProfileKey()
2595                     + " and " + network1.getProfileKey());
2596         }
2597         if (network2.linkedConfigurations == null) {
2598             network2.linkedConfigurations = new HashMap<>();
2599         }
2600         if (network1.linkedConfigurations == null) {
2601             network1.linkedConfigurations = new HashMap<>();
2602         }
2603         // TODO (b/30638473): This needs to become a set instead of map, but it will need
2604         // public interface changes and need some migration of existing store data.
2605         network2.linkedConfigurations.put(network1.getProfileKey(), 1);
2606         network1.linkedConfigurations.put(network2.getProfileKey(), 1);
2607     }
2608 
2609     /**
2610      * Helper methods to unlink 2 networks from each other.
2611      *
2612      * @param network1 WifiConfiguration corresponding to network 1.
2613      * @param network2 WifiConfiguration corresponding to network 2.
2614      */
unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2)2615     private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2616         if (network2.linkedConfigurations != null
2617                 && (network2.linkedConfigurations.get(network1.getProfileKey()) != null)) {
2618             if (mVerboseLoggingEnabled) {
2619                 Log.v(TAG, "unlinkNetworks un-link " + network1.getProfileKey()
2620                         + " from " + network2.getProfileKey());
2621             }
2622             network2.linkedConfigurations.remove(network1.getProfileKey());
2623         }
2624         if (network1.linkedConfigurations != null
2625                 && (network1.linkedConfigurations.get(network2.getProfileKey()) != null)) {
2626             if (mVerboseLoggingEnabled) {
2627                 Log.v(TAG, "unlinkNetworks un-link " + network2.getProfileKey()
2628                         + " from " + network1.getProfileKey());
2629             }
2630             network1.linkedConfigurations.remove(network2.getProfileKey());
2631         }
2632     }
2633 
2634     /**
2635      * This method runs through all the saved networks and checks if the provided network can be
2636      * linked with any of them.
2637      *
2638      * @param config WifiConfiguration object corresponding to the network that needs to be
2639      *               checked for potential links.
2640      */
attemptNetworkLinking(WifiConfiguration config)2641     private void attemptNetworkLinking(WifiConfiguration config) {
2642         // Only link WPA_PSK config.
2643         if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) {
2644             return;
2645         }
2646         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2647         // Ignore configurations with large number of BSSIDs.
2648         if (scanDetailCache != null
2649                 && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2650             return;
2651         }
2652         for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
2653             if (linkConfig.getProfileKey().equals(config.getProfileKey())) {
2654                 continue;
2655             }
2656             if (linkConfig.ephemeral) {
2657                 continue;
2658             }
2659             if (!linkConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
2660                 continue;
2661             }
2662             // Network Selector will be allowed to dynamically jump from a linked configuration
2663             // to another, hence only link configurations that have WPA_PSK security type.
2664             if (!linkConfig.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) {
2665                 continue;
2666             }
2667             ScanDetailCache linkScanDetailCache =
2668                     getScanDetailCacheForNetwork(linkConfig.networkId);
2669             // Ignore configurations with large number of BSSIDs.
2670             if (linkScanDetailCache != null
2671                     && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2672                 continue;
2673             }
2674             // Check if the networks should be linked/unlinked.
2675             if (shouldNetworksBeLinked(
2676                     config, linkConfig, scanDetailCache, linkScanDetailCache)) {
2677                 linkNetworks(config, linkConfig);
2678             } else {
2679                 unlinkNetworks(config, linkConfig);
2680             }
2681         }
2682     }
2683 
2684     /**
2685      * Retrieves a list of all the saved hidden networks for scans
2686      *
2687      * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2688      * networks, this list will be truncated and we might end up not sending the networks
2689      * with the highest chance of connecting to the firmware.
2690      * So, re-sort the network list based on the frequency of connection to those networks
2691      * and whether it was last seen in the scan results.
2692      *
2693      * @return list of networks in the order of priority.
2694      */
retrieveHiddenNetworkList()2695     public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
2696         List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2697         List<WifiConfiguration> networks = getConfiguredNetworks();
2698         // Remove any non hidden networks.
2699         networks.removeIf(config -> !config.hiddenSSID);
2700         networks.sort(mScanListComparator);
2701         // The most frequently connected network has the highest priority now.
2702         for (WifiConfiguration config : networks) {
2703             hiddenList.add(new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
2704         }
2705         return hiddenList;
2706     }
2707 
2708     /**
2709      * Check if the provided network was temporarily disabled by the user and still blocked.
2710      *
2711      * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru
2712      *                this API matched the WifiConfiguration.SSID rules, and thus be surrounded by
2713      *                quotes.
2714      * @return true if network is blocking, otherwise false.
2715      */
isNetworkTemporarilyDisabledByUser(String network)2716     public boolean isNetworkTemporarilyDisabledByUser(String network) {
2717         if (mUserTemporarilyDisabledList.isLocked(network)) {
2718             return true;
2719         }
2720         mUserTemporarilyDisabledList.remove(network);
2721         return false;
2722     }
2723 
2724     /**
2725      * Check if the provided network should be disabled because it's a non-carrier-merged network.
2726      * @param config WifiConfiguration
2727      * @return true if the network is a non-carrier-merged network and it should be disabled,
2728      * otherwise false.
2729      */
isNonCarrierMergedNetworkTemporarilyDisabled( @onNull WifiConfiguration config)2730     public boolean isNonCarrierMergedNetworkTemporarilyDisabled(
2731             @NonNull WifiConfiguration config) {
2732         return mNonCarrierMergedNetworksStatusTracker.isNetworkDisabled(config);
2733     }
2734 
2735     /**
2736      * User temporarily disable a network and will be block to auto-join when network is still
2737      * nearby.
2738      *
2739      * The network will be re-enabled when:
2740      * a) User select to connect the network.
2741      * b) The network is not in range for {@link #USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS}
2742      * c) The maximum disable duration configured by
2743      * config_wifiAllNonCarrierMergedWifiMaxDisableDurationMinutes has passed.
2744      * d) Toggle wifi off, reset network settings or device reboot.
2745      *
2746      * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru
2747      *                this API matched the WifiConfiguration.SSID rules, and thus be surrounded by
2748      *                quotes.
2749      *        uid     UID of the calling process.
2750      */
userTemporarilyDisabledNetwork(String network, int uid)2751     public void userTemporarilyDisabledNetwork(String network, int uid) {
2752         int maxDisableDurationMinutes = mContext.getResources().getInteger(R.integer
2753                 .config_wifiAllNonCarrierMergedWifiMaxDisableDurationMinutes);
2754         mUserTemporarilyDisabledList.add(network, USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS,
2755                 maxDisableDurationMinutes * 60 * 1000);
2756         Log.d(TAG, "Temporarily disable network: " + network + " uid=" + uid + " num="
2757                 + mUserTemporarilyDisabledList.size() + ", maxDisableDurationMinutes:"
2758                 + maxDisableDurationMinutes);
2759         removeUserChoiceFromDisabledNetwork(network, uid);
2760         saveToStore(false);
2761     }
2762 
2763     /**
2764      * Temporarily disable visible and configured networks except for carrier merged networks for
2765      * the given subscriptionId.
2766      * @param subscriptionId
2767      */
startRestrictingAutoJoinToSubscriptionId(int subscriptionId)2768     public void startRestrictingAutoJoinToSubscriptionId(int subscriptionId) {
2769         int minDisableDurationMinutes = mContext.getResources().getInteger(R.integer
2770                 .config_wifiAllNonCarrierMergedWifiMinDisableDurationMinutes);
2771         int maxDisableDurationMinutes = mContext.getResources().getInteger(R.integer
2772                 .config_wifiAllNonCarrierMergedWifiMaxDisableDurationMinutes);
2773         localLog("startRestrictingAutoJoinToSubscriptionId: " + subscriptionId
2774                 + " minDisableDurationMinutes:" + minDisableDurationMinutes
2775                 + " maxDisableDurationMinutes:" + maxDisableDurationMinutes);
2776         long maxDisableDurationMs = maxDisableDurationMinutes * 60 * 1000;
2777         // do a clear to make sure we start at a clean state.
2778         mNonCarrierMergedNetworksStatusTracker.clear();
2779         mNonCarrierMergedNetworksStatusTracker.disableAllNonCarrierMergedNetworks(subscriptionId,
2780                 minDisableDurationMinutes * 60 * 1000,
2781                 maxDisableDurationMs);
2782         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2783             ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2784             if (scanDetailCache == null) {
2785                 continue;
2786             }
2787             ScanResult scanResult = scanDetailCache.getMostRecentScanResult();
2788             if (scanResult == null) {
2789                 continue;
2790             }
2791             if (mClock.getWallClockMillis() - scanResult.seen
2792                     < NON_CARRIER_MERGED_NETWORKS_SCAN_CACHE_QUERY_DURATION_MS) {
2793                 // do not disable if this is a carrier-merged-network with the given subscriptionId
2794                 if (config.carrierMerged && config.subscriptionId == subscriptionId) {
2795                     continue;
2796                 }
2797                 mNonCarrierMergedNetworksStatusTracker.temporarilyDisableNetwork(config,
2798                         USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS, maxDisableDurationMs);
2799             }
2800         }
2801     }
2802 
2803     /**
2804      * Resets the effects of startTemporarilyDisablngAllNonCarrierMergedWifi.
2805      */
stopRestrictingAutoJoinToSubscriptionId()2806     public void stopRestrictingAutoJoinToSubscriptionId() {
2807         mNonCarrierMergedNetworksStatusTracker.clear();
2808     }
2809 
2810     /**
2811      * Update the user temporarily disabled network list with networks in range.
2812      * @param networks networks in range in String format, FQDN or SSID. And caller must ensure
2813      *                 that the SSID passed thru this API matched the WifiConfiguration.SSID rules,
2814      *                 and thus be surrounded by quotes.
2815      */
updateUserDisabledList(List<String> networks)2816     public void updateUserDisabledList(List<String> networks) {
2817         mUserTemporarilyDisabledList.update(new HashSet<>(networks));
2818         mNonCarrierMergedNetworksStatusTracker.update(new HashSet<>(networks));
2819     }
2820 
removeUserChoiceFromDisabledNetwork( @onNull String network, int uid)2821     private void removeUserChoiceFromDisabledNetwork(
2822             @NonNull String network, int uid) {
2823         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2824             if (TextUtils.equals(config.SSID, network) || TextUtils.equals(config.FQDN, network)) {
2825                 if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
2826                     mWifiMetrics.logUserActionEvent(
2827                             UserActionEvent.EVENT_DISCONNECT_WIFI, config.networkId);
2828                 }
2829                 removeConnectChoiceFromAllNetworks(config.getProfileKey());
2830             }
2831         }
2832     }
2833 
2834     /**
2835      * User enabled network manually, maybe trigger by user select to connect network.
2836      * @param networkId enabled network id.
2837      * @return true if the operation succeeded, false otherwise.
2838      */
userEnabledNetwork(int networkId)2839     public boolean userEnabledNetwork(int networkId) {
2840         WifiConfiguration configuration = getInternalConfiguredNetwork(networkId);
2841         if (configuration == null) {
2842             return false;
2843         }
2844         final String network;
2845         if (configuration.isPasspoint()) {
2846             network = configuration.FQDN;
2847         } else {
2848             network = configuration.SSID;
2849         }
2850         mUserTemporarilyDisabledList.remove(network);
2851         mWifiBlocklistMonitor.clearBssidBlocklistForSsid(configuration.SSID);
2852         Log.d(TAG, "Enable disabled network: " + network + " num="
2853                 + mUserTemporarilyDisabledList.size());
2854         return true;
2855     }
2856 
2857     /**
2858      * Clear all user temporarily disabled networks.
2859      */
clearUserTemporarilyDisabledList()2860     public void clearUserTemporarilyDisabledList() {
2861         mUserTemporarilyDisabledList.clear();
2862     }
2863 
2864     /**
2865      * Resets all sim networks state.
2866      */
resetSimNetworks()2867     public void resetSimNetworks() {
2868         if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
2869         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2870             if (config.enterpriseConfig == null
2871                     || !config.enterpriseConfig.isAuthenticationSimBased()) {
2872                 continue;
2873             }
2874             if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) {
2875                 Pair<String, String> currentIdentity =
2876                         mWifiCarrierInfoManager.getSimIdentity(config);
2877                 if (mVerboseLoggingEnabled) {
2878                     Log.d(TAG, "New identity for config " + config + ": " + currentIdentity);
2879                 }
2880                 // Update the loaded config
2881                 if (currentIdentity == null) {
2882                     Log.d(TAG, "Identity is null");
2883                 } else {
2884                     config.enterpriseConfig.setIdentity(currentIdentity.first);
2885                 }
2886                 // do not reset anonymous identity since it may be dependent on user-entry
2887                 // (i.e. cannot re-request on every reboot/SIM re-entry)
2888             } else {
2889                 // reset identity as well: supplicant will ask us for it
2890                 config.enterpriseConfig.setIdentity("");
2891                 if (!WifiCarrierInfoManager.isAnonymousAtRealmIdentity(
2892                         config.enterpriseConfig.getAnonymousIdentity())) {
2893                     config.enterpriseConfig.setAnonymousIdentity("");
2894                 }
2895             }
2896         }
2897     }
2898 
2899     /**
2900      * Clear all ephemeral carrier networks, make the subscriptionId update during the next network
2901      * selection.
2902      */
removeEphemeralCarrierNetworks()2903     public void removeEphemeralCarrierNetworks() {
2904         if (mVerboseLoggingEnabled) localLog("removeEphemeralCarrierNetwork");
2905         WifiConfiguration[] copiedConfigs =
2906                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
2907         for (WifiConfiguration config : copiedConfigs) {
2908             if (!config.ephemeral
2909                     || config.subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
2910                 continue;
2911             }
2912             removeNetwork(config.networkId, config.creatorUid, config.creatorName);
2913         }
2914     }
2915 
2916     /**
2917      * Helper method to perform the following operations during user switch/unlock:
2918      * - Remove private networks of the old user.
2919      * - Load from the new user store file.
2920      * - Save the store files again to migrate any user specific networks from the shared store
2921      *   to user store.
2922      * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
2923      * should ensure that the stores are accessible before invocation.
2924      *
2925      * @param userId The identifier of the new foreground user, after the unlock or switch.
2926      */
handleUserUnlockOrSwitch(int userId)2927     private void handleUserUnlockOrSwitch(int userId) {
2928         if (mVerboseLoggingEnabled) {
2929             Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
2930         }
2931         // Switch out the user store file.
2932         if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
2933             saveToStore(true);
2934             mPendingUnlockStoreRead = false;
2935         }
2936     }
2937 
2938     /**
2939      * Handles the switch to a different foreground user:
2940      * - Flush the current state to the old user's store file.
2941      * - Switch the user specific store file.
2942      * - Reload the networks from the store files (shared & user).
2943      * - Write the store files to move any user specific private networks from shared store to user
2944      *   store.
2945      *
2946      * Need to be called when {@link com.android.server.SystemService#onUserSwitching} is invoked.
2947      *
2948      * @param userId The identifier of the new foreground user, after the switch.
2949      * @return List of network ID's of all the private networks of the old user which will be
2950      * removed from memory.
2951      */
handleUserSwitch(int userId)2952     public Set<Integer> handleUserSwitch(int userId) {
2953         if (mVerboseLoggingEnabled) {
2954             Log.v(TAG, "Handling user switch for " + userId);
2955         }
2956         if (userId == mCurrentUserId) {
2957             Log.w(TAG, "User already in foreground " + userId);
2958             return new HashSet<>();
2959         }
2960         if (mPendingStoreRead) {
2961             Log.w(TAG, "User switch before store is read!");
2962             mConfiguredNetworks.setNewUser(userId);
2963             mCurrentUserId = userId;
2964             // Reset any state from previous user unlock.
2965             mDeferredUserUnlockRead = false;
2966             // Cannot read data from new user's CE store file before they log-in.
2967             mPendingUnlockStoreRead = true;
2968             return new HashSet<>();
2969         }
2970         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
2971             saveToStore(true);
2972         }
2973         // Remove any private networks of the old user before switching the userId.
2974         Set<Integer> removedNetworkIds = clearInternalDataForCurrentUser();
2975         mConfiguredNetworks.setNewUser(userId);
2976         mCurrentUserId = userId;
2977 
2978         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
2979             handleUserUnlockOrSwitch(mCurrentUserId);
2980             // only handle the switching of unlocked users in {@link WifiCarrierInfoManager}.
2981             mWifiCarrierInfoManager.onUnlockedUserSwitching(mCurrentUserId);
2982         } else {
2983             // Cannot read data from new user's CE store file before they log-in.
2984             mPendingUnlockStoreRead = true;
2985             Log.i(TAG, "Waiting for user unlock to load from store");
2986         }
2987         return removedNetworkIds;
2988     }
2989 
2990     /**
2991      * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
2992      * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
2993      *
2994      * Need to be called when {@link com.android.server.SystemService#onUserUnlocking} is invoked.
2995      *
2996      * @param userId The identifier of the user that unlocked.
2997      */
handleUserUnlock(int userId)2998     public void handleUserUnlock(int userId) {
2999         if (mVerboseLoggingEnabled) {
3000             Log.v(TAG, "Handling user unlock for " + userId);
3001         }
3002         if (userId != mCurrentUserId) {
3003             Log.e(TAG, "Ignore user unlock for non current user " + userId);
3004             return;
3005         }
3006         if (mPendingStoreRead) {
3007             Log.w(TAG, "Ignore user unlock until store is read!");
3008             mDeferredUserUnlockRead = true;
3009             return;
3010         }
3011         if (mPendingUnlockStoreRead) {
3012             handleUserUnlockOrSwitch(mCurrentUserId);
3013         }
3014     }
3015 
3016     /**
3017      * Handles the stop of foreground user. This is needed to write the store file to flush
3018      * out any pending data before the user's CE store storage is unavailable.
3019      *
3020      * Need to be called when {@link com.android.server.SystemService#onUserStopping} is invoked.
3021      *
3022      * @param userId The identifier of the user that stopped.
3023      */
handleUserStop(int userId)3024     public void handleUserStop(int userId) {
3025         if (mVerboseLoggingEnabled) {
3026             Log.v(TAG, "Handling user stop for " + userId);
3027         }
3028         if (userId == mCurrentUserId
3029                 && mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
3030             saveToStore(true);
3031             clearInternalDataForCurrentUser();
3032         }
3033     }
3034 
3035     /**
3036      * Helper method to clear internal databases.
3037      * This method clears the:
3038      *  - List of configured networks.
3039      *  - Map of scan detail caches.
3040      *  - List of deleted ephemeral networks.
3041      */
clearInternalData()3042     private void clearInternalData() {
3043         localLog("clearInternalData: Clearing all internal data");
3044         mConfiguredNetworks.clear();
3045         mUserTemporarilyDisabledList.clear();
3046         mNonCarrierMergedNetworksStatusTracker.clear();
3047         mRandomizedMacAddressMapping.clear();
3048         mScanDetailCaches.clear();
3049         clearLastSelectedNetwork();
3050     }
3051 
3052     /**
3053      * Helper method to clear internal databases of the specified user.
3054      * This method clears the:
3055      *  - Private configured configured networks of the specified user.
3056      *  - Map of scan detail caches.
3057      *  - List of deleted ephemeral networks.
3058      *
3059      * @return List of network ID's of all the private networks of the old user which will be
3060      * removed from memory.
3061      */
clearInternalDataForCurrentUser()3062     private Set<Integer> clearInternalDataForCurrentUser() {
3063         localLog("clearInternalUserData: Clearing user internal data for " + mCurrentUserId);
3064         Set<Integer> removedNetworkIds = new HashSet<>();
3065         // Remove any private networks of the old user before switching the userId.
3066         for (WifiConfiguration config : getConfiguredNetworks()) {
3067             if ((!config.shared && !mWifiPermissionsUtil
3068                     .doesUidBelongToCurrentUser(config.creatorUid))
3069                     || config.ephemeral) {
3070                 removedNetworkIds.add(config.networkId);
3071                 localLog("clearInternalUserData: removed config."
3072                         + " netId=" + config.networkId
3073                         + " configKey=" + config.getProfileKey());
3074                 mConfiguredNetworks.remove(config.networkId);
3075                 for (OnNetworkUpdateListener listener : mListeners) {
3076                     listener.onNetworkRemoved(
3077                             createExternalWifiConfiguration(config, true, Process.WIFI_UID));
3078                 }
3079             }
3080         }
3081         if (!removedNetworkIds.isEmpty()) {
3082             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED);
3083         }
3084         mUserTemporarilyDisabledList.clear();
3085         mNonCarrierMergedNetworksStatusTracker.clear();
3086         mScanDetailCaches.clear();
3087         clearLastSelectedNetwork();
3088         return removedNetworkIds;
3089     }
3090 
3091     /**
3092      * Helper function to populate the internal (in-memory) data from the retrieved shared store
3093      * (file) data.
3094      *
3095      * @param configurations list of configurations retrieved from store.
3096      */
loadInternalDataFromSharedStore( List<WifiConfiguration> configurations, Map<String, String> macAddressMapping)3097     private void loadInternalDataFromSharedStore(
3098             List<WifiConfiguration> configurations,
3099             Map<String, String> macAddressMapping) {
3100         for (WifiConfiguration configuration : configurations) {
3101             if (!WifiConfigurationUtil.validate(
3102                     configuration, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
3103                 Log.e(TAG, "Skipping malformed network from shared store: " + configuration);
3104                 continue;
3105             }
3106 
3107             WifiConfiguration existingConfiguration = getInternalConfiguredNetwork(configuration);
3108             if (null != existingConfiguration) {
3109                 Log.d(TAG, "Merging network from shared store "
3110                         + configuration.getProfileKey());
3111                 mergeWithInternalWifiConfiguration(existingConfiguration, configuration);
3112                 continue;
3113             }
3114 
3115             configuration.networkId = mNextNetworkId++;
3116             WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary(configuration);
3117             if (mVerboseLoggingEnabled) {
3118                 Log.v(TAG, "Adding network from shared store "
3119                         + configuration.getProfileKey());
3120             }
3121             try {
3122                 mConfiguredNetworks.put(configuration);
3123             } catch (IllegalArgumentException e) {
3124                 Log.e(TAG, "Failed to add network to config map", e);
3125             }
3126         }
3127         mRandomizedMacAddressMapping.putAll(macAddressMapping);
3128     }
3129 
3130     /**
3131      * Helper function to populate the internal (in-memory) data from the retrieved user store
3132      * (file) data.
3133      *
3134      * @param configurations list of configurations retrieved from store.
3135      */
loadInternalDataFromUserStore(List<WifiConfiguration> configurations)3136     private void loadInternalDataFromUserStore(List<WifiConfiguration> configurations) {
3137         for (WifiConfiguration configuration : configurations) {
3138             if (!WifiConfigurationUtil.validate(
3139                     configuration, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
3140                 Log.e(TAG, "Skipping malformed network from user store: " + configuration);
3141                 continue;
3142             }
3143 
3144             WifiConfiguration existingConfiguration = getInternalConfiguredNetwork(configuration);
3145             if (null != existingConfiguration) {
3146                 Log.d(TAG, "Merging network from user store "
3147                         + configuration.getProfileKey());
3148                 mergeWithInternalWifiConfiguration(existingConfiguration, configuration);
3149                 continue;
3150             }
3151 
3152             configuration.networkId = mNextNetworkId++;
3153             WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary(configuration);
3154             if (mVerboseLoggingEnabled) {
3155                 Log.v(TAG, "Adding network from user store "
3156                         + configuration.getProfileKey());
3157             }
3158             try {
3159                 mConfiguredNetworks.put(configuration);
3160             } catch (IllegalArgumentException e) {
3161                 Log.e(TAG, "Failed to add network to config map", e);
3162             }
3163 
3164             if (configuration.isMostRecentlyConnected) {
3165                 mLruConnectionTracker.addNetwork(configuration);
3166             }
3167         }
3168     }
3169 
3170     /**
3171      * Initializes the randomized MAC address for an internal WifiConfiguration depending on
3172      * whether it should use enhanced randomization.
3173      * @param config
3174      */
initRandomizedMacForInternalConfig(WifiConfiguration internalConfig)3175     private void initRandomizedMacForInternalConfig(WifiConfiguration internalConfig) {
3176         MacAddress randomizedMac = shouldUseEnhancedRandomization(internalConfig)
3177                 ? MacAddressUtils.createRandomUnicastAddress()
3178                 : getPersistentMacAddress(internalConfig);
3179         if (randomizedMac != null) {
3180             setRandomizedMacAddress(internalConfig, randomizedMac);
3181         }
3182     }
3183 
3184     /**
3185      * Assign randomized MAC addresses for configured networks.
3186      * This is needed to generate persistent randomized MAC address for existing networks when
3187      * a device updates to Q+ for the first time since we are not calling addOrUpdateNetwork when
3188      * we load configuration at boot.
3189      */
generateRandomizedMacAddresses()3190     private void generateRandomizedMacAddresses() {
3191         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3192             if (DEFAULT_MAC_ADDRESS.equals(config.getRandomizedMacAddress())) {
3193                 initRandomizedMacForInternalConfig(config);
3194             }
3195         }
3196     }
3197 
3198     /**
3199      * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
3200      * data.
3201      * This method:
3202      * 1. Clears all existing internal data.
3203      * 2. Sends out the networks changed broadcast after loading all the data.
3204      *
3205      * @param sharedConfigurations list of network configurations retrieved from shared store.
3206      * @param userConfigurations list of network configurations retrieved from user store.
3207      * @param macAddressMapping
3208      */
loadInternalData( List<WifiConfiguration> sharedConfigurations, List<WifiConfiguration> userConfigurations, Map<String, String> macAddressMapping)3209     private void loadInternalData(
3210             List<WifiConfiguration> sharedConfigurations,
3211             List<WifiConfiguration> userConfigurations,
3212             Map<String, String> macAddressMapping) {
3213         // Clear out all the existing in-memory lists and load the lists from what was retrieved
3214         // from the config store.
3215         clearInternalData();
3216         loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping);
3217         loadInternalDataFromUserStore(userConfigurations);
3218         generateRandomizedMacAddresses();
3219         if (mConfiguredNetworks.sizeForAllUsers() == 0) {
3220             Log.w(TAG, "No stored networks found.");
3221         }
3222         // reset identity & anonymous identity for networks using SIM-based authentication
3223         // on load (i.e. boot) so that if the user changed SIMs while the device was powered off,
3224         // we do not reuse stale credentials that would lead to authentication failure.
3225         resetSimNetworks();
3226         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_ADDED);
3227         mPendingStoreRead = false;
3228     }
3229 
3230     /**
3231      * Helper method to handle any config store errors on user builds vs other debuggable builds.
3232      */
handleConfigStoreFailure(boolean onlyUserStore)3233     private boolean handleConfigStoreFailure(boolean onlyUserStore) {
3234         // On eng/userdebug builds, return failure to leave the device in a debuggable state.
3235         if (!mBuildProperties.isUserBuild()) return false;
3236 
3237         // On user builds, ignore the failure and let the user create new networks.
3238         Log.w(TAG, "Ignoring config store errors on user build");
3239         if (!onlyUserStore) {
3240             loadInternalData(Collections.emptyList(), Collections.emptyList(),
3241                     Collections.emptyMap());
3242         } else {
3243             loadInternalDataFromUserStore(Collections.emptyList());
3244         }
3245         return true;
3246     }
3247 
3248     /**
3249      * Read the config store and load the in-memory lists from the store data retrieved and sends
3250      * out the networks changed broadcast.
3251      *
3252      * This reads all the network configurations from:
3253      * 1. Shared WifiConfigStore.xml
3254      * 2. User WifiConfigStore.xml
3255      *
3256      * @return true on success or not needed (fresh install), false otherwise.
3257      */
loadFromStore()3258     public boolean loadFromStore() {
3259         // If the user unlock comes in before we load from store, which means the user store have
3260         // not been setup yet for the current user. Setup the user store before the read so that
3261         // configurations for the current user will also being loaded.
3262         if (mDeferredUserUnlockRead) {
3263             Log.i(TAG, "Handling user unlock before loading from store.");
3264             List<WifiConfigStore.StoreFile> userStoreFiles =
3265                     WifiConfigStore.createUserFiles(
3266                             mCurrentUserId, mFrameworkFacade.isNiapModeOn(mContext));
3267             if (userStoreFiles == null) {
3268                 Log.wtf(TAG, "Failed to create user store files");
3269                 return false;
3270             }
3271             mWifiConfigStore.setUserStores(userStoreFiles);
3272             mDeferredUserUnlockRead = false;
3273         }
3274         try {
3275             mWifiConfigStore.read();
3276         } catch (IOException | IllegalStateException e) {
3277             Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
3278             return handleConfigStoreFailure(false);
3279         } catch (XmlPullParserException e) {
3280             Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
3281             return handleConfigStoreFailure(false);
3282         }
3283         loadInternalData(mNetworkListSharedStoreData.getConfigurations(),
3284                 mNetworkListUserStoreData.getConfigurations(),
3285                 mRandomizedMacStoreData.getMacMapping());
3286         return true;
3287     }
3288 
3289     /**
3290      * Read the user config store and load the in-memory lists from the store data retrieved and
3291      * sends out the networks changed broadcast.
3292      * This should be used for all user switches/unlocks to only load networks from the user
3293      * specific store and avoid reloading the shared networks.
3294      *
3295      * This reads all the network configurations from:
3296      * 1. User WifiConfigStore.xml
3297      *
3298      * @param userId The identifier of the foreground user.
3299      * @return true on success, false otherwise.
3300      */
loadFromUserStoreAfterUnlockOrSwitch(int userId)3301     private boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
3302         try {
3303             List<WifiConfigStore.StoreFile> userStoreFiles =
3304                     WifiConfigStore.createUserFiles(
3305                             userId, mFrameworkFacade.isNiapModeOn(mContext));
3306             if (userStoreFiles == null) {
3307                 Log.e(TAG, "Failed to create user store files");
3308                 return false;
3309             }
3310             mWifiConfigStore.switchUserStoresAndRead(userStoreFiles);
3311         } catch (IOException | IllegalStateException e) {
3312             Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
3313             return handleConfigStoreFailure(true);
3314         } catch (XmlPullParserException e) {
3315             Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are "
3316                     + "lost!", e);
3317             return handleConfigStoreFailure(true);
3318         }
3319         loadInternalDataFromUserStore(mNetworkListUserStoreData.getConfigurations());
3320         return true;
3321     }
3322 
3323     /**
3324      * Save the current snapshot of the in-memory lists to the config store.
3325      *
3326      * @param forceWrite Whether the write needs to be forced or not.
3327      * @return Whether the write was successful or not, this is applicable only for force writes.
3328      */
saveToStore(boolean forceWrite)3329     public boolean saveToStore(boolean forceWrite) {
3330         if (mPendingStoreRead) {
3331             Log.e(TAG, "Cannot save to store before store is read!");
3332             return false;
3333         }
3334         ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
3335         ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
3336         // List of network IDs for legacy Passpoint configuration to be removed.
3337         List<Integer> legacyPasspointNetId = new ArrayList<>();
3338         for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
3339             // Ignore ephemeral networks and non-legacy Passpoint configurations.
3340             if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
3341                 continue;
3342             }
3343 
3344             // Migrate the legacy Passpoint configurations owned by the current user to
3345             // {@link PasspointManager}.
3346             if (config.isLegacyPasspointConfig && !mWifiPermissionsUtil
3347                     .doesUidBelongToCurrentUser(config.creatorUid)) {
3348                 legacyPasspointNetId.add(config.networkId);
3349                 // Migrate the legacy Passpoint configuration and add it to PasspointManager.
3350                 if (!PasspointManager.addLegacyPasspointConfig(config)) {
3351                     Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
3352                 }
3353                 // This will prevent adding |config| to the |sharedConfigurations|.
3354                 continue;
3355             }
3356 
3357             config.isMostRecentlyConnected =
3358                     mLruConnectionTracker.isMostRecentlyConnected(config);
3359 
3360             // We push all shared networks & private networks not belonging to the current
3361             // user to the shared store. Ideally, private networks for other users should
3362             // not even be in memory,
3363             // But, this logic is in place to deal with store migration from N to O
3364             // because all networks were previously stored in a central file. We cannot
3365             // write these private networks to the user specific store until the corresponding
3366             // user logs in.
3367             if (config.shared || !mWifiPermissionsUtil
3368                     .doesUidBelongToCurrentUser(config.creatorUid)) {
3369                 sharedConfigurations.add(config);
3370             } else {
3371                 userConfigurations.add(config);
3372             }
3373         }
3374 
3375         // Remove the configurations for migrated Passpoint configurations.
3376         for (int networkId : legacyPasspointNetId) {
3377             mConfiguredNetworks.remove(networkId);
3378         }
3379 
3380         // Setup store data for write.
3381         mNetworkListSharedStoreData.setConfigurations(sharedConfigurations);
3382         mNetworkListUserStoreData.setConfigurations(userConfigurations);
3383         mRandomizedMacStoreData.setMacMapping(mRandomizedMacAddressMapping);
3384 
3385         try {
3386             mWifiConfigStore.write(forceWrite);
3387         } catch (IOException | IllegalStateException e) {
3388             Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
3389             return false;
3390         } catch (XmlPullParserException e) {
3391             Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
3392             return false;
3393         }
3394         return true;
3395     }
3396 
3397     /**
3398      * Helper method for logging into local log buffer.
3399      */
localLog(String s)3400     private void localLog(String s) {
3401         if (mLocalLog != null) {
3402             mLocalLog.log(s);
3403         }
3404     }
3405 
3406     /**
3407      * Dump the local log buffer and other internal state of WifiConfigManager.
3408      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)3409     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3410         pw.println("Dump of WifiConfigManager");
3411         pw.println("WifiConfigManager - Log Begin ----");
3412         mLocalLog.dump(fd, pw, args);
3413         pw.println("WifiConfigManager - Log End ----");
3414         pw.println("WifiConfigManager - Configured networks Begin ----");
3415         for (WifiConfiguration network : getInternalConfiguredNetworks()) {
3416             pw.println(network);
3417         }
3418         pw.println("WifiConfigManager - Configured networks End ----");
3419         pw.println("WifiConfigManager - ConfigurationMap Begin ----");
3420         mConfiguredNetworks.dump(fd, pw, args);
3421         pw.println("WifiConfigManager - ConfigurationMap End ----");
3422         pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
3423         pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
3424         pw.println("WifiConfigManager - PNO scan frequency culling enabled = "
3425                 + mContext.getResources().getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled));
3426         pw.println("WifiConfigManager - PNO scan recency sorting enabled = "
3427                 + mContext.getResources().getBoolean(R.bool.config_wifiPnoRecencySortingEnabled));
3428         mWifiConfigStore.dump(fd, pw, args);
3429         mWifiCarrierInfoManager.dump(fd, pw, args);
3430         mNonCarrierMergedNetworksStatusTracker.dump(fd, pw, args);
3431     }
3432 
3433     /**
3434      * Returns true if the given uid has permission to add, update or remove proxy settings
3435      */
canModifyProxySettings(int uid, String packageName)3436     private boolean canModifyProxySettings(int uid, String packageName) {
3437         final boolean isDeviceOwner = mWifiPermissionsUtil.isDeviceOwner(uid, packageName);
3438         final boolean isProfileOwner = mWifiPermissionsUtil.isProfileOwner(uid, packageName);
3439         final boolean hasNetworkSettingsPermission =
3440                 mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
3441         final boolean hasNetworkSetupWizardPermission =
3442                 mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid);
3443         final boolean hasNetworkManagedProvisioningPermission =
3444                 mWifiPermissionsUtil.checkNetworkManagedProvisioningPermission(uid);
3445         // If |uid| corresponds to the device owner, allow all modifications.
3446         if (isProfileOwner || isDeviceOwner || hasNetworkSettingsPermission
3447                 || hasNetworkSetupWizardPermission || hasNetworkManagedProvisioningPermission) {
3448             return true;
3449         }
3450         if (mVerboseLoggingEnabled) {
3451             Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
3452                     + " hasNetworkSettings=" + hasNetworkSettingsPermission
3453                     + " hasNetworkSetupWizard=" + hasNetworkSetupWizardPermission
3454                     + " DeviceOwner=" + isDeviceOwner
3455                     + " ProfileOwner=" + isProfileOwner);
3456         }
3457         return false;
3458     }
3459 
3460     /**
3461      * Add the network update event listener
3462      */
addOnNetworkUpdateListener(OnNetworkUpdateListener listener)3463     public void addOnNetworkUpdateListener(OnNetworkUpdateListener listener) {
3464         mListeners.add(listener);
3465     }
3466 
3467     /**
3468      * Remove the network update event listener
3469      */
removeOnNetworkUpdateListener(OnNetworkUpdateListener listener)3470     public void removeOnNetworkUpdateListener(OnNetworkUpdateListener listener) {
3471         mListeners.remove(listener);
3472     }
3473 
3474     /**
3475      * Set extra failure reason for given config. Used to surface extra failure details to the UI
3476      * @param netId The network ID of the config to set the extra failure reason for
3477      * @param reason the WifiConfiguration.ExtraFailureReason failure code representing the most
3478      *               recent failure reason
3479      */
setRecentFailureAssociationStatus(int netId, int reason)3480     public void setRecentFailureAssociationStatus(int netId, int reason) {
3481         WifiConfiguration config = getInternalConfiguredNetwork(netId);
3482         if (config == null) {
3483             return;
3484         }
3485         mWifiMetrics.incrementRecentFailureAssociationStatusCount(reason);
3486         int previousReason = config.recentFailure.getAssociationStatus();
3487         config.recentFailure.setAssociationStatus(reason, mClock.getElapsedSinceBootMillis());
3488         if (previousReason != reason) {
3489             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
3490         }
3491     }
3492 
3493     /**
3494      * @param netId The network ID of the config to clear the extra failure reason from
3495      */
clearRecentFailureReason(int netId)3496     public void clearRecentFailureReason(int netId) {
3497         WifiConfiguration config = getInternalConfiguredNetwork(netId);
3498         if (config == null) {
3499             return;
3500         }
3501         config.recentFailure.clear();
3502     }
3503 
3504     /**
3505      * Clear all recent failure reasons that have timed out.
3506      */
cleanupExpiredRecentFailureReasons()3507     public void cleanupExpiredRecentFailureReasons() {
3508         long timeoutDuration = mContext.getResources().getInteger(
3509                 R.integer.config_wifiRecentFailureReasonExpirationMinutes) * 60 * 1000;
3510         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3511             if (config.recentFailure.getAssociationStatus()
3512                     != WifiConfiguration.RECENT_FAILURE_NONE
3513                     && mClock.getElapsedSinceBootMillis()
3514                     >= config.recentFailure.getLastUpdateTimeSinceBootMillis() + timeoutDuration) {
3515                 config.recentFailure.clear();
3516                 sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
3517             }
3518         }
3519     }
3520 
3521     /**
3522      * Find the highest RSSI among all valid scanDetails in current network's scanDetail cache.
3523      * If scanDetail is too old, it is not considered to be valid.
3524      * @param netId The network ID of the config to find scan RSSI
3525      * @params scanRssiValidTimeMs The valid time for scan RSSI
3526      * @return The highest RSSI in dBm found with current network's scanDetail cache.
3527      */
findScanRssi(int netId, int scanRssiValidTimeMs)3528     public int findScanRssi(int netId, int scanRssiValidTimeMs) {
3529         int scanMaxRssi = WifiInfo.INVALID_RSSI;
3530         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(netId);
3531         if (scanDetailCache == null || scanDetailCache.size() == 0) return scanMaxRssi;
3532         long nowInMillis = mClock.getWallClockMillis();
3533         for (ScanDetail scanDetail : scanDetailCache.values()) {
3534             ScanResult result = scanDetail.getScanResult();
3535             if (result == null) continue;
3536             boolean valid = (nowInMillis - result.seen) < scanRssiValidTimeMs;
3537 
3538             if (valid) {
3539                 scanMaxRssi = Math.max(scanMaxRssi, result.level);
3540             }
3541         }
3542         return scanMaxRssi;
3543     }
3544 
3545     public Comparator<WifiConfiguration> getScanListComparator() {
3546         return mScanListComparator;
3547     }
3548 
3549     /**
3550      * This API is called when a connection successfully completes on an existing network
3551      * selected by the user. It is not called after the first connection of a newly added network.
3552      * Following actions will be triggered:
3553      * 1. If this network is disabled, we need re-enable it again.
3554      * 2. This network is favored over all the other networks visible in latest network
3555      * selection procedure.
3556      *
3557      * @param netId ID for the network chosen by the user
3558      * @param rssi the signal strength of the user selected network
3559      * @return true -- There is change made to connection choice of any saved network.
3560      * false -- There is no change made to connection choice of any saved network.
3561      */
3562     private boolean setUserConnectChoice(int netId, int rssi) {
3563         localLog("userSelectNetwork: network ID=" + netId);
3564         WifiConfiguration selected = getInternalConfiguredNetwork(netId);
3565 
3566         if (selected == null || selected.getProfileKey() == null) {
3567             localLog("userSelectNetwork: Invalid configuration with nid=" + netId);
3568             return false;
3569         }
3570 
3571         // Enable the network if it is disabled.
3572         if (!selected.getNetworkSelectionStatus().isNetworkEnabled()) {
3573             updateNetworkSelectionStatus(selected,
3574                     WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE);
3575         }
3576         boolean changed = setLegacyUserConnectChoice(selected, rssi);
3577         return changed;
3578     }
3579 
3580     /**
3581      * This maintains the legacy user connect choice state in the config store
3582      */
3583     private boolean setLegacyUserConnectChoice(@NonNull final WifiConfiguration selected,
3584             int rssi) {
3585         boolean change = false;
3586         Collection<WifiConfiguration> configuredNetworks = getInternalConfiguredNetworks();
3587         ArrayList<WifiConfiguration> networksInRange = new ArrayList<>();
3588         String key = selected.getProfileKey();
3589         for (WifiConfiguration network : configuredNetworks) {
3590             WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
3591             if (network.networkId == selected.networkId) {
3592                 if (status.getConnectChoice() != null) {
3593                     localLog("Remove user selection preference of " + status.getConnectChoice()
3594                             + " from " + network.SSID + " : " + network.networkId);
3595                     clearConnectChoiceInternal(network);
3596                     change = true;
3597                 }
3598                 continue;
3599             }
3600 
3601             if (status.getSeenInLastQualifiedNetworkSelection()) {
3602                 setConnectChoiceInternal(network, key, rssi);
3603                 change = true;
3604                 networksInRange.add(network);
3605             }
3606         }
3607 
3608         for (OnNetworkUpdateListener listener : mListeners) {
3609             listener.onConnectChoiceSet(networksInRange, key, rssi);
3610         }
3611         return change;
3612     }
3613 
3614     private void clearConnectChoiceInternal(WifiConfiguration config) {
3615         config.getNetworkSelectionStatus().setConnectChoice(null);
3616         config.getNetworkSelectionStatus().setConnectChoiceRssi(0);
3617     }
3618 
3619     private void setConnectChoiceInternal(WifiConfiguration config, String key, int rssi) {
3620         config.getNetworkSelectionStatus().setConnectChoice(key);
3621         config.getNetworkSelectionStatus().setConnectChoiceRssi(rssi);
3622         localLog("Add connect choice key: " + key + " rssi: " + rssi + " to "
3623                 + WifiNetworkSelector.toNetworkString(config));
3624     }
3625 
3626     /** Update WifiConfigManager before connecting to a network. */
3627     public void updateBeforeConnect(int networkId, int callingUid) {
3628         userEnabledNetwork(networkId);
3629         if (!enableNetwork(networkId, true, callingUid, null)
3630                 || !updateLastConnectUid(networkId, callingUid)) {
3631             Log.i(TAG, "connect Allowing uid " + callingUid
3632                     + " with insufficient permissions to connect=" + networkId);
3633         }
3634     }
3635 
3636     /** See {@link WifiManager#save(WifiConfiguration, WifiManager.ActionListener)} */
3637     public NetworkUpdateResult updateBeforeSaveNetwork(WifiConfiguration config, int callingUid) {
3638         NetworkUpdateResult result = addOrUpdateNetwork(config, callingUid);
3639         if (!result.isSuccess()) {
3640             Log.e(TAG, "saveNetwork adding/updating config=" + config + " failed");
3641             return result;
3642         }
3643         if (!enableNetwork(result.getNetworkId(), false, callingUid, null)) {
3644             Log.e(TAG, "saveNetwork enabling config=" + config + " failed");
3645             return NetworkUpdateResult.makeFailed();
3646         }
3647         return result;
3648     }
3649 
3650     /**
3651      * Gets the most recent scan result that is newer than maxAgeMillis for each configured network.
3652      * @param maxAgeMillis scan results older than this parameter will get filtered out.
3653      */
3654     public @NonNull List<ScanResult> getMostRecentScanResultsForConfiguredNetworks(
3655             int maxAgeMillis) {
3656         List<ScanResult> results = new ArrayList<>();
3657         long timeNowMs = mClock.getWallClockMillis();
3658         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3659             ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
3660             if (scanDetailCache == null) {
3661                 continue;
3662             }
3663             ScanResult scanResult = scanDetailCache.getMostRecentScanResult();
3664             if (scanResult == null) {
3665                 continue;
3666             }
3667             if (timeNowMs - scanResult.seen < maxAgeMillis) {
3668                 results.add(scanResult);
3669             }
3670         }
3671         return results;
3672     }
3673 
3674     /**
3675      * Update the configuration according to transition disable indications.
3676      *
3677      * @param networkId network ID corresponding to the network.
3678      * @param indicationBit transition disable indication bits.
3679      * @return true if the network was found, false otherwise.
3680      */
3681     public boolean updateNetworkTransitionDisable(
3682             int networkId,
3683             @WifiMonitor.TransitionDisableIndication int indicationBit) {
3684         localLog("updateNetworkTransitionDisable: network ID=" + networkId
3685                 + " indication: " + indicationBit);
3686         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
3687         if (config == null) {
3688             Log.e(TAG, "Cannot find network for " + networkId);
3689             return false;
3690         }
3691         if (0 != (indicationBit & WifiMonitor.TDI_USE_WPA3_PERSONAL)
3692                 && config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) {
3693             config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_PSK, false);
3694         }
3695         if (0 != (indicationBit & WifiMonitor.TDI_USE_SAE_PK)) {
3696             config.enableSaePkOnlyMode(true);
3697         }
3698         if (0 != (indicationBit & WifiMonitor.TDI_USE_WPA3_ENTERPRISE)
3699                 && config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) {
3700             config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_EAP, false);
3701         }
3702         if (0 != (indicationBit & WifiMonitor.TDI_USE_ENHANCED_OPEN)
3703                 && config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) {
3704             config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_OPEN, false);
3705         }
3706         return true;
3707     }
3708 
3709     /**
3710      * Retrieves the configured network corresponding to the provided configKey
3711      * without any masking.
3712      *
3713      * WARNING: Don't use this to pass network configurations except in the wifi stack, when
3714      * there is a need for passwords and randomized MAC address.
3715      *
3716      * @param configKey configKey of the requested network.
3717      * @return Copy of WifiConfiguration object if found, null otherwise.
3718      */
3719     private WifiConfiguration getConfiguredNetworkWithoutMasking(String configKey) {
3720         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
3721         if (config == null) {
3722             return null;
3723         }
3724         return new WifiConfiguration(config);
3725     }
3726 
3727     /**
3728      * This method links the config of the provided network id to every linkable saved network.
3729      *
3730      * @param networkId networkId corresponding to the network to be potentially linked.
3731      */
3732     public void updateLinkedNetworks(int networkId) {
3733         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
3734         if (internalConfig == null) {
3735             return;
3736         }
3737         internalConfig.linkedConfigurations = new HashMap<>();
3738         attemptNetworkLinking(internalConfig);
3739     }
3740 
3741     /**
3742      * This method returns a map containing each config key and unmasked WifiConfiguration of every
3743      * network linked to the provided network id.
3744      * @param networkId networkId to get the linked configs of.
3745      * @return HashMap of config key to unmasked WifiConfiguration
3746      */
3747     public Map<String, WifiConfiguration> getLinkedNetworksWithoutMasking(int networkId) {
3748         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
3749         if (internalConfig == null) {
3750             return null;
3751         }
3752 
3753         Map<String, WifiConfiguration> linkedNetworks = new HashMap<>();
3754         Map<String, Integer> linkedConfigurations = internalConfig.linkedConfigurations;
3755         if (linkedConfigurations == null) {
3756             return null;
3757         }
3758         for (String configKey : linkedConfigurations.keySet()) {
3759             linkedNetworks.put(configKey, getConfiguredNetworkWithoutMasking(configKey));
3760         }
3761         return linkedNetworks;
3762     }
3763 
3764     /**
3765      * This method updates FILS AKMs to the internal network.
3766      *
3767      * @param networkId networkId corresponding to the network to be updated.
3768      */
3769     public void updateFilsAkms(int networkId,
3770             boolean isFilsSha256Supported, boolean isFilsSha384Supported) {
3771         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
3772         if (internalConfig == null) {
3773             return;
3774         }
3775         internalConfig.enableFils(isFilsSha256Supported, isFilsSha384Supported);
3776     }
3777 }
3778