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