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