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