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