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