• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import 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      * Copy over public elements from an external WifiConfiguration object to the internal
917      * configuration object if element has been set in the provided external WifiConfiguration.
918      * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
919      * for every update.
920      *
921      * This method updates all elements that are common to both network addition & update.
922      * The following fields of {@link WifiConfiguration} are not copied from external configs:
923      *  > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
924      *  > status - The status needs to be explicitly updated using
925      *             {@link WifiManager#enableNetwork(int, boolean)} or
926      *             {@link WifiManager#disableNetwork(int)}.
927      *
928      * @param internalConfig WifiConfiguration object in our internal map.
929      * @param externalConfig WifiConfiguration object provided from the external API.
930      */
mergeWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)931     private void mergeWithInternalWifiConfiguration(
932             WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
933         if (externalConfig.SSID != null) {
934             internalConfig.SSID = externalConfig.SSID;
935         }
936         if (externalConfig.BSSID != null) {
937             internalConfig.BSSID = externalConfig.BSSID.toLowerCase();
938         }
939         internalConfig.hiddenSSID = externalConfig.hiddenSSID;
940         internalConfig.requirePmf = externalConfig.requirePmf;
941 
942         if (externalConfig.preSharedKey != null
943                 && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
944             internalConfig.preSharedKey = externalConfig.preSharedKey;
945         }
946         // Modify only wep keys are present in the provided configuration. This is a little tricky
947         // because there is no easy way to tell if the app is actually trying to null out the
948         // existing keys or not.
949         if (externalConfig.wepKeys != null) {
950             boolean hasWepKey = false;
951             for (int i = 0; i < internalConfig.wepKeys.length; i++) {
952                 if (externalConfig.wepKeys[i] != null
953                         && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
954                     internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
955                     hasWepKey = true;
956                 }
957             }
958             if (hasWepKey) {
959                 internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
960             }
961         }
962         if (externalConfig.FQDN != null) {
963             internalConfig.FQDN = externalConfig.FQDN;
964         }
965         if (externalConfig.providerFriendlyName != null) {
966             internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
967         }
968         if (externalConfig.roamingConsortiumIds != null) {
969             internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone();
970         }
971 
972         // Copy over all the auth/protocol/key mgmt parameters if set.
973         if (externalConfig.allowedAuthAlgorithms != null
974                 && !externalConfig.allowedAuthAlgorithms.isEmpty()) {
975             internalConfig.allowedAuthAlgorithms =
976                     (BitSet) externalConfig.allowedAuthAlgorithms.clone();
977         }
978         if (externalConfig.allowedProtocols != null
979                 && !externalConfig.allowedProtocols.isEmpty()) {
980             internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone();
981         }
982         if (externalConfig.allowedKeyManagement != null
983                 && !externalConfig.allowedKeyManagement.isEmpty()) {
984             internalConfig.allowedKeyManagement =
985                     (BitSet) externalConfig.allowedKeyManagement.clone();
986         }
987         if (externalConfig.allowedPairwiseCiphers != null
988                 && !externalConfig.allowedPairwiseCiphers.isEmpty()) {
989             internalConfig.allowedPairwiseCiphers =
990                     (BitSet) externalConfig.allowedPairwiseCiphers.clone();
991         }
992         if (externalConfig.allowedGroupCiphers != null
993                 && !externalConfig.allowedGroupCiphers.isEmpty()) {
994             internalConfig.allowedGroupCiphers =
995                     (BitSet) externalConfig.allowedGroupCiphers.clone();
996         }
997         if (externalConfig.allowedGroupManagementCiphers != null
998                 && !externalConfig.allowedGroupManagementCiphers.isEmpty()) {
999             internalConfig.allowedGroupManagementCiphers =
1000                     (BitSet) externalConfig.allowedGroupManagementCiphers.clone();
1001         }
1002         // allowedSuiteBCiphers is set internally according to the certificate type
1003 
1004         // Copy over the |IpConfiguration| parameters if set.
1005         if (externalConfig.getIpConfiguration() != null) {
1006             IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
1007             if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
1008                 internalConfig.setIpAssignment(ipAssignment);
1009                 if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
1010                     internalConfig.setStaticIpConfiguration(
1011                             new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
1012                 }
1013             }
1014             IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
1015             if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
1016                 internalConfig.setProxySettings(proxySettings);
1017                 if (proxySettings == IpConfiguration.ProxySettings.PAC
1018                         || proxySettings == IpConfiguration.ProxySettings.STATIC) {
1019                     internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
1020                 }
1021             }
1022         }
1023 
1024         // Copy over the |WifiEnterpriseConfig| parameters if set.
1025         if (externalConfig.enterpriseConfig != null) {
1026             internalConfig.enterpriseConfig.copyFromExternal(
1027                     externalConfig.enterpriseConfig, PASSWORD_MASK);
1028         }
1029 
1030         // Copy over any metered information.
1031         internalConfig.meteredHint = externalConfig.meteredHint;
1032         internalConfig.meteredOverride = externalConfig.meteredOverride;
1033 
1034         // Copy trusted bit
1035         internalConfig.trusted = externalConfig.trusted;
1036 
1037         // Copy over macRandomizationSetting
1038         internalConfig.macRandomizationSetting = externalConfig.macRandomizationSetting;
1039         internalConfig.carrierId = externalConfig.carrierId;
1040         internalConfig.isHomeProviderNetwork = externalConfig.isHomeProviderNetwork;
1041     }
1042 
1043     /**
1044      * Set all the exposed defaults in the newly created WifiConfiguration object.
1045      * These fields have a default value advertised in our public documentation. The only exception
1046      * is the hidden |IpConfiguration| parameters, these have a default value even though they're
1047      * hidden.
1048      *
1049      * @param configuration provided WifiConfiguration object.
1050      */
setDefaultsInWifiConfiguration(WifiConfiguration configuration)1051     private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
1052         configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
1053         configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
1054 
1055         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
1056         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
1057 
1058         configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
1059         configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
1060         configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
1061 
1062         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
1063         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
1064         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
1065         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
1066         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
1067 
1068         configuration.allowedGroupManagementCiphers
1069                 .set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
1070 
1071         configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
1072         configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
1073 
1074         configuration.status = WifiConfiguration.Status.DISABLED;
1075         configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
1076                 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1077         configuration.getNetworkSelectionStatus().setNetworkSelectionDisableReason(
1078                 NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
1079     }
1080 
1081     /**
1082      * Create a new internal WifiConfiguration object by copying over parameters from the provided
1083      * external configuration and set defaults for the appropriate parameters.
1084      *
1085      * @param externalConfig WifiConfiguration object provided from the external API.
1086      * @return New WifiConfiguration object with parameters merged from the provided external
1087      * configuration.
1088      */
createNewInternalWifiConfigurationFromExternal( WifiConfiguration externalConfig, int uid, @Nullable String packageName)1089     private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
1090             WifiConfiguration externalConfig, int uid, @Nullable String packageName) {
1091         WifiConfiguration newInternalConfig = new WifiConfiguration();
1092 
1093         // First allocate a new network ID for the configuration.
1094         newInternalConfig.networkId = mNextNetworkId++;
1095 
1096         // First set defaults in the new configuration created.
1097         setDefaultsInWifiConfiguration(newInternalConfig);
1098 
1099         // Copy over all the public elements from the provided configuration.
1100         mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
1101 
1102         // Copy over the hidden configuration parameters. These are the only parameters used by
1103         // system apps to indicate some property about the network being added.
1104         // These are only copied over for network additions and ignored for network updates.
1105         newInternalConfig.requirePmf = externalConfig.requirePmf;
1106         newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
1107         newInternalConfig.ephemeral = externalConfig.ephemeral;
1108         newInternalConfig.osu = externalConfig.osu;
1109         newInternalConfig.fromWifiNetworkSuggestion = externalConfig.fromWifiNetworkSuggestion;
1110         newInternalConfig.fromWifiNetworkSpecifier = externalConfig.fromWifiNetworkSpecifier;
1111         newInternalConfig.useExternalScores = externalConfig.useExternalScores;
1112         newInternalConfig.shared = externalConfig.shared;
1113         newInternalConfig.updateIdentifier = externalConfig.updateIdentifier;
1114         newInternalConfig.setPasspointUniqueId(externalConfig.getPasspointUniqueId());
1115 
1116         // Add debug information for network addition.
1117         newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
1118         newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
1119                 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid);
1120         initRandomizedMacForInternalConfig(newInternalConfig);
1121         return newInternalConfig;
1122     }
1123 
1124     /**
1125      * Create a new internal WifiConfiguration object by copying over parameters from the provided
1126      * external configuration to a copy of the existing internal WifiConfiguration object.
1127      *
1128      * @param internalConfig WifiConfiguration object in our internal map.
1129      * @param externalConfig WifiConfiguration object provided from the external API.
1130      * @return Copy of existing WifiConfiguration object with parameters merged from the provided
1131      * configuration.
1132      */
updateExistingInternalWifiConfigurationFromExternal( WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid, @Nullable String packageName)1133     private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
1134             WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid,
1135             @Nullable String packageName) {
1136         WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
1137 
1138         // Copy over all the public elements from the provided configuration.
1139         mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
1140 
1141         // Add debug information for network update.
1142         newInternalConfig.lastUpdateUid = uid;
1143         newInternalConfig.lastUpdateName =
1144                 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid);
1145 
1146         return newInternalConfig;
1147     }
1148 
logUserActionEvents(WifiConfiguration before, WifiConfiguration after)1149     private void logUserActionEvents(WifiConfiguration before, WifiConfiguration after) {
1150         // Logs changes in meteredOverride.
1151         if (before.meteredOverride != after.meteredOverride) {
1152             mWifiInjector.getWifiMetrics().logUserActionEvent(
1153                     WifiMetrics.convertMeteredOverrideEnumToUserActionEventType(
1154                             after.meteredOverride),
1155                     after.networkId);
1156         }
1157 
1158         // Logs changes in macRandomizationSetting.
1159         if (before.macRandomizationSetting != after.macRandomizationSetting) {
1160             mWifiInjector.getWifiMetrics().logUserActionEvent(
1161                     after.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE
1162                             ? UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF
1163                             : UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_ON,
1164                     after.networkId);
1165         }
1166     }
1167 
1168     /**
1169      * Add a network or update a network configuration to our database.
1170      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1171      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1172      *
1173      * @param config provided WifiConfiguration object.
1174      * @param uid UID of the app requesting the network addition/modification.
1175      * @param packageName Package name of the app requesting the network addition/modification.
1176      * @return NetworkUpdateResult object representing status of the update.
1177      */
addOrUpdateNetworkInternal(WifiConfiguration config, int uid, @Nullable String packageName)1178     private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid,
1179                                                            @Nullable String packageName) {
1180         if (mVerboseLoggingEnabled) {
1181             Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
1182         }
1183         WifiConfiguration newInternalConfig = null;
1184 
1185         // First check if we already have a network with the provided network id or configKey.
1186         WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
1187         // No existing network found. So, potentially a network add.
1188         if (existingInternalConfig == null) {
1189             if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
1190                 Log.e(TAG, "Cannot add network with invalid config");
1191                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1192             }
1193             newInternalConfig =
1194                     createNewInternalWifiConfigurationFromExternal(config, uid, packageName);
1195             // Since the original config provided may have had an empty
1196             // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
1197             // network with the the same configkey.
1198             existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.getKey());
1199         }
1200         // Existing network found. So, a network update.
1201         if (existingInternalConfig != null) {
1202             if (!WifiConfigurationUtil.validate(
1203                     config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
1204                 Log.e(TAG, "Cannot update network with invalid config");
1205                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1206             }
1207             // Check for the app's permission before we let it update this network.
1208             if (!canModifyNetwork(existingInternalConfig, uid, packageName)) {
1209                 Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1210                         + config.getKey());
1211                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1212             }
1213             if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1214                     && !config.isPasspoint()) {
1215                 logUserActionEvents(existingInternalConfig, config);
1216             }
1217             newInternalConfig =
1218                     updateExistingInternalWifiConfigurationFromExternal(
1219                             existingInternalConfig, config, uid, packageName);
1220         }
1221 
1222         // Only add networks with proxy settings if the user has permission to
1223         if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
1224                 && !canModifyProxySettings(uid, packageName)) {
1225             Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
1226                     + config.getKey() + ". Must have NETWORK_SETTINGS,"
1227                     + " or be device or profile owner.");
1228             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1229         }
1230 
1231         if (WifiConfigurationUtil.hasMacRandomizationSettingsChanged(existingInternalConfig,
1232                 newInternalConfig) && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1233                 && !mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)
1234                 && !(newInternalConfig.isPasspoint() && uid == newInternalConfig.creatorUid)) {
1235             Log.e(TAG, "UID " + uid + " does not have permission to modify MAC randomization "
1236                     + "Settings " + config.getKey() + ". Must have "
1237                     + "NETWORK_SETTINGS or NETWORK_SETUP_WIZARD or be the creator adding or "
1238                     + "updating a passpoint network.");
1239             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1240         }
1241 
1242         // Update the keys for saved enterprise networks. For Passpoint, the certificates
1243         // and keys are installed at the time the provider is installed. For suggestion enterprise
1244         // network the certificates and keys are installed at the time the suggestion is added
1245         if (!config.isPasspoint() && !config.fromWifiNetworkSuggestion && config.isEnterprise()) {
1246             if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
1247                 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1248             }
1249         }
1250 
1251         boolean newNetwork = (existingInternalConfig == null);
1252         // This is needed to inform IpClient about any IP configuration changes.
1253         boolean hasIpChanged =
1254                 newNetwork || WifiConfigurationUtil.hasIpChanged(
1255                         existingInternalConfig, newInternalConfig);
1256         boolean hasProxyChanged =
1257                 newNetwork || WifiConfigurationUtil.hasProxyChanged(
1258                         existingInternalConfig, newInternalConfig);
1259         // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
1260         boolean hasCredentialChanged =
1261                 newNetwork || WifiConfigurationUtil.hasCredentialChanged(
1262                         existingInternalConfig, newInternalConfig);
1263         if (hasCredentialChanged) {
1264             newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
1265         }
1266 
1267         // Add it to our internal map. This will replace any existing network configuration for
1268         // updates.
1269         try {
1270             mConfiguredNetworks.put(newInternalConfig);
1271         } catch (IllegalArgumentException e) {
1272             Log.e(TAG, "Failed to add network to config map", e);
1273             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1274         }
1275         // Only re-enable network: 1. add or update user saved network; 2. add or update a user
1276         // saved passpoint network framework consider it is a new network.
1277         if (!newInternalConfig.fromWifiNetworkSuggestion
1278                 && (!newInternalConfig.isPasspoint() || newNetwork)) {
1279             userEnabledNetwork(newInternalConfig.networkId);
1280         }
1281 
1282         // Stage the backup of the SettingsProvider package which backs this up.
1283         mBackupManagerProxy.notifyDataChanged();
1284 
1285         NetworkUpdateResult result =
1286                 new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);
1287         result.setIsNewNetwork(newNetwork);
1288         result.setNetworkId(newInternalConfig.networkId);
1289 
1290         localLog("addOrUpdateNetworkInternal: added/updated config."
1291                 + " netId=" + newInternalConfig.networkId
1292                 + " configKey=" + newInternalConfig.getKey()
1293                 + " uid=" + Integer.toString(newInternalConfig.creatorUid)
1294                 + " name=" + newInternalConfig.creatorName);
1295         return result;
1296     }
1297 
1298     /**
1299      * Add a network or update a network configuration to our database.
1300      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1301      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1302      *
1303      * @param config provided WifiConfiguration object.
1304      * @param uid UID of the app requesting the network addition/modification.
1305      * @param packageName Package name of the app requesting the network addition/modification.
1306      * @return NetworkUpdateResult object representing status of the update.
1307      */
addOrUpdateNetwork(WifiConfiguration config, int uid, @Nullable String packageName)1308     public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid,
1309                                                   @Nullable String packageName) {
1310         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1311             Log.e(TAG, "UID " + uid + " not visible to the current user");
1312             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1313         }
1314         if (config == null) {
1315             Log.e(TAG, "Cannot add/update network with null config");
1316             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1317         }
1318         if (mPendingStoreRead) {
1319             Log.e(TAG, "Cannot add/update network before store is read!");
1320             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1321         }
1322         WifiConfiguration existingConfig = getInternalConfiguredNetwork(config);
1323         if (!config.isEphemeral()) {
1324             // Removes the existing ephemeral network if it exists to add this configuration.
1325             if (existingConfig != null && existingConfig.isEphemeral()) {
1326                 // In this case, new connection for this config won't happen because same
1327                 // network is already registered as an ephemeral network.
1328                 // Clear the Ephemeral Network to address the situation.
1329                 removeNetwork(
1330                         existingConfig.networkId, existingConfig.creatorUid, config.creatorName);
1331             }
1332         }
1333 
1334         NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid, packageName);
1335         if (!result.isSuccess()) {
1336             Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
1337             return result;
1338         }
1339         WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
1340         sendConfiguredNetworkChangedBroadcast(
1341                 result.isNewNetwork()
1342                         ? WifiManager.CHANGE_REASON_ADDED
1343                         : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1344         // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
1345         if (!config.ephemeral && !config.isPasspoint()) {
1346             saveToStore(true);
1347         }
1348 
1349         for (OnNetworkUpdateListener listener : mListeners) {
1350             if (result.isNewNetwork()) {
1351                 listener.onNetworkAdded(
1352                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID));
1353             } else {
1354                 listener.onNetworkUpdated(
1355                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID),
1356                         createExternalWifiConfiguration(existingConfig, true, Process.WIFI_UID));
1357             }
1358         }
1359         return result;
1360     }
1361 
1362     /**
1363      * Add a network or update a network configuration to our database.
1364      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1365      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1366      *
1367      * @param config provided WifiConfiguration object.
1368      * @param uid    UID of the app requesting the network addition/modification.
1369      * @return NetworkUpdateResult object representing status of the update.
1370      */
addOrUpdateNetwork(WifiConfiguration config, int uid)1371     public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
1372         return addOrUpdateNetwork(config, uid, null);
1373     }
1374 
1375     /**
1376      * Removes the specified network configuration from our database.
1377      *
1378      * @param config provided WifiConfiguration object.
1379      * @param uid UID of the app requesting the network deletion.
1380      * @return true if successful, false otherwise.
1381      */
removeNetworkInternal(WifiConfiguration config, int uid)1382     private boolean removeNetworkInternal(WifiConfiguration config, int uid) {
1383         if (mVerboseLoggingEnabled) {
1384             Log.v(TAG, "Removing network " + config.getPrintableSsid());
1385         }
1386         // Remove any associated enterprise keys for saved enterprise networks. Passpoint network
1387         // will remove the enterprise keys when provider is uninstalled. Suggestion enterprise
1388         // networks will remove the enterprise keys when suggestion is removed.
1389         if (!config.fromWifiNetworkSuggestion && !config.isPasspoint() && config.isEnterprise()) {
1390             mWifiKeyStore.removeKeys(config.enterpriseConfig);
1391         }
1392 
1393         removeConnectChoiceFromAllNetworks(config.getKey());
1394         mConfiguredNetworks.remove(config.networkId);
1395         mScanDetailCaches.remove(config.networkId);
1396         // Stage the backup of the SettingsProvider package which backs this up.
1397         mBackupManagerProxy.notifyDataChanged();
1398         mWifiInjector.getBssidBlocklistMonitor().handleNetworkRemoved(config.SSID);
1399 
1400         localLog("removeNetworkInternal: removed config."
1401                 + " netId=" + config.networkId
1402                 + " configKey=" + config.getKey()
1403                 + " uid=" + Integer.toString(uid)
1404                 + " name=" + mContext.getPackageManager().getNameForUid(uid));
1405         return true;
1406     }
1407 
1408     /**
1409      * Removes the specified network configuration from our database.
1410      *
1411      * @param networkId network ID of the provided network.
1412      * @param uid       UID of the app requesting the network deletion.
1413      * @return true if successful, false otherwise.
1414      */
removeNetwork(int networkId, int uid, String packageName)1415     public boolean removeNetwork(int networkId, int uid, String packageName) {
1416         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1417             Log.e(TAG, "UID " + uid + " not visible to the current user");
1418             return false;
1419         }
1420         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1421         if (config == null) {
1422             return false;
1423         }
1424         if (!canModifyNetwork(config, uid, packageName)) {
1425             Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1426                     + config.getKey());
1427             return false;
1428         }
1429         if (!removeNetworkInternal(config, uid)) {
1430             Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1431             return false;
1432         }
1433         if (networkId == mLastSelectedNetworkId) {
1434             clearLastSelectedNetwork();
1435         }
1436         if (!config.ephemeral && !config.isPasspoint()) {
1437             mLruConnectionTracker.removeNetwork(config);
1438         }
1439         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED);
1440         // Unless the removed network is ephemeral or Passpoint, persist the network removal.
1441         if (!config.ephemeral && !config.isPasspoint()) {
1442             saveToStore(true);
1443         }
1444         for (OnNetworkUpdateListener listener : mListeners) {
1445             listener.onNetworkRemoved(
1446                     createExternalWifiConfiguration(config, true, Process.WIFI_UID));
1447         }
1448         return true;
1449     }
1450 
getCreatorPackageName(WifiConfiguration config)1451     private String getCreatorPackageName(WifiConfiguration config) {
1452         String creatorName = config.creatorName;
1453         // getNameForUid (Stored in WifiConfiguration.creatorName) returns a concatenation of name
1454         // and uid for shared UIDs ("name:uid").
1455         if (!creatorName.contains(":")) {
1456             return creatorName; // regular app not using shared UID.
1457         }
1458         // Separate the package name from the string for app using shared UID.
1459         return creatorName.substring(0, creatorName.indexOf(":"));
1460     }
1461 
1462     /**
1463      * Remove all networks associated with an application.
1464      *
1465      * @param app Application info of the package of networks to remove.
1466      * @return the {@link Set} of networks that were removed by this call. Networks which matched
1467      *         but failed to remove are omitted from this set.
1468      */
removeNetworksForApp(ApplicationInfo app)1469     public Set<Integer> removeNetworksForApp(ApplicationInfo app) {
1470         if (app == null || app.packageName == null) {
1471             return Collections.<Integer>emptySet();
1472         }
1473         Log.d(TAG, "Remove all networks for app " + app);
1474         Set<Integer> removedNetworks = new ArraySet<>();
1475         WifiConfiguration[] copiedConfigs =
1476                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1477         for (WifiConfiguration config : copiedConfigs) {
1478             if (app.uid != config.creatorUid
1479                     || !app.packageName.equals(getCreatorPackageName(config))) {
1480                 continue;
1481             }
1482             localLog("Removing network " + config.SSID
1483                     + ", application \"" + app.packageName + "\" uninstalled"
1484                     + " from user " + UserHandle.getUserHandleForUid(app.uid));
1485             if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) {
1486                 removedNetworks.add(config.networkId);
1487             }
1488         }
1489         return removedNetworks;
1490     }
1491 
1492     /**
1493      * Remove all networks associated with a user.
1494      *
1495      * @param userId The identifier of the user which is being removed.
1496      * @return the {@link Set} of networks that were removed by this call. Networks which matched
1497      *         but failed to remove are omitted from this set.
1498      */
removeNetworksForUser(int userId)1499     Set<Integer> removeNetworksForUser(int userId) {
1500         Log.d(TAG, "Remove all networks for user " + userId);
1501         Set<Integer> removedNetworks = new ArraySet<>();
1502         WifiConfiguration[] copiedConfigs =
1503                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1504         for (WifiConfiguration config : copiedConfigs) {
1505             if (userId != UserHandle.getUserHandleForUid(config.creatorUid).getIdentifier()) {
1506                 continue;
1507             }
1508             localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1509             if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) {
1510                 removedNetworks.add(config.networkId);
1511             }
1512         }
1513         return removedNetworks;
1514     }
1515 
1516     /**
1517      * Iterates through the internal list of configured networks and removes any ephemeral or
1518      * passpoint network configurations which are transient in nature.
1519      *
1520      * @return true if a network was removed, false otherwise.
1521      */
removeAllEphemeralOrPasspointConfiguredNetworks()1522     public boolean removeAllEphemeralOrPasspointConfiguredNetworks() {
1523         if (mVerboseLoggingEnabled) {
1524             Log.v(TAG, "Removing all passpoint or ephemeral configured networks");
1525         }
1526         boolean didRemove = false;
1527         WifiConfiguration[] copiedConfigs =
1528                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1529         for (WifiConfiguration config : copiedConfigs) {
1530             if (config.isPasspoint()) {
1531                 Log.d(TAG, "Removing passpoint network config " + config.getKey());
1532                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1533                 didRemove = true;
1534             } else if (config.ephemeral) {
1535                 Log.d(TAG, "Removing ephemeral network config " + config.getKey());
1536                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1537                 didRemove = true;
1538             }
1539         }
1540         return didRemove;
1541     }
1542 
1543     /**
1544      * Removes the suggestion network configuration matched with {@code configKey} provided.
1545      *
1546      * @param configKey Config Key for the corresponding network suggestion.
1547      * @return true if a network was removed, false otherwise.
1548      */
removeSuggestionConfiguredNetwork(@onNull String configKey)1549     public boolean removeSuggestionConfiguredNetwork(@NonNull String configKey) {
1550         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
1551         if (config != null && config.ephemeral && config.fromWifiNetworkSuggestion) {
1552             Log.d(TAG, "Removing suggestion network config " + config.getKey());
1553             return removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1554         }
1555         return false;
1556     }
1557 
1558     /**
1559      * Removes the passpoint network configuration matched with {@code configKey} provided.
1560      *
1561      * @param configKey Config Key for the corresponding passpoint.
1562      * @return true if a network was removed, false otherwise.
1563      */
removePasspointConfiguredNetwork(@onNull String configKey)1564     public boolean removePasspointConfiguredNetwork(@NonNull String configKey) {
1565         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
1566         if (config != null && config.isPasspoint()) {
1567             Log.d(TAG, "Removing passpoint network config " + config.getKey());
1568             return removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1569         }
1570         return false;
1571     }
1572 
1573     /**
1574      * Check whether a network belong to a known list of networks that may not support randomized
1575      * MAC.
1576      * @param networkId
1577      * @return true if the network is in the hotlist and MAC randomization is enabled.
1578      */
isInFlakyRandomizationSsidHotlist(int networkId)1579     public boolean isInFlakyRandomizationSsidHotlist(int networkId) {
1580         WifiConfiguration config = getConfiguredNetwork(networkId);
1581         return config != null
1582                 && config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT
1583                 && mDeviceConfigFacade.getRandomizationFlakySsidHotlist().contains(config.SSID);
1584     }
1585 
1586     /**
1587      * Helper method to mark a network enabled for network selection.
1588      */
setNetworkSelectionEnabled(WifiConfiguration config)1589     private void setNetworkSelectionEnabled(WifiConfiguration config) {
1590         NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1591         if (status.getNetworkSelectionStatus()
1592                 != NetworkSelectionStatus.NETWORK_SELECTION_ENABLED) {
1593             localLog("setNetworkSelectionEnabled: configKey=" + config.getKey()
1594                     + " old networkStatus=" + status.getNetworkStatusString()
1595                     + " disableReason=" + status.getNetworkSelectionDisableReasonString());
1596         }
1597         status.setNetworkSelectionStatus(
1598                 NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
1599         status.setDisableTime(
1600                 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1601         status.setNetworkSelectionDisableReason(NetworkSelectionStatus.DISABLED_NONE);
1602 
1603         // Clear out all the disable reason counters.
1604         status.clearDisableReasonCounter();
1605         for (OnNetworkUpdateListener listener : mListeners) {
1606             listener.onNetworkEnabled(
1607                     createExternalWifiConfiguration(config, true, Process.WIFI_UID));
1608         }
1609     }
1610 
1611     /**
1612      * Helper method to mark a network temporarily disabled for network selection.
1613      */
setNetworkSelectionTemporarilyDisabled( WifiConfiguration config, int disableReason)1614     private void setNetworkSelectionTemporarilyDisabled(
1615             WifiConfiguration config, int disableReason) {
1616         NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1617         status.setNetworkSelectionStatus(
1618                 NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
1619         // Only need a valid time filled in for temporarily disabled networks.
1620         status.setDisableTime(mClock.getElapsedSinceBootMillis());
1621         status.setNetworkSelectionDisableReason(disableReason);
1622         for (OnNetworkUpdateListener listener : mListeners) {
1623             listener.onNetworkTemporarilyDisabled(
1624                     createExternalWifiConfiguration(config, true, Process.WIFI_UID), disableReason);
1625         }
1626     }
1627 
1628     /**
1629      * Helper method to mark a network permanently disabled for network selection.
1630      */
setNetworkSelectionPermanentlyDisabled( WifiConfiguration config, int disableReason)1631     private void setNetworkSelectionPermanentlyDisabled(
1632             WifiConfiguration config, int disableReason) {
1633         NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1634         status.setNetworkSelectionStatus(
1635                 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1636         status.setDisableTime(
1637                 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1638         status.setNetworkSelectionDisableReason(disableReason);
1639         for (OnNetworkUpdateListener listener : mListeners) {
1640             WifiConfiguration configForListener = new WifiConfiguration(config);
1641             listener.onNetworkPermanentlyDisabled(
1642                     createExternalWifiConfiguration(config, true, Process.WIFI_UID), disableReason);
1643         }
1644     }
1645 
1646     /**
1647      * Helper method to set the publicly exposed status for the network and send out the network
1648      * status change broadcast.
1649      */
setNetworkStatus(WifiConfiguration config, int status)1650     private void setNetworkStatus(WifiConfiguration config, int status) {
1651         config.status = status;
1652         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1653     }
1654 
1655     /**
1656      * Sets a network's status (both internal and public) according to the update reason and
1657      * its current state.
1658      *
1659      * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
1660      * public {@link WifiConfiguration#status} field if the network is either enabled or
1661      * permanently disabled.
1662      *
1663      * @param config network to be updated.
1664      * @param reason reason code for update.
1665      * @return true if the input configuration has been updated, false otherwise.
1666      */
setNetworkSelectionStatus(WifiConfiguration config, int reason)1667     private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
1668         NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1669         if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
1670             Log.e(TAG, "Invalid Network disable reason " + reason);
1671             return false;
1672         }
1673         if (reason == NetworkSelectionStatus.DISABLED_NONE) {
1674             setNetworkSelectionEnabled(config);
1675             setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1676         } else if (reason < NetworkSelectionStatus.PERMANENTLY_DISABLED_STARTING_INDEX) {
1677             setNetworkSelectionTemporarilyDisabled(config, reason);
1678         } else {
1679             setNetworkSelectionPermanentlyDisabled(config, reason);
1680             setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
1681         }
1682         localLog("setNetworkSelectionStatus: configKey=" + config.getKey()
1683                 + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
1684                 + networkStatus.getNetworkSelectionDisableReasonString());
1685         saveToStore(false);
1686         return true;
1687     }
1688 
1689     /**
1690      * Update a network's status (both internal and public) according to the update reason and
1691      * its current state.
1692      *
1693      * @param config network to be updated.
1694      * @param reason reason code for update.
1695      * @return true if the input configuration has been updated, false otherwise.
1696      */
updateNetworkSelectionStatus(WifiConfiguration config, int reason)1697     private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1698         NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1699         if (reason != NetworkSelectionStatus.DISABLED_NONE) {
1700 
1701             // Do not update SSID blacklist with information if this is the only
1702             // SSID be observed. By ignoring it we will cause additional failures
1703             // which will trigger Watchdog.
1704             if (reason == NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION
1705                     || reason == NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE
1706                     || reason == NetworkSelectionStatus.DISABLED_DHCP_FAILURE) {
1707                 if (mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate()) {
1708                     if (mVerboseLoggingEnabled) {
1709                         Log.v(TAG, "Ignore update network selection status "
1710                                     + "since Watchdog trigger is activated");
1711                     }
1712                     return false;
1713                 }
1714             }
1715 
1716             networkStatus.incrementDisableReasonCounter(reason);
1717             // For network disable reasons, we should only update the status if we cross the
1718             // threshold.
1719             int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
1720             int disableReasonThreshold = getNetworkSelectionDisableThreshold(reason);
1721             if (disableReasonCounter < disableReasonThreshold) {
1722                 if (mVerboseLoggingEnabled) {
1723                     Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
1724                             + " for reason "
1725                             + NetworkSelectionStatus.getNetworkSelectionDisableReasonString(reason)
1726                             + " is " + networkStatus.getDisableReasonCounter(reason)
1727                             + " and threshold is " + disableReasonThreshold);
1728                 }
1729                 return true;
1730             }
1731         }
1732         return setNetworkSelectionStatus(config, reason);
1733     }
1734 
1735     /**
1736      * Update a network's status (both internal and public) according to the update reason and
1737      * its current state.
1738      *
1739      * Each network has 2 status:
1740      * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
1741      * for temporarily disabling a network for Network Selector.
1742      * 2. Status: This is the exposed status for a network. This is mostly set by
1743      * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
1744      * {@link WifiManager#disableNetwork(int)}.
1745      *
1746      * @param networkId network ID of the network that needs the update.
1747      * @param reason    reason to update the network.
1748      * @return true if the input configuration has been updated, false otherwise.
1749      */
updateNetworkSelectionStatus(int networkId, int reason)1750     public boolean updateNetworkSelectionStatus(int networkId, int reason) {
1751         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1752         if (config == null) {
1753             return false;
1754         }
1755         return updateNetworkSelectionStatus(config, reason);
1756     }
1757 
1758     /**
1759      * Attempt to re-enable a network for network selection, if this network was either:
1760      * a) Previously temporarily disabled, but its disable timeout has expired, or
1761      * b) Previously disabled because of a user switch, but is now visible to the current
1762      * user.
1763      *
1764      * @param config configuration for the network to be re-enabled for network selection. The
1765      *               network corresponding to the config must be visible to the current user.
1766      * @return true if the network identified by {@param config} was re-enabled for qualified
1767      * network selection, false otherwise.
1768      */
tryEnableNetwork(WifiConfiguration config)1769     private boolean tryEnableNetwork(WifiConfiguration config) {
1770         NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1771         if (networkStatus.isNetworkTemporaryDisabled()) {
1772             long timeDifferenceMs =
1773                     mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
1774             int disableReason = networkStatus.getNetworkSelectionDisableReason();
1775             int blockedBssids = Math.min(MAX_BLOCKED_BSSID_PER_NETWORK,
1776                     mWifiInjector.getBssidBlocklistMonitor()
1777                             .updateAndGetNumBlockedBssidsForSsid(config.SSID));
1778             // if no BSSIDs are blocked then we should keep trying to connect to something
1779             long disableTimeoutMs = 0;
1780             if (blockedBssids > 0) {
1781                 double multiplier = Math.pow(2.0, blockedBssids - 1.0);
1782                 disableTimeoutMs = (long) (getNetworkSelectionDisableTimeoutMillis(disableReason)
1783                         * multiplier);
1784             }
1785             if (timeDifferenceMs >= disableTimeoutMs) {
1786                 return updateNetworkSelectionStatus(
1787                         config, NetworkSelectionStatus.DISABLED_NONE);
1788             }
1789         }
1790         return false;
1791     }
1792 
1793     /**
1794      * Attempt to re-enable a network for network selection, if this network was either:
1795      * a) Previously temporarily disabled, but its disable timeout has expired, or
1796      * b) Previously disabled because of a user switch, but is now visible to the current
1797      * user.
1798      *
1799      * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1800      * @return true if the network identified by {@param networkId} was re-enabled for qualified
1801      * network selection, false otherwise.
1802      */
tryEnableNetwork(int networkId)1803     public boolean tryEnableNetwork(int networkId) {
1804         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1805         if (config == null) {
1806             return false;
1807         }
1808         return tryEnableNetwork(config);
1809     }
1810 
1811     /**
1812      * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
1813      *
1814      * @param networkId     network ID of the network that needs the update.
1815      * @param disableOthers Whether to disable all other networks or not. This is used to indicate
1816      *                      that the app requested connection to a specific network.
1817      * @param uid           uid of the app requesting the update.
1818      * @return true if it succeeds, false otherwise
1819      */
enableNetwork(int networkId, boolean disableOthers, int uid, String packageName)1820     public boolean enableNetwork(int networkId, boolean disableOthers, int uid,
1821                                  String packageName) {
1822         if (mVerboseLoggingEnabled) {
1823             Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
1824         }
1825         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1826             Log.e(TAG, "UID " + uid + " not visible to the current user");
1827             return false;
1828         }
1829         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1830         if (config == null) {
1831             return false;
1832         }
1833         // Set the "last selected" flag even if the app does not have permissions to modify this
1834         // network config. Apps are allowed to connect to networks even if they don't have
1835         // permission to modify it.
1836         if (disableOthers) {
1837             setLastSelectedNetwork(networkId);
1838         }
1839         if (!canModifyNetwork(config, uid, packageName)) {
1840             Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1841                     + config.getKey());
1842             return false;
1843         }
1844         if (!updateNetworkSelectionStatus(
1845                 networkId, WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)) {
1846             return false;
1847         }
1848         saveToStore(true);
1849         return true;
1850     }
1851 
1852     /**
1853      * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
1854      *
1855      * @param networkId network ID of the network that needs the update.
1856      * @param uid       uid of the app requesting the update.
1857      * @return true if it succeeds, false otherwise
1858      */
disableNetwork(int networkId, int uid, String packageName)1859     public boolean disableNetwork(int networkId, int uid, String packageName) {
1860         if (mVerboseLoggingEnabled) {
1861             Log.v(TAG, "Disabling network " + networkId);
1862         }
1863         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1864             Log.e(TAG, "UID " + uid + " not visible to the current user");
1865             return false;
1866         }
1867         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1868         if (config == null) {
1869             return false;
1870         }
1871         // Reset the "last selected" flag even if the app does not have permissions to modify this
1872         // network config.
1873         if (networkId == mLastSelectedNetworkId) {
1874             clearLastSelectedNetwork();
1875         }
1876         if (!canModifyNetwork(config, uid, packageName)) {
1877             Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1878                     + config.getKey());
1879             return false;
1880         }
1881         if (!updateNetworkSelectionStatus(
1882                 networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
1883             return false;
1884         }
1885         saveToStore(true);
1886         return true;
1887     }
1888 
1889     /**
1890      * Changes the user's choice to allow auto-join using the
1891      * {@link WifiManager#allowAutojoin(int, boolean)} API.
1892      *
1893      * @param networkId network ID of the network that needs the update.
1894      * @param choice the choice to allow auto-join or not
1895      * @return true if it succeeds, false otherwise
1896      */
allowAutojoin(int networkId, boolean choice)1897     public boolean allowAutojoin(int networkId, boolean choice) {
1898         if (mVerboseLoggingEnabled) {
1899             Log.v(TAG, "Setting allowAutojoin to " + choice + " for netId " + networkId);
1900         }
1901         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1902         if (config == null) {
1903             Log.e(TAG, "allowAutojoin: Supplied networkId " + networkId
1904                     + " has no matching config");
1905             return false;
1906         }
1907 
1908         config.allowAutojoin = choice;
1909         if (!choice) {
1910             removeConnectChoiceFromAllNetworks(config.getKey());
1911             clearNetworkConnectChoice(config.networkId);
1912         }
1913         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1914         if (!config.ephemeral) {
1915             saveToStore(true);
1916         }
1917         return true;
1918     }
1919 
1920     /**
1921      * Updates the last connected UID for the provided configuration.
1922      *
1923      * @param networkId network ID corresponding to the network.
1924      * @param uid       uid of the app requesting the connection.
1925      * @return true if the network was found, false otherwise.
1926      */
updateLastConnectUid(int networkId, int uid)1927     public boolean updateLastConnectUid(int networkId, int uid) {
1928         if (mVerboseLoggingEnabled) {
1929             Log.v(TAG, "Update network last connect UID for " + networkId);
1930         }
1931         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUser(uid)) {
1932             Log.e(TAG, "UID " + uid + " not visible to the current user");
1933             return false;
1934         }
1935         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1936         if (config == null) {
1937             return false;
1938         }
1939         config.lastConnectUid = uid;
1940         return true;
1941     }
1942 
1943     /**
1944      * Updates a network configuration after a successful connection to it.
1945      *
1946      * This method updates the following WifiConfiguration elements:
1947      * 1. Set the |lastConnected| timestamp.
1948      * 2. Increment |numAssociation| counter.
1949      * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
1950      * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
1951      * 5. Sets the status of network as |CURRENT|.
1952      *
1953      * @param networkId network ID corresponding to the network.
1954      * @return true if the network was found, false otherwise.
1955      */
updateNetworkAfterConnect(int networkId)1956     public boolean updateNetworkAfterConnect(int networkId) {
1957         if (mVerboseLoggingEnabled) {
1958             Log.v(TAG, "Update network after connect for " + networkId);
1959         }
1960         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1961         if (config == null) {
1962             return false;
1963         }
1964 
1965         // Only record connection order for non-passpoint from user saved or suggestion.
1966         if (!config.isPasspoint() && (config.fromWifiNetworkSuggestion || !config.ephemeral)) {
1967             mLruConnectionTracker.addNetwork(config);
1968         }
1969         config.lastConnected = mClock.getWallClockMillis();
1970         config.numAssociation++;
1971         config.getNetworkSelectionStatus().clearDisableReasonCounter();
1972         config.getNetworkSelectionStatus().setHasEverConnected(true);
1973         setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
1974         saveToStore(false);
1975         return true;
1976     }
1977 
1978     /**
1979      * Updates a network configuration after disconnection from it.
1980      *
1981      * This method updates the following WifiConfiguration elements:
1982      * 1. Set the |lastDisConnected| timestamp.
1983      * 2. Sets the status of network back to |ENABLED|.
1984      *
1985      * @param networkId network ID corresponding to the network.
1986      * @return true if the network was found, false otherwise.
1987      */
updateNetworkAfterDisconnect(int networkId)1988     public boolean updateNetworkAfterDisconnect(int networkId) {
1989         if (mVerboseLoggingEnabled) {
1990             Log.v(TAG, "Update network after disconnect for " + networkId);
1991         }
1992         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1993         if (config == null) {
1994             return false;
1995         }
1996         config.lastDisconnected = mClock.getWallClockMillis();
1997         config.randomizedMacExpirationTimeMs = Math.max(config.randomizedMacExpirationTimeMs,
1998                 config.lastDisconnected + AGGRESSIVE_MAC_WAIT_AFTER_DISCONNECT_MS);
1999         // If the network hasn't been disabled, mark it back as
2000         // enabled after disconnection.
2001         if (config.status == WifiConfiguration.Status.CURRENT) {
2002             setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
2003         }
2004         saveToStore(false);
2005         return true;
2006     }
2007 
2008     /**
2009      * Set default GW MAC address for the provided network.
2010      *
2011      * @param networkId network ID corresponding to the network.
2012      * @param macAddress MAC address of the gateway to be set.
2013      * @return true if the network was found, false otherwise.
2014      */
setNetworkDefaultGwMacAddress(int networkId, String macAddress)2015     public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
2016         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2017         if (config == null) {
2018             return false;
2019         }
2020         config.defaultGwMacAddress = macAddress;
2021         return true;
2022     }
2023 
2024     /**
2025      * Clear the {@link NetworkSelectionStatus#mCandidate},
2026      * {@link NetworkSelectionStatus#mCandidateScore} &
2027      * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
2028      *
2029      * This is invoked by Network Selector at the start of every selection procedure to clear all
2030      * configured networks' scan-result-candidates.
2031      *
2032      * @param networkId network ID corresponding to the network.
2033      * @return true if the network was found, false otherwise.
2034      */
clearNetworkCandidateScanResult(int networkId)2035     public boolean clearNetworkCandidateScanResult(int networkId) {
2036         if (mVerboseLoggingEnabled) {
2037             Log.v(TAG, "Clear network candidate scan result for " + networkId);
2038         }
2039         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2040         if (config == null) {
2041             return false;
2042         }
2043         config.getNetworkSelectionStatus().setCandidate(null);
2044         config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
2045         config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
2046         return true;
2047     }
2048 
2049     /**
2050      * Set the {@link NetworkSelectionStatus#mCandidate},
2051      * {@link NetworkSelectionStatus#mCandidateScore} &
2052      * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
2053      *
2054      * This is invoked by Network Selector when it sees a network during network selection procedure
2055      * to set the scan result candidate.
2056      *
2057      * @param networkId  network ID corresponding to the network.
2058      * @param scanResult Candidate ScanResult associated with this network.
2059      * @param score      Score assigned to the candidate.
2060      * @return true if the network was found, false otherwise.
2061      */
setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score)2062     public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) {
2063         if (mVerboseLoggingEnabled) {
2064             Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId);
2065         }
2066         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2067         if (config == null) {
2068             Log.e(TAG, "Cannot find network for " + networkId);
2069             return false;
2070         }
2071         config.getNetworkSelectionStatus().setCandidate(scanResult);
2072         config.getNetworkSelectionStatus().setCandidateScore(score);
2073         config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
2074         return true;
2075     }
2076 
2077     /**
2078      * Iterate through all the saved networks and remove the provided configuration from the
2079      * {@link NetworkSelectionStatus#mConnectChoice} from them.
2080      *
2081      * This is invoked when a network is removed from our records.
2082      *
2083      * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
2084      */
removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey)2085     private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
2086         if (mVerboseLoggingEnabled) {
2087             Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
2088         }
2089         if (connectChoiceConfigKey == null) {
2090             return;
2091         }
2092         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2093             WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
2094             String connectChoice = status.getConnectChoice();
2095             if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
2096                 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
2097                         + " : " + config.networkId);
2098                 clearNetworkConnectChoice(config.networkId);
2099             }
2100         }
2101     }
2102 
2103     /**
2104      * Clear the {@link NetworkSelectionStatus#mConnectChoice} for the provided network.
2105      *
2106      * @param networkId network ID corresponding to the network.
2107      * @return true if the network was found, false otherwise.
2108      */
clearNetworkConnectChoice(int networkId)2109     public boolean clearNetworkConnectChoice(int networkId) {
2110         if (mVerboseLoggingEnabled) {
2111             Log.v(TAG, "Clear network connect choice for " + networkId);
2112         }
2113         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2114         if (config == null) {
2115             return false;
2116         }
2117         config.getNetworkSelectionStatus().setConnectChoice(null);
2118         saveToStore(false);
2119         return true;
2120     }
2121 
2122     /**
2123      * Set the {@link NetworkSelectionStatus#mConnectChoice} for the provided network.
2124      *
2125      * This is invoked by Network Selector when the user overrides the currently connected network
2126      * choice.
2127      *
2128      * @param networkId              network ID corresponding to the network.
2129      * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
2130      *                               this network.
2131      * @param timestamp              timestamp at which the choice was made.
2132      * @return true if the network was found, false otherwise.
2133      */
setNetworkConnectChoice( int networkId, String connectChoiceConfigKey)2134     public boolean setNetworkConnectChoice(
2135             int networkId, String connectChoiceConfigKey) {
2136         if (mVerboseLoggingEnabled) {
2137             Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
2138         }
2139         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2140         if (config == null) {
2141             return false;
2142         }
2143         config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
2144         saveToStore(false);
2145         return true;
2146     }
2147 
2148     /**
2149      * Increments the number of no internet access reports in the provided network.
2150      *
2151      * @param networkId network ID corresponding to the network.
2152      * @return true if the network was found, false otherwise.
2153      */
incrementNetworkNoInternetAccessReports(int networkId)2154     public boolean incrementNetworkNoInternetAccessReports(int networkId) {
2155         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2156         if (config == null) {
2157             return false;
2158         }
2159         config.numNoInternetAccessReports++;
2160         return true;
2161     }
2162 
2163     /**
2164      * Sets the internet access is validated or not in the provided network.
2165      *
2166      * @param networkId network ID corresponding to the network.
2167      * @param validated Whether access is validated or not.
2168      * @return true if the network was found, false otherwise.
2169      */
setNetworkValidatedInternetAccess(int networkId, boolean validated)2170     public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
2171         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2172         if (config == null) {
2173             return false;
2174         }
2175         config.validatedInternetAccess = validated;
2176         config.numNoInternetAccessReports = 0;
2177         saveToStore(false);
2178         return true;
2179     }
2180 
2181     /**
2182      * Sets whether the internet access is expected or not in the provided network.
2183      *
2184      * @param networkId network ID corresponding to the network.
2185      * @param expected  Whether access is expected or not.
2186      * @return true if the network was found, false otherwise.
2187      */
setNetworkNoInternetAccessExpected(int networkId, boolean expected)2188     public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
2189         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2190         if (config == null) {
2191             return false;
2192         }
2193         config.noInternetAccessExpected = expected;
2194         return true;
2195     }
2196 
2197     /**
2198      * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
2199      * is done when either the corresponding network is either removed or disabled.
2200      */
clearLastSelectedNetwork()2201     public void clearLastSelectedNetwork() {
2202         if (mVerboseLoggingEnabled) {
2203             Log.v(TAG, "Clearing last selected network");
2204         }
2205         mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2206         mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
2207     }
2208 
2209     /**
2210      * Helper method to mark a network as the last selected one by an app/user. This is set
2211      * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
2212      * This is used by network selector to assign a special bonus during network selection.
2213      */
setLastSelectedNetwork(int networkId)2214     private void setLastSelectedNetwork(int networkId) {
2215         if (mVerboseLoggingEnabled) {
2216             Log.v(TAG, "Setting last selected network to " + networkId);
2217         }
2218         mLastSelectedNetworkId = networkId;
2219         mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
2220     }
2221 
2222     /**
2223      * Retrieve the network Id corresponding to the last network that was explicitly selected by
2224      * an app/user.
2225      *
2226      * @return network Id corresponding to the last selected network.
2227      */
getLastSelectedNetwork()2228     public int getLastSelectedNetwork() {
2229         return mLastSelectedNetworkId;
2230     }
2231 
2232     /**
2233      * Retrieve the configKey corresponding to the last network that was explicitly selected by
2234      * an app/user.
2235      *
2236      * @return network Id corresponding to the last selected network.
2237      */
getLastSelectedNetworkConfigKey()2238     public String getLastSelectedNetworkConfigKey() {
2239         if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
2240             return "";
2241         }
2242         WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
2243         if (config == null) {
2244             return "";
2245         }
2246         return config.getKey();
2247     }
2248 
2249     /**
2250      * Retrieve the time stamp at which a network was explicitly selected by an app/user.
2251      *
2252      * @return timestamp in milliseconds from boot when this was set.
2253      */
getLastSelectedTimeStamp()2254     public long getLastSelectedTimeStamp() {
2255         return mLastSelectedTimeStamp;
2256     }
2257 
2258     /**
2259      * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
2260      * network.
2261      *
2262      * @param networkId network ID corresponding to the network.
2263      * @return existing {@link ScanDetailCache} entry if one exists or null.
2264      */
getScanDetailCacheForNetwork(int networkId)2265     public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
2266         return mScanDetailCaches.get(networkId);
2267     }
2268 
2269     /**
2270      * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
2271      * the provided network.
2272      *
2273      * @param config configuration corresponding to the the network.
2274      * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
2275      * this network.
2276      */
getOrCreateScanDetailCacheForNetwork(WifiConfiguration config)2277     private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
2278         if (config == null) return null;
2279         ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
2280         if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
2281             cache = new ScanDetailCache(
2282                     config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
2283             mScanDetailCaches.put(config.networkId, cache);
2284         }
2285         return cache;
2286     }
2287 
2288     /**
2289      * Saves the provided ScanDetail into the corresponding scan detail cache entry
2290      * {@link #mScanDetailCaches} for the provided network.
2291      *
2292      * @param config     configuration corresponding to the the network.
2293      * @param scanDetail new scan detail instance to be saved into the cache.
2294      */
saveToScanDetailCacheForNetwork( WifiConfiguration config, ScanDetail scanDetail)2295     private void saveToScanDetailCacheForNetwork(
2296             WifiConfiguration config, ScanDetail scanDetail) {
2297         ScanResult scanResult = scanDetail.getScanResult();
2298 
2299         WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID);
2300         network.addFrequency(scanResult.frequency);
2301         ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
2302         if (scanDetailCache == null) {
2303             Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
2304             return;
2305         }
2306 
2307         // Adding a new BSSID
2308         if (config.ephemeral) {
2309             // For an ephemeral Wi-Fi config, the ScanResult should be considered
2310             // untrusted.
2311             scanResult.untrusted = true;
2312         }
2313 
2314         // Add the scan detail to this network's scan detail cache.
2315         scanDetailCache.put(scanDetail);
2316 
2317         // Since we added a scan result to this configuration, re-attempt linking.
2318         // TODO: Do we really need to do this after every scan result?
2319         attemptNetworkLinking(config);
2320     }
2321 
2322     /**
2323      * Retrieves a configured network corresponding to the provided scan detail if one exists.
2324      *
2325      * @param scanDetail ScanDetail instance  to use for looking up the network.
2326      * @return WifiConfiguration object representing the network corresponding to the scanDetail,
2327      * null if none exists.
2328      *
2329      * TODO (b/142035508): This should only return saved networks (and rename to
2330      * getSavedNetworkForScanDetail()).
2331      */
getConfiguredNetworkForScanDetail(ScanDetail scanDetail)2332     public WifiConfiguration getConfiguredNetworkForScanDetail(ScanDetail scanDetail) {
2333         ScanResult scanResult = scanDetail.getScanResult();
2334         if (scanResult == null) {
2335             Log.e(TAG, "No scan result found in scan detail");
2336             return null;
2337         }
2338         WifiConfiguration config = null;
2339         try {
2340             config = mConfiguredNetworks.getByScanResultForCurrentUser(scanResult);
2341         } catch (IllegalArgumentException e) {
2342             Log.e(TAG, "Failed to lookup network from config map", e);
2343         }
2344         if (config != null) {
2345             if (mVerboseLoggingEnabled) {
2346                 Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.getKey()
2347                         + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
2348             }
2349         }
2350         return config;
2351     }
2352 
2353     /**
2354      * Caches the provided |scanDetail| into the corresponding scan detail cache entry
2355      * {@link #mScanDetailCaches} for the retrieved network.
2356      *
2357      * @param scanDetail input a scanDetail from the scan result
2358      * TODO (b/142035508): This should only return saved networks (and rename to
2359      * updateScanDetailCacheFromScanDetail()).
2360      */
updateScanDetailCacheFromScanDetail(ScanDetail scanDetail)2361     public void updateScanDetailCacheFromScanDetail(ScanDetail scanDetail) {
2362         WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail);
2363         if (network == null) {
2364             return;
2365         }
2366         saveToScanDetailCacheForNetwork(network, scanDetail);
2367     }
2368     /**
2369      * Retrieves a configured network corresponding to the provided scan detail if one exists and
2370      * caches the provided |scanDetail| into the corresponding scan detail cache entry
2371      * {@link #mScanDetailCaches} for the retrieved network.
2372      *
2373      * @param scanDetail input a scanDetail from the scan result
2374      * @return WifiConfiguration object representing the network corresponding to the scanDetail,
2375      * null if none exists.
2376      * TODO (b/142035508): This should only return saved networks (and rename to
2377      * getSavedNetworkForScanDetailAndCache()).
2378      */
getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail)2379     public WifiConfiguration getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail) {
2380         WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail);
2381         if (network == null) {
2382             return null;
2383         }
2384         saveToScanDetailCacheForNetwork(network, scanDetail);
2385         // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
2386         // Information Element (IE), into the associated WifiConfigurations. Most of the
2387         // time there is no TIM IE in the scan result (Probe Response instead of Beacon
2388         // Frame), these scanResult DTIM's are negative and ignored.
2389         // Used for metrics collection.
2390         if (scanDetail.getNetworkDetail() != null
2391                 && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
2392             network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
2393         }
2394         return createExternalWifiConfiguration(network, true, Process.WIFI_UID);
2395     }
2396 
2397     /**
2398      * Update the scan detail cache associated with current connected network with latest
2399      * RSSI value in the provided WifiInfo.
2400      * This is invoked when we get an RSSI poll update after connection.
2401      *
2402      * @param info WifiInfo instance pointing to the current connected network.
2403      */
updateScanDetailCacheFromWifiInfo(WifiInfo info)2404     public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
2405         WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
2406         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
2407         if (config != null && scanDetailCache != null) {
2408             ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
2409             if (scanDetail != null) {
2410                 ScanResult result = scanDetail.getScanResult();
2411                 long previousSeen = result.seen;
2412                 int previousRssi = result.level;
2413                 // Update the scan result
2414                 scanDetail.setSeen();
2415                 result.level = info.getRssi();
2416                 // Average the RSSI value
2417                 long maxAge = SCAN_RESULT_MAXIMUM_AGE_MS;
2418                 long age = result.seen - previousSeen;
2419                 if (previousSeen > 0 && age > 0 && age < maxAge / 2) {
2420                     // Average the RSSI with previously seen instances of this scan result
2421                     double alpha = 0.5 - (double) age / (double) maxAge;
2422                     result.level = (int) ((double) result.level * (1 - alpha)
2423                                         + (double) previousRssi * alpha);
2424                 }
2425                 if (mVerboseLoggingEnabled) {
2426                     Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
2427                             + " BSSID=" + result.BSSID
2428                             + " RSSI=" + result.level
2429                             + " for " + config.getKey());
2430                 }
2431             }
2432         }
2433     }
2434 
2435     /**
2436      * Save the ScanDetail to the ScanDetailCache of the given network.  This is used
2437      * by {@link PasspointNetworkNominator} for caching
2438      * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
2439      *
2440      * @param networkId The ID of the network to save ScanDetail to
2441      * @param scanDetail The ScanDetail to cache
2442      */
updateScanDetailForNetwork(int networkId, ScanDetail scanDetail)2443     public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
2444         WifiConfiguration network = getInternalConfiguredNetwork(networkId);
2445         if (network == null) {
2446             return;
2447         }
2448         saveToScanDetailCacheForNetwork(network, scanDetail);
2449     }
2450 
2451     /**
2452      * Helper method to check if the 2 provided networks can be linked or not.
2453      * Networks are considered for linking if:
2454      * 1. Share the same GW MAC address.
2455      * 2. Scan results for the networks have AP's with MAC address which differ only in the last
2456      * nibble.
2457      *
2458      * @param network1         WifiConfiguration corresponding to network 1.
2459      * @param network2         WifiConfiguration corresponding to network 2.
2460      * @param scanDetailCache1 ScanDetailCache entry for network 1.
2461      * @param scanDetailCache1 ScanDetailCache entry for network 2.
2462      * @return true if the networks should be linked, false if the networks should be unlinked.
2463      */
shouldNetworksBeLinked( WifiConfiguration network1, WifiConfiguration network2, ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2)2464     private boolean shouldNetworksBeLinked(
2465             WifiConfiguration network1, WifiConfiguration network2,
2466             ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
2467         // TODO (b/30706406): Link networks only with same passwords if the
2468         // |mOnlyLinkSameCredentialConfigurations| flag is set.
2469         if (mContext.getResources().getBoolean(
2470                 R.bool.config_wifi_only_link_same_credential_configurations)) {
2471             if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
2472                 if (mVerboseLoggingEnabled) {
2473                     Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch");
2474                 }
2475                 return false;
2476             }
2477         }
2478         if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
2479             // If both default GW are known, link only if they are equal
2480             if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) {
2481                 if (mVerboseLoggingEnabled) {
2482                     Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
2483                             + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
2484                 }
2485                 return true;
2486             }
2487         } else {
2488             // We do not know BOTH default gateways hence we will try to link
2489             // hoping that WifiConfigurations are indeed behind the same gateway.
2490             // once both WifiConfiguration have been tried and thus once both default gateways
2491             // are known we will revisit the choice of linking them.
2492             if (scanDetailCache1 != null && scanDetailCache2 != null) {
2493                 for (String abssid : scanDetailCache1.keySet()) {
2494                     for (String bbssid : scanDetailCache2.keySet()) {
2495                         if (abssid.regionMatches(
2496                                 true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
2497                             // If first 16 ASCII characters of BSSID matches,
2498                             // we assume this is a DBDC.
2499                             if (mVerboseLoggingEnabled) {
2500                                 Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
2501                                         + network2.SSID + " and " + network1.SSID
2502                                         + " bssida " + abssid + " bssidb " + bbssid);
2503                             }
2504                             return true;
2505                         }
2506                     }
2507                 }
2508             }
2509         }
2510         return false;
2511     }
2512 
2513     /**
2514      * Helper methods to link 2 networks together.
2515      *
2516      * @param network1 WifiConfiguration corresponding to network 1.
2517      * @param network2 WifiConfiguration corresponding to network 2.
2518      */
linkNetworks(WifiConfiguration network1, WifiConfiguration network2)2519     private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2520         if (mVerboseLoggingEnabled) {
2521             Log.v(TAG, "linkNetworks will link " + network2.getKey()
2522                     + " and " + network1.getKey());
2523         }
2524         if (network2.linkedConfigurations == null) {
2525             network2.linkedConfigurations = new HashMap<>();
2526         }
2527         if (network1.linkedConfigurations == null) {
2528             network1.linkedConfigurations = new HashMap<>();
2529         }
2530         // TODO (b/30638473): This needs to become a set instead of map, but it will need
2531         // public interface changes and need some migration of existing store data.
2532         network2.linkedConfigurations.put(network1.getKey(), 1);
2533         network1.linkedConfigurations.put(network2.getKey(), 1);
2534     }
2535 
2536     /**
2537      * Helper methods to unlink 2 networks from each other.
2538      *
2539      * @param network1 WifiConfiguration corresponding to network 1.
2540      * @param network2 WifiConfiguration corresponding to network 2.
2541      */
unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2)2542     private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2543         if (network2.linkedConfigurations != null
2544                 && (network2.linkedConfigurations.get(network1.getKey()) != null)) {
2545             if (mVerboseLoggingEnabled) {
2546                 Log.v(TAG, "unlinkNetworks un-link " + network1.getKey()
2547                         + " from " + network2.getKey());
2548             }
2549             network2.linkedConfigurations.remove(network1.getKey());
2550         }
2551         if (network1.linkedConfigurations != null
2552                 && (network1.linkedConfigurations.get(network2.getKey()) != null)) {
2553             if (mVerboseLoggingEnabled) {
2554                 Log.v(TAG, "unlinkNetworks un-link " + network2.getKey()
2555                         + " from " + network1.getKey());
2556             }
2557             network1.linkedConfigurations.remove(network2.getKey());
2558         }
2559     }
2560 
2561     /**
2562      * This method runs through all the saved networks and checks if the provided network can be
2563      * linked with any of them.
2564      *
2565      * @param config WifiConfiguration object corresponding to the network that needs to be
2566      *               checked for potential links.
2567      */
attemptNetworkLinking(WifiConfiguration config)2568     private void attemptNetworkLinking(WifiConfiguration config) {
2569         // Only link WPA_PSK config.
2570         if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2571             return;
2572         }
2573         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2574         // Ignore configurations with large number of BSSIDs.
2575         if (scanDetailCache != null
2576                 && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2577             return;
2578         }
2579         for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
2580             if (linkConfig.getKey().equals(config.getKey())) {
2581                 continue;
2582             }
2583             if (linkConfig.ephemeral) {
2584                 continue;
2585             }
2586             // Network Selector will be allowed to dynamically jump from a linked configuration
2587             // to another, hence only link configurations that have WPA_PSK security type.
2588             if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2589                 continue;
2590             }
2591             ScanDetailCache linkScanDetailCache =
2592                     getScanDetailCacheForNetwork(linkConfig.networkId);
2593             // Ignore configurations with large number of BSSIDs.
2594             if (linkScanDetailCache != null
2595                     && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2596                 continue;
2597             }
2598             // Check if the networks should be linked/unlinked.
2599             if (shouldNetworksBeLinked(
2600                     config, linkConfig, scanDetailCache, linkScanDetailCache)) {
2601                 linkNetworks(config, linkConfig);
2602             } else {
2603                 unlinkNetworks(config, linkConfig);
2604             }
2605         }
2606     }
2607 
2608     /**
2609      * Retrieves a list of all the saved hidden networks for scans
2610      *
2611      * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2612      * networks, this list will be truncated and we might end up not sending the networks
2613      * with the highest chance of connecting to the firmware.
2614      * So, re-sort the network list based on the frequency of connection to those networks
2615      * and whether it was last seen in the scan results.
2616      *
2617      * @return list of networks in the order of priority.
2618      */
retrieveHiddenNetworkList()2619     public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
2620         List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2621         List<WifiConfiguration> networks = getConfiguredNetworks();
2622         // Remove any non hidden networks.
2623         networks.removeIf(config -> !config.hiddenSSID);
2624         networks.sort(mScanListComparator);
2625         // The most frequently connected network has the highest priority now.
2626         for (WifiConfiguration config : networks) {
2627             hiddenList.add(new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
2628         }
2629         return hiddenList;
2630     }
2631 
2632     /**
2633      * Check if the provided network was temporarily disabled by the user and still blocked.
2634      *
2635      * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru
2636      *                this API matched the WifiConfiguration.SSID rules, and thus be surrounded by
2637      *                quotes.
2638      * @return true if network is blocking, otherwise false.
2639      */
isNetworkTemporarilyDisabledByUser(String network)2640     public boolean isNetworkTemporarilyDisabledByUser(String network) {
2641         if (mUserTemporarilyDisabledList.isLocked(network)) {
2642             return true;
2643         }
2644         mUserTemporarilyDisabledList.remove(network);
2645         return false;
2646     }
2647 
2648     /**
2649      * User temporarily disable a network and will be block to auto-join when network is still
2650      * nearby.
2651      *
2652      * The network will be re-enabled when:
2653      * a) User select to connect the network.
2654      * b) The network is not in range for {@link #USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS}
2655      * c) Toggle wifi off, reset network settings or device reboot.
2656      *
2657      * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru
2658      *                this API matched the WifiConfiguration.SSID rules, and thus be surrounded by
2659      *                quotes.
2660      *        uid     UID of the calling process.
2661      */
userTemporarilyDisabledNetwork(String network, int uid)2662     public void userTemporarilyDisabledNetwork(String network, int uid) {
2663         mUserTemporarilyDisabledList.add(network, USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS);
2664         Log.d(TAG, "Temporarily disable network: " + network + " uid=" + uid + " num="
2665                 + mUserTemporarilyDisabledList.size());
2666         removeUserChoiceFromDisabledNetwork(network, uid);
2667     }
2668 
2669     /**
2670      * Update the user temporarily disabled network list with networks in range.
2671      * @param networks networks in range in String format, FQDN or SSID. And caller must ensure
2672      *                 that the SSID passed thru this API matched the WifiConfiguration.SSID rules,
2673      *                 and thus be surrounded by quotes.
2674      */
updateUserDisabledList(List<String> networks)2675     public void updateUserDisabledList(List<String> networks) {
2676         mUserTemporarilyDisabledList.update(new HashSet<>(networks));
2677     }
2678 
removeUserChoiceFromDisabledNetwork( @onNull String network, int uid)2679     private void removeUserChoiceFromDisabledNetwork(
2680             @NonNull String network, int uid) {
2681         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2682             if (TextUtils.equals(config.SSID, network) || TextUtils.equals(config.FQDN, network)) {
2683                 if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
2684                     mWifiInjector.getWifiMetrics().logUserActionEvent(
2685                             UserActionEvent.EVENT_DISCONNECT_WIFI, config.networkId);
2686                 }
2687                 removeConnectChoiceFromAllNetworks(config.getKey());
2688             }
2689         }
2690     }
2691 
2692     /**
2693      * User enabled network manually, maybe trigger by user select to connect network.
2694      * @param networkId enabled network id.
2695      */
userEnabledNetwork(int networkId)2696     public void userEnabledNetwork(int networkId) {
2697         WifiConfiguration configuration = getInternalConfiguredNetwork(networkId);
2698         if (configuration == null) {
2699             return;
2700         }
2701         String network;
2702         if (configuration.isPasspoint()) {
2703             network = configuration.FQDN;
2704         } else {
2705             network = configuration.SSID;
2706         }
2707         mUserTemporarilyDisabledList.remove(network);
2708         mWifiInjector.getBssidBlocklistMonitor().clearBssidBlocklistForSsid(configuration.SSID);
2709         Log.d(TAG, "Enable disabled network: " + network + " num="
2710                 + mUserTemporarilyDisabledList.size());
2711     }
2712 
2713     /**
2714      * Clear all user temporarily disabled networks.
2715      */
clearUserTemporarilyDisabledList()2716     public void clearUserTemporarilyDisabledList() {
2717         mUserTemporarilyDisabledList.clear();
2718     }
2719 
2720     /**
2721      * Resets all sim networks state.
2722      */
resetSimNetworks()2723     public void resetSimNetworks() {
2724         if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
2725         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2726             if (config.enterpriseConfig == null
2727                     || !config.enterpriseConfig.isAuthenticationSimBased()) {
2728                 continue;
2729             }
2730             if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) {
2731                 Pair<String, String> currentIdentity =
2732                         mWifiCarrierInfoManager.getSimIdentity(config);
2733                 if (mVerboseLoggingEnabled) {
2734                     Log.d(TAG, "New identity for config " + config + ": " + currentIdentity);
2735                 }
2736                 // Update the loaded config
2737                 if (currentIdentity == null) {
2738                     Log.d(TAG, "Identity is null");
2739                 } else {
2740                     config.enterpriseConfig.setIdentity(currentIdentity.first);
2741                 }
2742                 // do not reset anonymous identity since it may be dependent on user-entry
2743                 // (i.e. cannot re-request on every reboot/SIM re-entry)
2744             } else {
2745                 // reset identity as well: supplicant will ask us for it
2746                 config.enterpriseConfig.setIdentity("");
2747                 if (!WifiCarrierInfoManager.isAnonymousAtRealmIdentity(
2748                         config.enterpriseConfig.getAnonymousIdentity())) {
2749                     config.enterpriseConfig.setAnonymousIdentity("");
2750                 }
2751             }
2752         }
2753     }
2754 
2755     /**
2756      * Helper method to perform the following operations during user switch/unlock:
2757      * - Remove private networks of the old user.
2758      * - Load from the new user store file.
2759      * - Save the store files again to migrate any user specific networks from the shared store
2760      *   to user store.
2761      * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
2762      * should ensure that the stores are accessible before invocation.
2763      *
2764      * @param userId The identifier of the new foreground user, after the unlock or switch.
2765      */
handleUserUnlockOrSwitch(int userId)2766     private void handleUserUnlockOrSwitch(int userId) {
2767         if (mVerboseLoggingEnabled) {
2768             Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
2769         }
2770         // Switch out the user store file.
2771         if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
2772             saveToStore(true);
2773             mPendingUnlockStoreRead = false;
2774         }
2775     }
2776 
2777     /**
2778      * Handles the switch to a different foreground user:
2779      * - Flush the current state to the old user's store file.
2780      * - Switch the user specific store file.
2781      * - Reload the networks from the store files (shared & user).
2782      * - Write the store files to move any user specific private networks from shared store to user
2783      *   store.
2784      *
2785      * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked.
2786      *
2787      * @param userId The identifier of the new foreground user, after the switch.
2788      * @return List of network ID's of all the private networks of the old user which will be
2789      * removed from memory.
2790      */
handleUserSwitch(int userId)2791     public Set<Integer> handleUserSwitch(int userId) {
2792         if (mVerboseLoggingEnabled) {
2793             Log.v(TAG, "Handling user switch for " + userId);
2794         }
2795         if (userId == mCurrentUserId) {
2796             Log.w(TAG, "User already in foreground " + userId);
2797             return new HashSet<>();
2798         }
2799         if (mPendingStoreRead) {
2800             Log.w(TAG, "User switch before store is read!");
2801             mConfiguredNetworks.setNewUser(userId);
2802             mCurrentUserId = userId;
2803             // Reset any state from previous user unlock.
2804             mDeferredUserUnlockRead = false;
2805             // Cannot read data from new user's CE store file before they log-in.
2806             mPendingUnlockStoreRead = true;
2807             return new HashSet<>();
2808         }
2809         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
2810             saveToStore(true);
2811         }
2812         // Remove any private networks of the old user before switching the userId.
2813         Set<Integer> removedNetworkIds = clearInternalDataForCurrentUser();
2814         mConfiguredNetworks.setNewUser(userId);
2815         mCurrentUserId = userId;
2816 
2817         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
2818             handleUserUnlockOrSwitch(mCurrentUserId);
2819         } else {
2820             // Cannot read data from new user's CE store file before they log-in.
2821             mPendingUnlockStoreRead = true;
2822             Log.i(TAG, "Waiting for user unlock to load from store");
2823         }
2824         return removedNetworkIds;
2825     }
2826 
2827     /**
2828      * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
2829      * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
2830      *
2831      * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked.
2832      *
2833      * @param userId The identifier of the user that unlocked.
2834      */
handleUserUnlock(int userId)2835     public void handleUserUnlock(int userId) {
2836         if (mVerboseLoggingEnabled) {
2837             Log.v(TAG, "Handling user unlock for " + userId);
2838         }
2839         if (userId != mCurrentUserId) {
2840             Log.e(TAG, "Ignore user unlock for non current user " + userId);
2841             return;
2842         }
2843         if (mPendingStoreRead) {
2844             Log.w(TAG, "Ignore user unlock until store is read!");
2845             mDeferredUserUnlockRead = true;
2846             return;
2847         }
2848         if (mPendingUnlockStoreRead) {
2849             handleUserUnlockOrSwitch(mCurrentUserId);
2850         }
2851     }
2852 
2853     /**
2854      * Handles the stop of foreground user. This is needed to write the store file to flush
2855      * out any pending data before the user's CE store storage is unavailable.
2856      *
2857      * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked.
2858      *
2859      * @param userId The identifier of the user that stopped.
2860      */
handleUserStop(int userId)2861     public void handleUserStop(int userId) {
2862         if (mVerboseLoggingEnabled) {
2863             Log.v(TAG, "Handling user stop for " + userId);
2864         }
2865         if (userId == mCurrentUserId
2866                 && mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
2867             saveToStore(true);
2868             clearInternalDataForCurrentUser();
2869         }
2870     }
2871 
2872     /**
2873      * Helper method to clear internal databases.
2874      * This method clears the:
2875      *  - List of configured networks.
2876      *  - Map of scan detail caches.
2877      *  - List of deleted ephemeral networks.
2878      */
clearInternalData()2879     private void clearInternalData() {
2880         localLog("clearInternalData: Clearing all internal data");
2881         mConfiguredNetworks.clear();
2882         mUserTemporarilyDisabledList.clear();
2883         mRandomizedMacAddressMapping.clear();
2884         mScanDetailCaches.clear();
2885         clearLastSelectedNetwork();
2886     }
2887 
2888     /**
2889      * Helper method to clear internal databases of the specified user.
2890      * This method clears the:
2891      *  - Private configured configured networks of the specified user.
2892      *  - Map of scan detail caches.
2893      *  - List of deleted ephemeral networks.
2894      *
2895      * @return List of network ID's of all the private networks of the old user which will be
2896      * removed from memory.
2897      */
clearInternalDataForCurrentUser()2898     private Set<Integer> clearInternalDataForCurrentUser() {
2899         localLog("clearInternalUserData: Clearing user internal data for " + mCurrentUserId);
2900         Set<Integer> removedNetworkIds = new HashSet<>();
2901         // Remove any private networks of the old user before switching the userId.
2902         for (WifiConfiguration config : getConfiguredNetworks()) {
2903             if ((!config.shared && !mWifiPermissionsUtil
2904                     .doesUidBelongToCurrentUser(config.creatorUid))
2905                     || config.ephemeral) {
2906                 removedNetworkIds.add(config.networkId);
2907                 localLog("clearInternalUserData: removed config."
2908                         + " netId=" + config.networkId
2909                         + " configKey=" + config.getKey());
2910                 mConfiguredNetworks.remove(config.networkId);
2911                 for (OnNetworkUpdateListener listener : mListeners) {
2912                     listener.onNetworkRemoved(
2913                             createExternalWifiConfiguration(config, true, Process.WIFI_UID));
2914                 }
2915             }
2916         }
2917         if (!removedNetworkIds.isEmpty()) {
2918             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED);
2919         }
2920         mUserTemporarilyDisabledList.clear();
2921         mScanDetailCaches.clear();
2922         clearLastSelectedNetwork();
2923         return removedNetworkIds;
2924     }
2925 
2926     /**
2927      * Helper function to populate the internal (in-memory) data from the retrieved shared store
2928      * (file) data.
2929      *
2930      * @param configurations list of configurations retrieved from store.
2931      */
loadInternalDataFromSharedStore( List<WifiConfiguration> configurations, Map<String, String> macAddressMapping)2932     private void loadInternalDataFromSharedStore(
2933             List<WifiConfiguration> configurations,
2934             Map<String, String> macAddressMapping) {
2935         for (WifiConfiguration configuration : configurations) {
2936             configuration.networkId = mNextNetworkId++;
2937             if (mVerboseLoggingEnabled) {
2938                 Log.v(TAG, "Adding network from shared store " + configuration.getKey());
2939             }
2940             try {
2941                 mConfiguredNetworks.put(configuration);
2942             } catch (IllegalArgumentException e) {
2943                 Log.e(TAG, "Failed to add network to config map", e);
2944             }
2945         }
2946         mRandomizedMacAddressMapping.putAll(macAddressMapping);
2947     }
2948 
2949     /**
2950      * Helper function to populate the internal (in-memory) data from the retrieved user store
2951      * (file) data.
2952      *
2953      * @param configurations list of configurations retrieved from store.
2954      */
loadInternalDataFromUserStore(List<WifiConfiguration> configurations)2955     private void loadInternalDataFromUserStore(List<WifiConfiguration> configurations) {
2956         for (WifiConfiguration configuration : configurations) {
2957             configuration.networkId = mNextNetworkId++;
2958             if (mVerboseLoggingEnabled) {
2959                 Log.v(TAG, "Adding network from user store " + configuration.getKey());
2960             }
2961             try {
2962                 mConfiguredNetworks.put(configuration);
2963             } catch (IllegalArgumentException e) {
2964                 Log.e(TAG, "Failed to add network to config map", e);
2965             }
2966 
2967             if (configuration.isMostRecentlyConnected) {
2968                 mLruConnectionTracker.addNetwork(configuration);
2969             }
2970         }
2971     }
2972 
2973     /**
2974      * Initializes the randomized MAC address for an internal WifiConfiguration depending on
2975      * whether it should use aggressive randomization.
2976      * @param config
2977      */
initRandomizedMacForInternalConfig(WifiConfiguration internalConfig)2978     private void initRandomizedMacForInternalConfig(WifiConfiguration internalConfig) {
2979         MacAddress randomizedMac = shouldUseAggressiveRandomization(internalConfig)
2980                 ? MacAddressUtils.createRandomUnicastAddress()
2981                 : getPersistentMacAddress(internalConfig);
2982         if (randomizedMac != null) {
2983             internalConfig.setRandomizedMacAddress(randomizedMac);
2984         }
2985     }
2986 
2987     /**
2988      * Assign randomized MAC addresses for configured networks.
2989      * This is needed to generate persistent randomized MAC address for existing networks when
2990      * a device updates to Q+ for the first time since we are not calling addOrUpdateNetwork when
2991      * we load configuration at boot.
2992      */
generateRandomizedMacAddresses()2993     private void generateRandomizedMacAddresses() {
2994         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2995             if (DEFAULT_MAC_ADDRESS.equals(config.getRandomizedMacAddress())) {
2996                 initRandomizedMacForInternalConfig(config);
2997             }
2998         }
2999     }
3000 
3001     /**
3002      * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
3003      * data.
3004      * This method:
3005      * 1. Clears all existing internal data.
3006      * 2. Sends out the networks changed broadcast after loading all the data.
3007      *
3008      * @param sharedConfigurations list of network configurations retrieved from shared store.
3009      * @param userConfigurations list of network configurations retrieved from user store.
3010      * @param macAddressMapping
3011      */
loadInternalData( List<WifiConfiguration> sharedConfigurations, List<WifiConfiguration> userConfigurations, Map<String, String> macAddressMapping)3012     private void loadInternalData(
3013             List<WifiConfiguration> sharedConfigurations,
3014             List<WifiConfiguration> userConfigurations,
3015             Map<String, String> macAddressMapping) {
3016         // Clear out all the existing in-memory lists and load the lists from what was retrieved
3017         // from the config store.
3018         clearInternalData();
3019         loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping);
3020         loadInternalDataFromUserStore(userConfigurations);
3021         generateRandomizedMacAddresses();
3022         if (mConfiguredNetworks.sizeForAllUsers() == 0) {
3023             Log.w(TAG, "No stored networks found.");
3024         }
3025         // reset identity & anonymous identity for networks using SIM-based authentication
3026         // on load (i.e. boot) so that if the user changed SIMs while the device was powered off,
3027         // we do not reuse stale credentials that would lead to authentication failure.
3028         resetSimNetworks();
3029         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_ADDED);
3030         mPendingStoreRead = false;
3031     }
3032 
3033     /**
3034      * Read the config store and load the in-memory lists from the store data retrieved and sends
3035      * out the networks changed broadcast.
3036      *
3037      * This reads all the network configurations from:
3038      * 1. Shared WifiConfigStore.xml
3039      * 2. User WifiConfigStore.xml
3040      *
3041      * @return true on success or not needed (fresh install), false otherwise.
3042      */
loadFromStore()3043     public boolean loadFromStore() {
3044         // If the user unlock comes in before we load from store, which means the user store have
3045         // not been setup yet for the current user. Setup the user store before the read so that
3046         // configurations for the current user will also being loaded.
3047         if (mDeferredUserUnlockRead) {
3048             Log.i(TAG, "Handling user unlock before loading from store.");
3049             List<WifiConfigStore.StoreFile> userStoreFiles =
3050                     WifiConfigStore.createUserFiles(
3051                             mCurrentUserId, mFrameworkFacade.isNiapModeOn(mContext));
3052             if (userStoreFiles == null) {
3053                 Log.wtf(TAG, "Failed to create user store files");
3054                 return false;
3055             }
3056             mWifiConfigStore.setUserStores(userStoreFiles);
3057             mDeferredUserUnlockRead = false;
3058         }
3059         try {
3060             mWifiConfigStore.read();
3061         } catch (IOException | IllegalStateException e) {
3062             Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
3063             return false;
3064         } catch (XmlPullParserException e) {
3065             Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
3066             return false;
3067         }
3068         loadInternalData(mNetworkListSharedStoreData.getConfigurations(),
3069                 mNetworkListUserStoreData.getConfigurations(),
3070                 mRandomizedMacStoreData.getMacMapping());
3071         return true;
3072     }
3073 
3074     /**
3075      * Read the user config store and load the in-memory lists from the store data retrieved and
3076      * sends out the networks changed broadcast.
3077      * This should be used for all user switches/unlocks to only load networks from the user
3078      * specific store and avoid reloading the shared networks.
3079      *
3080      * This reads all the network configurations from:
3081      * 1. User WifiConfigStore.xml
3082      *
3083      * @param userId The identifier of the foreground user.
3084      * @return true on success, false otherwise.
3085      */
loadFromUserStoreAfterUnlockOrSwitch(int userId)3086     private boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
3087         try {
3088             List<WifiConfigStore.StoreFile> userStoreFiles =
3089                     WifiConfigStore.createUserFiles(
3090                             userId, mFrameworkFacade.isNiapModeOn(mContext));
3091             if (userStoreFiles == null) {
3092                 Log.e(TAG, "Failed to create user store files");
3093                 return false;
3094             }
3095             mWifiConfigStore.switchUserStoresAndRead(userStoreFiles);
3096         } catch (IOException | IllegalStateException e) {
3097             Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
3098             return false;
3099         } catch (XmlPullParserException e) {
3100             Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are "
3101                     + "lost!", e);
3102             return false;
3103         }
3104         loadInternalDataFromUserStore(mNetworkListUserStoreData.getConfigurations());
3105         return true;
3106     }
3107 
3108     /**
3109      * Save the current snapshot of the in-memory lists to the config store.
3110      *
3111      * @param forceWrite Whether the write needs to be forced or not.
3112      * @return Whether the write was successful or not, this is applicable only for force writes.
3113      */
saveToStore(boolean forceWrite)3114     public boolean saveToStore(boolean forceWrite) {
3115         if (mPendingStoreRead) {
3116             Log.e(TAG, "Cannot save to store before store is read!");
3117             return false;
3118         }
3119         ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
3120         ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
3121         // List of network IDs for legacy Passpoint configuration to be removed.
3122         List<Integer> legacyPasspointNetId = new ArrayList<>();
3123         for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
3124             // Ignore ephemeral networks and non-legacy Passpoint configurations.
3125             if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
3126                 continue;
3127             }
3128 
3129             // Migrate the legacy Passpoint configurations owned by the current user to
3130             // {@link PasspointManager}.
3131             if (config.isLegacyPasspointConfig && !mWifiPermissionsUtil
3132                     .doesUidBelongToCurrentUser(config.creatorUid)) {
3133                 legacyPasspointNetId.add(config.networkId);
3134                 // Migrate the legacy Passpoint configuration and add it to PasspointManager.
3135                 if (!PasspointManager.addLegacyPasspointConfig(config)) {
3136                     Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
3137                 }
3138                 // This will prevent adding |config| to the |sharedConfigurations|.
3139                 continue;
3140             }
3141 
3142             config.isMostRecentlyConnected =
3143                     mLruConnectionTracker.isMostRecentlyConnected(config);
3144 
3145             // We push all shared networks & private networks not belonging to the current
3146             // user to the shared store. Ideally, private networks for other users should
3147             // not even be in memory,
3148             // But, this logic is in place to deal with store migration from N to O
3149             // because all networks were previously stored in a central file. We cannot
3150             // write these private networks to the user specific store until the corresponding
3151             // user logs in.
3152             if (config.shared || !mWifiPermissionsUtil
3153                     .doesUidBelongToCurrentUser(config.creatorUid)) {
3154                 sharedConfigurations.add(config);
3155             } else {
3156                 userConfigurations.add(config);
3157             }
3158         }
3159 
3160         // Remove the configurations for migrated Passpoint configurations.
3161         for (int networkId : legacyPasspointNetId) {
3162             mConfiguredNetworks.remove(networkId);
3163         }
3164 
3165         // Setup store data for write.
3166         mNetworkListSharedStoreData.setConfigurations(sharedConfigurations);
3167         mNetworkListUserStoreData.setConfigurations(userConfigurations);
3168         mRandomizedMacStoreData.setMacMapping(mRandomizedMacAddressMapping);
3169 
3170         try {
3171             mWifiConfigStore.write(forceWrite);
3172         } catch (IOException | IllegalStateException e) {
3173             Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
3174             return false;
3175         } catch (XmlPullParserException e) {
3176             Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
3177             return false;
3178         }
3179         return true;
3180     }
3181 
3182     /**
3183      * Helper method for logging into local log buffer.
3184      */
localLog(String s)3185     private void localLog(String s) {
3186         if (mLocalLog != null) {
3187             mLocalLog.log(s);
3188         }
3189     }
3190 
3191     /**
3192      * Dump the local log buffer and other internal state of WifiConfigManager.
3193      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)3194     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3195         pw.println("Dump of WifiConfigManager");
3196         pw.println("WifiConfigManager - Log Begin ----");
3197         mLocalLog.dump(fd, pw, args);
3198         pw.println("WifiConfigManager - Log End ----");
3199         pw.println("WifiConfigManager - Configured networks Begin ----");
3200         for (WifiConfiguration network : getInternalConfiguredNetworks()) {
3201             pw.println(network);
3202         }
3203         pw.println("WifiConfigManager - Configured networks End ----");
3204         pw.println("WifiConfigManager - ConfigurationMap Begin ----");
3205         mConfiguredNetworks.dump(fd, pw, args);
3206         pw.println("WifiConfigManager - ConfigurationMap End ----");
3207         pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
3208         pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
3209         pw.println("WifiConfigManager - PNO scan frequency culling enabled = "
3210                 + mContext.getResources().getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled));
3211         pw.println("WifiConfigManager - PNO scan recency sorting enabled = "
3212                 + mContext.getResources().getBoolean(R.bool.config_wifiPnoRecencySortingEnabled));
3213         mWifiConfigStore.dump(fd, pw, args);
3214         mWifiCarrierInfoManager.dump(fd, pw, args);
3215     }
3216 
3217     /**
3218      * Returns true if the given uid has permission to add, update or remove proxy settings
3219      */
canModifyProxySettings(int uid, String packageName)3220     private boolean canModifyProxySettings(int uid, String packageName) {
3221         final boolean isDeviceOwner = mWifiPermissionsUtil.isDeviceOwner(uid, packageName);
3222         final boolean isProfileOwner = mWifiPermissionsUtil.isProfileOwner(uid, packageName);
3223         final boolean hasNetworkSettingsPermission =
3224                 mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
3225         final boolean hasNetworkSetupWizardPermission =
3226                 mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid);
3227         final boolean hasNetworkManagedProvisioningPermission =
3228                 mWifiPermissionsUtil.checkNetworkManagedProvisioningPermission(uid);
3229         // If |uid| corresponds to the device owner, allow all modifications.
3230         if (isProfileOwner || isDeviceOwner || hasNetworkSettingsPermission
3231                 || hasNetworkSetupWizardPermission || hasNetworkManagedProvisioningPermission) {
3232             return true;
3233         }
3234         if (mVerboseLoggingEnabled) {
3235             Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
3236                     + " hasNetworkSettings=" + hasNetworkSettingsPermission
3237                     + " hasNetworkSetupWizard=" + hasNetworkSetupWizardPermission
3238                     + " DeviceOwner=" + isDeviceOwner
3239                     + " ProfileOwner=" + isProfileOwner);
3240         }
3241         return false;
3242     }
3243 
3244     /**
3245      * Add the network update event listener
3246      */
addOnNetworkUpdateListener(OnNetworkUpdateListener listener)3247     public void addOnNetworkUpdateListener(OnNetworkUpdateListener listener) {
3248         mListeners.add(listener);
3249     }
3250 
3251     /**
3252      * Set extra failure reason for given config. Used to surface extra failure details to the UI
3253      * @param netId The network ID of the config to set the extra failure reason for
3254      * @param reason the WifiConfiguration.ExtraFailureReason failure code representing the most
3255      *               recent failure reason
3256      */
setRecentFailureAssociationStatus(int netId, int reason)3257     public void setRecentFailureAssociationStatus(int netId, int reason) {
3258         WifiConfiguration config = getInternalConfiguredNetwork(netId);
3259         if (config == null) {
3260             return;
3261         }
3262         config.recentFailure.setAssociationStatus(reason);
3263     }
3264 
3265     /**
3266      * @param netId The network ID of the config to clear the extra failure reason from
3267      */
clearRecentFailureReason(int netId)3268     public void clearRecentFailureReason(int netId) {
3269         WifiConfiguration config = getInternalConfiguredNetwork(netId);
3270         if (config == null) {
3271             return;
3272         }
3273         config.recentFailure.clear();
3274     }
3275 
3276     /**
3277      * Find the highest RSSI among all valid scanDetails in current network's scanDetail cache.
3278      * If scanDetail is too old, it is not considered to be valid.
3279      * @param netId The network ID of the config to find scan RSSI
3280      * @params scanRssiValidTimeMs The valid time for scan RSSI
3281      * @return The highest RSSI in dBm found with current network's scanDetail cache.
3282      */
findScanRssi(int netId, int scanRssiValidTimeMs)3283     public int findScanRssi(int netId, int scanRssiValidTimeMs) {
3284         int scanMaxRssi = WifiInfo.INVALID_RSSI;
3285         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(netId);
3286         if (scanDetailCache == null || scanDetailCache.size() == 0) return scanMaxRssi;
3287         long nowInMillis = mClock.getWallClockMillis();
3288         for (ScanDetail scanDetail : scanDetailCache.values()) {
3289             ScanResult result = scanDetail.getScanResult();
3290             if (result == null) continue;
3291             boolean valid = (nowInMillis - result.seen) < scanRssiValidTimeMs;
3292 
3293             if (valid) {
3294                 scanMaxRssi = Math.max(scanMaxRssi, result.level);
3295             }
3296         }
3297         return scanMaxRssi;
3298     }
3299 
3300     public Comparator<WifiConfiguration> getScanListComparator() {
3301         return mScanListComparator;
3302     }
3303 }
3304