• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
20 import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_INVALID_CONFIGURATION;
21 import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_INVALID_CONFIGURATION_ENTERPRISE;
22 import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_NO_PERMISSION_MODIFY_CONFIG;
23 import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_SUCCESS;
24 import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE;
25 
26 import android.Manifest;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.app.ActivityManager;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.pm.ApplicationInfo;
34 import android.net.DhcpOption;
35 import android.net.IpConfiguration;
36 import android.net.MacAddress;
37 import android.net.ProxyInfo;
38 import android.net.StaticIpConfiguration;
39 import android.net.wifi.ScanResult;
40 import android.net.wifi.SecurityParams;
41 import android.net.wifi.WifiConfiguration;
42 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
43 import android.net.wifi.WifiEnterpriseConfig;
44 import android.net.wifi.WifiInfo;
45 import android.net.wifi.WifiManager;
46 import android.net.wifi.WifiScanner;
47 import android.net.wifi.WifiSsid;
48 import android.os.Process;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.provider.Settings;
52 import android.telephony.SubscriptionManager;
53 import android.telephony.TelephonyManager;
54 import android.text.TextUtils;
55 import android.util.ArraySet;
56 import android.util.LocalLog;
57 import android.util.Log;
58 import android.util.Pair;
59 
60 import com.android.internal.annotations.VisibleForTesting;
61 import com.android.net.module.util.MacAddressUtils;
62 import com.android.server.wifi.hotspot2.PasspointManager;
63 import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent;
64 import com.android.server.wifi.util.CertificateSubjectInfo;
65 import com.android.server.wifi.util.LruConnectionTracker;
66 import com.android.server.wifi.util.MissingCounterTimerLockList;
67 import com.android.server.wifi.util.WifiPermissionsUtil;
68 import com.android.wifi.resources.R;
69 
70 import org.xmlpull.v1.XmlPullParserException;
71 
72 import java.io.FileDescriptor;
73 import java.io.IOException;
74 import java.io.PrintWriter;
75 import java.security.cert.CertificateParsingException;
76 import java.security.cert.X509Certificate;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.Collection;
80 import java.util.Collections;
81 import java.util.Comparator;
82 import java.util.HashMap;
83 import java.util.HashSet;
84 import java.util.LinkedHashSet;
85 import java.util.List;
86 import java.util.Map;
87 import java.util.Objects;
88 import java.util.Set;
89 import java.util.stream.Collectors;
90 
91 /**
92  * This class provides the APIs to manage configured Wi-Fi networks.
93  * It deals with the following:
94  * - Maintaining a list of configured networks for quick access.
95  * - Persisting the configurations to store when required.
96  * - Supporting WifiManager Public API calls:
97  *   > addOrUpdateNetwork()
98  *   > removeNetwork()
99  *   > enableNetwork()
100  *   > disableNetwork()
101  * - Handle user switching on multi-user devices.
102  *
103  * All network configurations retrieved from this class are copies of the original configuration
104  * stored in the internal database. So, any updates to the retrieved configuration object are
105  * meaningless and will not be reflected in the original database.
106  * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored
107  * in the internal database. Any configuration updates should be triggered with appropriate helper
108  * methods of this class using the configuration's unique networkId.
109  *
110  * NOTE: These API's are not thread safe and should only be used from the main Wifi thread.
111  */
112 public class WifiConfigManager {
113     /**
114      * String used to mask passwords to public interface.
115      */
116     @VisibleForTesting
117     public static final String PASSWORD_MASK = "*";
118 
119     /**
120      * Interface for other modules to listen to the network updated events.
121      * Note: Credentials are masked to avoid accidentally sending credentials outside the stack.
122      * Use WifiConfigManager#getConfiguredNetworkWithPassword() to retrieve credentials.
123      */
124     public interface OnNetworkUpdateListener {
125         /**
126          * Invoked on network being added.
127          */
onNetworkAdded(@onNull WifiConfiguration config)128         default void onNetworkAdded(@NonNull WifiConfiguration config) { };
129         /**
130          * Invoked on network being enabled.
131          */
onNetworkEnabled(@onNull WifiConfiguration config)132         default void onNetworkEnabled(@NonNull WifiConfiguration config) { };
133         /**
134          * Invoked on network being permanently disabled.
135          */
onNetworkPermanentlyDisabled(@onNull WifiConfiguration config, int disableReason)136         default void onNetworkPermanentlyDisabled(@NonNull WifiConfiguration config,
137                 int disableReason) { };
138         /**
139          * Invoked on network being removed.
140          */
onNetworkRemoved(@onNull WifiConfiguration config)141         default void onNetworkRemoved(@NonNull WifiConfiguration config) { };
142         /**
143          * Invoked on network being temporarily disabled.
144          */
onNetworkTemporarilyDisabled(@onNull WifiConfiguration config, int disableReason)145         default void onNetworkTemporarilyDisabled(@NonNull WifiConfiguration config,
146                 int disableReason) { };
147         /**
148          * Invoked on network being updated.
149          *
150          * @param newConfig Updated WifiConfiguration object.
151          * @param oldConfig Prev WifiConfiguration object.
152          */
onNetworkUpdated( @onNull WifiConfiguration newConfig, @NonNull WifiConfiguration oldConfig)153         default void onNetworkUpdated(
154                 @NonNull WifiConfiguration newConfig, @NonNull WifiConfiguration oldConfig) { };
155 
156         /**
157          * Invoked when user connect choice is set.
158          * @param networks List of network profiles to set user connect choice.
159          * @param choiceKey Network key {@link WifiConfiguration#getProfileKey()}
160          *                  corresponding to the network which the user chose.
161          * @param rssi the signal strength of the user selected network
162          */
onConnectChoiceSet(@onNull List<WifiConfiguration> networks, String choiceKey, int rssi)163         default void onConnectChoiceSet(@NonNull List<WifiConfiguration> networks,
164                 String choiceKey, int rssi) { }
165 
166         /**
167          * Invoked when user connect choice is removed.
168          * @param choiceKey The network profile key of the user connect choice that was removed.
169          */
onConnectChoiceRemoved(@onNull String choiceKey)170         default void onConnectChoiceRemoved(@NonNull String choiceKey){ }
171 
172         /**
173          * Invoke when security params changed, especially when NetworkTransitionDisable event
174          * received
175          * @param oldConfig The original WifiConfiguration
176          * @param securityParams the updated securityParams
177          */
onSecurityParamsUpdate(@onNull WifiConfiguration oldConfig, List<SecurityParams> securityParams)178         default void onSecurityParamsUpdate(@NonNull WifiConfiguration oldConfig,
179                 List<SecurityParams> securityParams) { }
180     }
181     /**
182      * Max size of scan details to cache in {@link #mScanDetailCaches}.
183      */
184     @VisibleForTesting
185     public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192;
186     /**
187      * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds
188      * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some
189      * buffer time before the next eviction.
190      */
191     @VisibleForTesting
192     public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128;
193     /**
194      * Link networks only if they have less than this number of scan cache entries.
195      */
196     @VisibleForTesting
197     public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6;
198     /**
199      * Link networks only if the bssid in scan results for the networks match in the first
200      * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7"
201      */
202     @VisibleForTesting
203     public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16;
204     /**
205      * Log tag for this class.
206      */
207     private static final String TAG = "WifiConfigManager";
208     /**
209      * Maximum age of scan results that can be used for averaging out RSSI value.
210      */
211     private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
212 
213     /**
214      * Enforce a minimum time to wait after the last disconnect to generate a new randomized MAC,
215      * since IPv6 networks don't provide the DHCP lease duration.
216      * 4 hours.
217      */
218     @VisibleForTesting
219     protected static final long NON_PERSISTENT_MAC_WAIT_AFTER_DISCONNECT_MS = 4 * 60 * 60 * 1000;
220     @VisibleForTesting
221     protected static final long NON_PERSISTENT_MAC_REFRESH_MS_MIN = 30 * 60 * 1000; // 30 minutes
222     @VisibleForTesting
223     protected static final long NON_PERSISTENT_MAC_REFRESH_MS_MAX = 24 * 60 * 60 * 1000; // 24 hours
224 
225     private static final MacAddress DEFAULT_MAC_ADDRESS =
226             MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
227 
228     private static final String VRRP_MAC_ADDRESS_PREFIX = "00:00:5E:00:01";
229 
230     /**
231      * Expiration timeout for user disconnect network. (1 hour)
232      */
233     @VisibleForTesting
234     public static final long USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS = (long) 1000 * 60 * 60;
235 
236     @VisibleForTesting
237     public static final int SCAN_RESULT_MISSING_COUNT_THRESHOLD = 1;
238     @VisibleForTesting
239     protected static final String NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG =
240             "non_persistent_mac_randomization_force_enabled";
241     private static final int NON_CARRIER_MERGED_NETWORKS_SCAN_CACHE_QUERY_DURATION_MS =
242             10 * 60 * 1000; // 10 minutes
243 
244     /**
245      * General sorting algorithm of all networks for scanning purposes:
246      * Place the configurations in ascending order of their AgeIndex. AgeIndex is based on most
247      * recently connected order. The lower the more recently connected.
248      * If networks have the same AgeIndex, place the configurations with
249      * |lastSeenInQualifiedNetworkSelection| set first.
250      */
251     private final WifiConfigurationUtil.WifiConfigurationComparator mScanListComparator =
252             new WifiConfigurationUtil.WifiConfigurationComparator() {
253                 @Override
254                 public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
255                     int indexA = mLruConnectionTracker.getAgeIndexOfNetwork(a);
256                     int indexB = mLruConnectionTracker.getAgeIndexOfNetwork(b);
257                     if (indexA != indexB) {
258                         return Integer.compare(indexA, indexB);
259                     } else {
260                         boolean isConfigALastSeen =
261                                 a.getNetworkSelectionStatus()
262                                         .getSeenInLastQualifiedNetworkSelection();
263                         boolean isConfigBLastSeen =
264                                 b.getNetworkSelectionStatus()
265                                         .getSeenInLastQualifiedNetworkSelection();
266                         return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
267                     }
268                 }
269             };
270 
271     /**
272      * List of external dependencies for WifiConfigManager.
273      */
274     private final Context mContext;
275     private final WifiInjector mWifiInjector;
276     private final Clock mClock;
277     private final UserManager mUserManager;
278     private final BackupManagerProxy mBackupManagerProxy;
279     private final WifiKeyStore mWifiKeyStore;
280     private final WifiConfigStore mWifiConfigStore;
281     private final WifiPermissionsUtil mWifiPermissionsUtil;
282     private final MacAddressUtil mMacAddressUtil;
283     private final WifiMetrics mWifiMetrics;
284     private final WifiBlocklistMonitor mWifiBlocklistMonitor;
285     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
286     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
287     private final WifiScoreCard mWifiScoreCard;
288     // Keep order of network connection.
289     private final LruConnectionTracker mLruConnectionTracker;
290     private final BuildProperties mBuildProperties;
291 
292     /**
293      * Local log used for debugging any WifiConfigManager issues.
294      */
295     private final LocalLog mLocalLog;
296     /**
297      * Map of configured networks with network id as the key.
298      */
299     private final ConfigurationMap mConfiguredNetworks;
300     /**
301      * Stores a map of NetworkId to ScanDetailCache.
302      */
303     private final Map<Integer, ScanDetailCache> mScanDetailCaches;
304     /**
305      * Framework keeps a list of networks that where temporarily disabled by user,
306      * framework knows not to autoconnect again even if the app/scorer recommends it.
307      * Network will be based on FQDN for passpoint and SSID for non-passpoint.
308      * List will be deleted when Wifi turn off, device restart or network settings reset.
309      * Also when user manfully select to connect network will unblock that network.
310      */
311     private final MissingCounterTimerLockList<String> mUserTemporarilyDisabledList;
312     private final NonCarrierMergedNetworksStatusTracker mNonCarrierMergedNetworksStatusTracker;
313 
314 
315     /**
316      * Framework keeps a mapping from configKey to the randomized MAC address so that
317      * when a user forgets a network and thne adds it back, the same randomized MAC address
318      * will get used.
319      */
320     private final Map<String, String> mRandomizedMacAddressMapping;
321 
322     /**
323      * Store the network update listeners.
324      */
325     private final Set<OnNetworkUpdateListener> mListeners;
326 
327     private final FrameworkFacade mFrameworkFacade;
328     private final DeviceConfigFacade mDeviceConfigFacade;
329 
330     /**
331      * Verbose logging flag. Toggled by developer options.
332      */
333     private boolean mVerboseLoggingEnabled = false;
334     /**
335      * Current logged in user ID.
336      */
337     private int mCurrentUserId = UserHandle.SYSTEM.getIdentifier();
338     /**
339      * Flag to indicate that the new user's store has not yet been read since user switch.
340      * Initialize this flag to |true| to trigger a read on the first user unlock after
341      * bootup.
342      */
343     private boolean mPendingUnlockStoreRead = true;
344     /**
345      * Flag to indicate if we have performed a read from store at all. This is used to gate
346      * any user unlock/switch operations until we read the store (Will happen if wifi is disabled
347      * when user updates from N to O).
348      */
349     private boolean mPendingStoreRead = true;
350     /**
351      * Flag to indicate if the user unlock was deferred until the store load occurs.
352      */
353     private boolean mDeferredUserUnlockRead = false;
354     /**
355      * This is keeping track of the next network ID to be assigned. Any new networks will be
356      * assigned |mNextNetworkId| as network ID.
357      */
358     private int mNextNetworkId = 0;
359     /**
360      * This is used to remember which network was selected successfully last by an app. This is set
361      * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
362      * This is the only way for an app to request connection to a specific network using the
363      * {@link WifiManager} API's.
364      */
365     private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
366     private long mLastSelectedTimeStamp =
367             WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
368 
369     // Store data for network list and deleted ephemeral SSID list.  Used for serializing
370     // parsing data to/from the config store.
371     private final NetworkListSharedStoreData mNetworkListSharedStoreData;
372     private final NetworkListUserStoreData mNetworkListUserStoreData;
373     private final RandomizedMacStoreData mRandomizedMacStoreData;
374 
375     private static class NetworkIdentifier {
376         private WifiSsid mSsid;
377         private byte[] mOui;
NetworkIdentifier(WifiSsid ssid, byte[] oui)378         NetworkIdentifier(WifiSsid ssid, byte[] oui) {
379             mSsid = ssid;
380             mOui = oui;
381         }
382 
383         @Override
hashCode()384         public int hashCode() {
385             return Objects.hash(mSsid, Arrays.hashCode(mOui));
386         }
387 
388         @Override
equals(Object otherObj)389         public boolean equals(Object otherObj) {
390             if (this == otherObj) {
391                 return true;
392             } else if (!(otherObj instanceof NetworkIdentifier)) {
393                 return false;
394             }
395             NetworkIdentifier other = (NetworkIdentifier) otherObj;
396             return Objects.equals(mSsid, other.mSsid) && Arrays.equals(mOui, other.mOui);
397         }
398     }
399     private final Map<NetworkIdentifier, List<DhcpOption>> mCustomDhcpOptions = new HashMap<>();
400 
401     /**
402      * Create new instance of WifiConfigManager.
403      */
WifiConfigManager( Context context, WifiKeyStore wifiKeyStore, WifiConfigStore wifiConfigStore, NetworkListSharedStoreData networkListSharedStoreData, NetworkListUserStoreData networkListUserStoreData, RandomizedMacStoreData randomizedMacStoreData, LruConnectionTracker lruConnectionTracker, WifiInjector wifiInjector)404     WifiConfigManager(
405             Context context,
406             WifiKeyStore wifiKeyStore,
407             WifiConfigStore wifiConfigStore,
408             NetworkListSharedStoreData networkListSharedStoreData,
409             NetworkListUserStoreData networkListUserStoreData,
410             RandomizedMacStoreData randomizedMacStoreData,
411             LruConnectionTracker lruConnectionTracker,
412             WifiInjector wifiInjector) {
413         mContext = context;
414         mWifiInjector = wifiInjector;
415         mClock = wifiInjector.getClock();
416         mUserManager = wifiInjector.getUserManager();
417         mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager();
418         mWifiMetrics = wifiInjector.getWifiMetrics();
419         mWifiBlocklistMonitor = wifiInjector.getWifiBlocklistMonitor();
420         mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
421         mWifiScoreCard = wifiInjector.getWifiScoreCard();
422         mWifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil();
423         mFrameworkFacade = wifiInjector.getFrameworkFacade();
424         mDeviceConfigFacade = wifiInjector.getDeviceConfigFacade();
425         mMacAddressUtil = wifiInjector.getMacAddressUtil();
426         mBuildProperties = wifiInjector.getBuildProperties();
427 
428         mBackupManagerProxy = new BackupManagerProxy();
429         mWifiKeyStore = wifiKeyStore;
430         mWifiConfigStore = wifiConfigStore;
431         mConfiguredNetworks = new ConfigurationMap(mWifiPermissionsUtil);
432         mScanDetailCaches = new HashMap<>(16, 0.75f);
433         mUserTemporarilyDisabledList =
434                 new MissingCounterTimerLockList<>(SCAN_RESULT_MISSING_COUNT_THRESHOLD, mClock);
435         mNonCarrierMergedNetworksStatusTracker = new NonCarrierMergedNetworksStatusTracker(mClock);
436         mRandomizedMacAddressMapping = new HashMap<>();
437         mListeners = new ArraySet<>();
438 
439         // Register store data for network list and deleted ephemeral SSIDs.
440         mNetworkListSharedStoreData = networkListSharedStoreData;
441         mNetworkListUserStoreData = networkListUserStoreData;
442         mRandomizedMacStoreData = randomizedMacStoreData;
443         mWifiConfigStore.registerStoreData(mNetworkListSharedStoreData);
444         mWifiConfigStore.registerStoreData(mNetworkListUserStoreData);
445         mWifiConfigStore.registerStoreData(mRandomizedMacStoreData);
446 
447         mLocalLog = new LocalLog(
448                 context.getSystemService(ActivityManager.class).isLowRamDevice() ? 128 : 256);
449         mLruConnectionTracker = lruConnectionTracker;
450     }
451 
452     /**
453      * Update the cellular data availability of the default data SIM.
454      */
onCellularConnectivityChanged(@ifiDataStall.CellularDataStatusCode int status)455     public void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status) {
456         localLog("onCellularConnectivityChanged:" + status);
457         if (status == WifiDataStall.CELLULAR_DATA_NOT_AVAILABLE) {
458             stopRestrictingAutoJoinToSubscriptionId();
459         }
460     }
461 
462     /**
463      * Determine if the framework should perform non-persistent MAC randomization when connecting
464      * to the SSID or FQDN in the input WifiConfiguration.
465      * @param config
466      * @return
467      */
shouldUseNonPersistentRandomization(WifiConfiguration config)468     public boolean shouldUseNonPersistentRandomization(WifiConfiguration config) {
469         if (!isMacRandomizationSupported()
470                 || config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE) {
471             return false;
472         }
473 
474         // Use non-persistent randomization if it's forced on by dev option
475         if (mFrameworkFacade.getIntegerSetting(mContext,
476                 NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG, 0) == 1) {
477             return true;
478         }
479 
480         // use non-persistent or persistent randomization if configured to do so.
481         if (config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NON_PERSISTENT) {
482             return true;
483         }
484         if (config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
485             return false;
486         }
487 
488         // otherwise the wifi frameworks should decide automatically
489         if (config.getIpConfiguration().getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
490             return false;
491         }
492         if (config.isOpenNetwork() && shouldEnableNonPersistentRandomizationOnOpenNetwork(config)) {
493             return true;
494         }
495         if (config.isPasspoint()) {
496             return isNetworkOptInForNonPersistentRandomization(config.FQDN);
497         } else {
498             return isNetworkOptInForNonPersistentRandomization(config.SSID);
499         }
500     }
501 
shouldEnableNonPersistentRandomizationOnOpenNetwork(WifiConfiguration config)502     private boolean shouldEnableNonPersistentRandomizationOnOpenNetwork(WifiConfiguration config) {
503         if (!mContext.getResources().getBoolean(
504                         R.bool.config_wifiAllowNonPersistentMacRandomizationOnOpenSsids)) {
505             return false;
506         }
507         return config.getNetworkSelectionStatus().hasEverConnected()
508                 && config.getNetworkSelectionStatus().hasNeverDetectedCaptivePortal();
509     }
510 
isNetworkOptInForNonPersistentRandomization(String ssidOrFqdn)511     private boolean isNetworkOptInForNonPersistentRandomization(String ssidOrFqdn) {
512         Set<String> perDeviceSsidBlocklist = new ArraySet<>(mContext.getResources().getStringArray(
513                 R.array.config_wifi_non_persistent_randomization_ssid_blocklist));
514         if (mDeviceConfigFacade.getNonPersistentMacRandomizationSsidBlocklist().contains(ssidOrFqdn)
515                 || perDeviceSsidBlocklist.contains(ssidOrFqdn)) {
516             return false;
517         }
518         Set<String> perDeviceSsidAllowlist = new ArraySet<>(mContext.getResources().getStringArray(
519                 R.array.config_wifi_non_persistent_randomization_ssid_allowlist));
520         return mDeviceConfigFacade.getNonPersistentMacRandomizationSsidAllowlist()
521                 .contains(ssidOrFqdn) || perDeviceSsidAllowlist.contains(ssidOrFqdn);
522     }
523 
524     @VisibleForTesting
getRandomizedMacAddressMappingSize()525     protected int getRandomizedMacAddressMappingSize() {
526         return mRandomizedMacAddressMapping.size();
527     }
528 
529     /**
530      * The persistent randomized MAC address is locally generated for each SSID and does not
531      * change until factory reset of the device. In the initial Q release the per-SSID randomized
532      * MAC is saved on the device, but in an update the storing of randomized MAC is removed.
533      * Instead, the randomized MAC is calculated directly from the SSID and a on device secret.
534      * For backward compatibility, this method first checks the device storage for saved
535      * randomized MAC. If it is not found or the saved MAC is invalid then it will calculate the
536      * randomized MAC directly.
537      *
538      * In the future as devices launched on Q no longer get supported, this method should get
539      * simplified to return the calculated MAC address directly.
540      * @param config the WifiConfiguration to obtain MAC address for.
541      * @return persistent MAC address for this WifiConfiguration
542      */
543     @VisibleForTesting
getPersistentMacAddress(WifiConfiguration config)544     public MacAddress getPersistentMacAddress(WifiConfiguration config) {
545         // mRandomizedMacAddressMapping had been the location to save randomized MAC addresses.
546         String persistentMacString = mRandomizedMacAddressMapping.get(
547                 config.getNetworkKey());
548         // Use the MAC address stored in the storage if it exists and is valid. Otherwise
549         // use the MAC address calculated from a hash function as the persistent MAC.
550         if (persistentMacString != null) {
551             try {
552                 return MacAddress.fromString(persistentMacString);
553             } catch (IllegalArgumentException e) {
554                 Log.e(TAG, "Error creating randomized MAC address from stored value.");
555                 mRandomizedMacAddressMapping.remove(config.getNetworkKey());
556             }
557         }
558         MacAddress result = mMacAddressUtil.calculatePersistentMacForSta(config.getNetworkKey(),
559                 Process.WIFI_UID);
560         if (result == null) {
561             Log.wtf(TAG, "Failed to generate MAC address from KeyStore even after retrying. "
562                     + "Using locally generated MAC address instead.");
563             result = config.getRandomizedMacAddress();
564             if (DEFAULT_MAC_ADDRESS.equals(result)) {
565                 result = MacAddressUtils.createRandomUnicastAddress();
566             }
567         }
568         return result;
569     }
570 
571     /**
572      * Sets the randomized MAC expiration time based on the DHCP lease duration.
573      * This should be called every time DHCP lease information is obtained.
574      */
updateRandomizedMacExpireTime(WifiConfiguration config, long dhcpLeaseSeconds)575     public void updateRandomizedMacExpireTime(WifiConfiguration config, long dhcpLeaseSeconds) {
576         WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId);
577         if (internalConfig == null) {
578             return;
579         }
580         long expireDurationMs = (dhcpLeaseSeconds & 0xffffffffL) * 1000;
581         expireDurationMs = Math.max(NON_PERSISTENT_MAC_REFRESH_MS_MIN, expireDurationMs);
582         expireDurationMs = Math.min(NON_PERSISTENT_MAC_REFRESH_MS_MAX, expireDurationMs);
583         internalConfig.randomizedMacExpirationTimeMs = mClock.getWallClockMillis()
584                 + expireDurationMs;
585     }
586 
setRandomizedMacAddress(WifiConfiguration config, MacAddress mac)587     private void setRandomizedMacAddress(WifiConfiguration config, MacAddress mac) {
588         config.setRandomizedMacAddress(mac);
589         config.randomizedMacLastModifiedTimeMs = mClock.getWallClockMillis();
590     }
591 
592     /**
593      * Obtain the persistent MAC address by first reading from an internal database. If non exists
594      * then calculate the persistent MAC using HMAC-SHA256.
595      * Finally set the randomized MAC of the configuration to the randomized MAC obtained.
596      * @param config the WifiConfiguration to make the update
597      * @return the persistent MacAddress or null if the operation is unsuccessful
598      */
setRandomizedMacToPersistentMac(WifiConfiguration config)599     private MacAddress setRandomizedMacToPersistentMac(WifiConfiguration config) {
600         MacAddress persistentMac = getPersistentMacAddress(config);
601         if (persistentMac == null || persistentMac.equals(config.getRandomizedMacAddress())) {
602             return persistentMac;
603         }
604         WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId);
605         setRandomizedMacAddress(internalConfig, persistentMac);
606         return persistentMac;
607     }
608 
609     /**
610      * This method is called before connecting to a network that has non-persistent randomization
611      * enabled, and will re-randomize the MAC address if needed.
612      * @param config the WifiConfiguration to make the update
613      * @return the updated MacAddress
614      */
updateRandomizedMacIfNeeded(WifiConfiguration config)615     private MacAddress updateRandomizedMacIfNeeded(WifiConfiguration config) {
616         boolean shouldUpdateMac = config.randomizedMacExpirationTimeMs
617                 < mClock.getWallClockMillis() || mClock.getWallClockMillis()
618                 - config.randomizedMacLastModifiedTimeMs >= NON_PERSISTENT_MAC_REFRESH_MS_MAX;
619         if (!shouldUpdateMac) {
620             return config.getRandomizedMacAddress();
621         }
622         WifiConfiguration internalConfig = getInternalConfiguredNetwork(config.networkId);
623         setRandomizedMacAddress(internalConfig, MacAddressUtils.createRandomUnicastAddress());
624         return internalConfig.getRandomizedMacAddress();
625     }
626 
627     /**
628      * Returns the randomized MAC address that should be used for this WifiConfiguration.
629      * This API may return a randomized MAC different from the persistent randomized MAC if
630      * the WifiConfiguration is configured for non-persistent MAC randomization.
631      * @param config
632      * @return MacAddress
633      */
getRandomizedMacAndUpdateIfNeeded(WifiConfiguration config, boolean isForSecondaryDbs)634     public MacAddress getRandomizedMacAndUpdateIfNeeded(WifiConfiguration config,
635             boolean isForSecondaryDbs) {
636         MacAddress mac = shouldUseNonPersistentRandomization(config)
637                 ? updateRandomizedMacIfNeeded(config)
638                 : setRandomizedMacToPersistentMac(config);
639         // If this is the secondary STA for multi internet for DBS AP, use a different MAC than the
640         // persistent mac randomization, as the primary and secondary STAs could connect to the
641         // same SSID.
642         if (isForSecondaryDbs) {
643             mac = MacAddressUtil.nextMacAddress(mac);
644         }
645         return mac;
646     }
647 
648     /**
649      * Enable/disable verbose logging in WifiConfigManager & its helper classes.
650      */
enableVerboseLogging(boolean verbose)651     public void enableVerboseLogging(boolean verbose) {
652         mVerboseLoggingEnabled = verbose;
653         mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
654         mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
655         mWifiBlocklistMonitor.enableVerboseLogging(mVerboseLoggingEnabled);
656     }
657 
658     /**
659      * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
660      * is needed when the network configurations are being requested via the public WifiManager
661      * API's.
662      * This currently masks the following elements: psk, wepKeys & enterprise config password.
663      */
maskPasswordsInWifiConfiguration(WifiConfiguration configuration)664     private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
665         if (!TextUtils.isEmpty(configuration.preSharedKey)) {
666             configuration.preSharedKey = PASSWORD_MASK;
667         }
668         if (configuration.wepKeys != null) {
669             for (int i = 0; i < configuration.wepKeys.length; i++) {
670                 if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
671                     configuration.wepKeys[i] = PASSWORD_MASK;
672                 }
673             }
674         }
675         if (configuration.enterpriseConfig != null && !TextUtils.isEmpty(
676                 configuration.enterpriseConfig.getPassword())) {
677             configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
678         }
679     }
680 
681     /**
682      * Helper method to mask randomized MAC address from the provided WifiConfiguration Object.
683      * This is needed when the network configurations are being requested via the public
684      * WifiManager API's. This method puts "02:00:00:00:00:00" as the MAC address.
685      * @param configuration WifiConfiguration to hide the MAC address
686      */
maskRandomizedMacAddressInWifiConfiguration(WifiConfiguration configuration)687     private void maskRandomizedMacAddressInWifiConfiguration(WifiConfiguration configuration) {
688         setRandomizedMacAddress(configuration, DEFAULT_MAC_ADDRESS);
689     }
690 
691     /**
692      * Helper method to create a copy of the provided internal WifiConfiguration object to be
693      * passed to external modules.
694      *
695      * @param configuration provided WifiConfiguration object.
696      * @param maskPasswords Mask passwords or not.
697      * @param targetUid Target UID for MAC address reading: -1 = mask all, 0 = mask none, >0 =
698      *                  mask all but the targetUid (carrier app).
699      * @return Copy of the WifiConfiguration object, or a default WifiConfiguration if the input
700      *         is null.
701      */
createExternalWifiConfiguration( @onNull WifiConfiguration configuration, boolean maskPasswords, int targetUid)702     private @NonNull WifiConfiguration createExternalWifiConfiguration(
703             @NonNull WifiConfiguration configuration, boolean maskPasswords, int targetUid) {
704         if (configuration == null) {
705             Log.wtf(TAG, "Unexpected null configuration in createExternalWifiConfiguration");
706             return new WifiConfiguration();
707         }
708         WifiConfiguration network = new WifiConfiguration(configuration);
709         if (maskPasswords) {
710             maskPasswordsInWifiConfiguration(network);
711         }
712         if (targetUid != Process.WIFI_UID && targetUid != Process.SYSTEM_UID
713                 && targetUid != configuration.creatorUid) {
714             maskRandomizedMacAddressInWifiConfiguration(network);
715         }
716         if (!isMacRandomizationSupported()) {
717             network.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
718         }
719         return network;
720     }
721 
722     /**
723      * Returns whether MAC randomization is supported on this device.
724      * @return
725      */
isMacRandomizationSupported()726     private boolean isMacRandomizationSupported() {
727         return mContext.getResources().getBoolean(
728                 R.bool.config_wifi_connected_mac_randomization_supported);
729     }
730 
731     /**
732      * Fetch the list of currently configured networks maintained in WifiConfigManager.
733      *
734      * This retrieves a copy of the internal configurations maintained by WifiConfigManager and
735      * should be used for any public interfaces.
736      *
737      * @param savedOnly     Retrieve only saved networks.
738      * @param maskPasswords Mask passwords or not.
739      * @param targetUid Target UID for MAC address reading: -1 (Invalid UID) = mask all,
740      *                  WIFI||SYSTEM = mask none, <other> = mask all but the targetUid (carrier
741      *                  app).
742      * @return List of WifiConfiguration objects representing the networks.
743      */
getConfiguredNetworks( boolean savedOnly, boolean maskPasswords, int targetUid)744     private List<WifiConfiguration> getConfiguredNetworks(
745             boolean savedOnly, boolean maskPasswords, int targetUid) {
746         List<WifiConfiguration> networks = new ArrayList<>();
747         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
748             if (savedOnly && (config.ephemeral || config.isPasspoint())) {
749                 continue;
750             }
751             networks.add(createExternalWifiConfiguration(config, maskPasswords, targetUid));
752         }
753         return networks;
754     }
755 
756     /**
757      * Retrieves the list of all configured networks with passwords masked.
758      *
759      * @return List of WifiConfiguration objects representing the networks.
760      */
getConfiguredNetworks()761     public List<WifiConfiguration> getConfiguredNetworks() {
762         return getConfiguredNetworks(false, true, Process.WIFI_UID);
763     }
764 
765     /**
766      * Retrieves the list of all configured networks with the passwords in plaintext.
767      *
768      * WARNING: Don't use this to pass network configurations to external apps. Should only be
769      * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
770      * TODO: Need to understand the current use case of this API.
771      *
772      * @return List of WifiConfiguration objects representing the networks.
773      */
getConfiguredNetworksWithPasswords()774     public List<WifiConfiguration> getConfiguredNetworksWithPasswords() {
775         return getConfiguredNetworks(false, false, Process.WIFI_UID);
776     }
777 
778     /**
779      * Retrieves the list of all configured networks with the passwords masked.
780      *
781      * @return List of WifiConfiguration objects representing the networks.
782      */
getSavedNetworks(int targetUid)783     public List<WifiConfiguration> getSavedNetworks(int targetUid) {
784         return getConfiguredNetworks(true, true, targetUid);
785     }
786 
787     /**
788      * Retrieves the configured network corresponding to the provided networkId with password
789      * masked.
790      *
791      * @param networkId networkId of the requested network.
792      * @return WifiConfiguration object if found, null otherwise.
793      */
getConfiguredNetwork(int networkId)794     public @Nullable WifiConfiguration getConfiguredNetwork(int networkId) {
795         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
796         if (config == null) {
797             return null;
798         }
799         // Create a new configuration object with the passwords masked to send out to the external
800         // world.
801         return createExternalWifiConfiguration(config, true, Process.WIFI_UID);
802     }
803 
804     /**
805      * Retrieves the configured network corresponding to the provided config key with password
806      * masked.
807      *
808      * @param configKey configKey of the requested network.
809      * @return WifiConfiguration object if found, null otherwise.
810      */
getConfiguredNetwork(String configKey)811     public @Nullable WifiConfiguration getConfiguredNetwork(String configKey) {
812         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
813         if (config == null) {
814             return null;
815         }
816         // Create a new configuration object with the passwords masked to send out to the external
817         // world.
818         return createExternalWifiConfiguration(config, true, Process.WIFI_UID);
819     }
820 
821     /**
822      * Retrieves the configured network corresponding to the provided networkId with password
823      * in plaintext.
824      *
825      * WARNING: Don't use this to pass network configurations to external apps. Should only be
826      * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
827      *
828      * @param networkId networkId of the requested network.
829      * @return WifiConfiguration object if found, null otherwise.
830      */
getConfiguredNetworkWithPassword(int networkId)831     public @Nullable WifiConfiguration getConfiguredNetworkWithPassword(int networkId) {
832         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
833         if (config == null) {
834             return null;
835         }
836         // Create a new configuration object without the passwords masked to send out to the
837         // external world.
838         return createExternalWifiConfiguration(config, false, Process.WIFI_UID);
839     }
840 
841     /**
842      * Retrieves the configured network corresponding to the provided networkId
843      * without any masking.
844      *
845      * WARNING: Don't use this to pass network configurations except in the wifi stack, when
846      * there is a need for passwords and randomized MAC address.
847      *
848      * @param networkId networkId of the requested network.
849      * @return Copy of WifiConfiguration object if found, null otherwise.
850      */
getConfiguredNetworkWithoutMasking(int networkId)851     public @Nullable WifiConfiguration getConfiguredNetworkWithoutMasking(int networkId) {
852         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
853         if (config == null) {
854             return null;
855         }
856         return new WifiConfiguration(config);
857     }
858 
859     /**
860      * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
861      * the networks in our database.
862      */
getInternalConfiguredNetworks()863     private Collection<WifiConfiguration> getInternalConfiguredNetworks() {
864         return mConfiguredNetworks.valuesForCurrentUser();
865     }
866 
getInternalConfiguredNetworkByUpgradableType( @onNull WifiConfiguration config)867     private @Nullable WifiConfiguration getInternalConfiguredNetworkByUpgradableType(
868             @NonNull WifiConfiguration config) {
869         WifiConfiguration internalConfig = null;
870         int securityType = config.getDefaultSecurityParams().getSecurityType();
871         WifiConfiguration possibleExistingConfig = new WifiConfiguration(config);
872         switch (securityType) {
873             case WifiConfiguration.SECURITY_TYPE_PSK:
874                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
875                 break;
876             case WifiConfiguration.SECURITY_TYPE_SAE:
877                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
878                 break;
879             case WifiConfiguration.SECURITY_TYPE_EAP:
880                 possibleExistingConfig.setSecurityParams(
881                         WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
882                 break;
883             case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
884                 possibleExistingConfig.setSecurityParams(
885                         WifiConfiguration.SECURITY_TYPE_EAP);
886                 break;
887             case WifiConfiguration.SECURITY_TYPE_OPEN:
888                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
889                 break;
890             case WifiConfiguration.SECURITY_TYPE_OWE:
891                 possibleExistingConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
892                 break;
893             default:
894                 return null;
895         }
896         internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(
897                 possibleExistingConfig.getProfileKey());
898         return internalConfig;
899     }
900 
901     /**
902      * Helper method to retrieve the internal WifiConfiguration object corresponding to the
903      * provided configuration in our database.
904      * This first attempts to find the network using the provided network ID in configuration,
905      * else it attempts to find a matching configuration using the configKey.
906      */
getInternalConfiguredNetwork( @onNull WifiConfiguration config)907     private @Nullable WifiConfiguration getInternalConfiguredNetwork(
908             @NonNull WifiConfiguration config) {
909         WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
910         if (internalConfig != null) {
911             return internalConfig;
912         }
913         internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(
914                 config.getProfileKey());
915         if (internalConfig != null) {
916             return internalConfig;
917         }
918         internalConfig = getInternalConfiguredNetworkByUpgradableType(config);
919         if (internalConfig == null) {
920             Log.e(TAG, "Cannot find network with networkId " + config.networkId
921                     + " or configKey " + config.getProfileKey()
922                     + " or upgradable security type check");
923         }
924         return internalConfig;
925     }
926 
927     /**
928      * Helper method to retrieve the internal WifiConfiguration object corresponding to the
929      * provided network ID in our database.
930      */
getInternalConfiguredNetwork(int networkId)931     private @Nullable WifiConfiguration getInternalConfiguredNetwork(int networkId) {
932         if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
933             return null;
934         }
935         WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId);
936         if (internalConfig == null) {
937             Log.e(TAG, "Cannot find network with networkId " + networkId);
938         }
939         return internalConfig;
940     }
941 
942     /**
943      * Helper method to retrieve the internal WifiConfiguration object corresponding to the
944      * provided configKey in our database.
945      */
getInternalConfiguredNetwork(String configKey)946     private @Nullable WifiConfiguration getInternalConfiguredNetwork(String configKey) {
947         WifiConfiguration internalConfig =
948                 mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey);
949         if (internalConfig == null) {
950             Log.e(TAG, "Cannot find network with configKey " + configKey);
951         }
952         return internalConfig;
953     }
954 
955     /**
956      * Method to send out the configured networks change broadcast when network configurations
957      * changed.
958      *
959      * In Android R we stopped sending out WifiConfiguration due to user privacy concerns.
960      * Thus, no matter how many networks changed,
961      * {@link WifiManager#EXTRA_MULTIPLE_NETWORKS_CHANGED} is always set to true, and
962      * {@link WifiManager#EXTRA_WIFI_CONFIGURATION} is always null.
963      *
964      * @param reason  The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
965      *                WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
966      * @param config The related to the change. This is only sent out for system users, and could
967      *               be null if multiple WifiConfigurations are affected by the change.
968      */
sendConfiguredNetworkChangedBroadcast(int reason, @Nullable WifiConfiguration config)969     private void sendConfiguredNetworkChangedBroadcast(int reason,
970             @Nullable WifiConfiguration config) {
971         Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
972         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
973         intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
974         intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
975         mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.ACCESS_WIFI_STATE);
976 
977         // Send another broadcast including the WifiConfiguration to System only
978         Intent intentForSystem = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
979         intentForSystem.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
980         intentForSystem.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, config == null);
981         intentForSystem.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
982         intentForSystem.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION,
983                 config == null ? null : createExternalWifiConfiguration(config, true, -1));
984         mContext.sendBroadcastAsUser(intentForSystem, UserHandle.SYSTEM,
985                 Manifest.permission.NETWORK_STACK);
986     }
987 
988     /**
989      * Checks if |uid| has permission to modify the provided configuration.
990      *
991      * @param config         WifiConfiguration object corresponding to the network to be modified.
992      * @param uid            UID of the app requesting the modification.
993      * @param packageName    Package name of the app requesting the modification.
994      */
canModifyNetwork(WifiConfiguration config, int uid, @Nullable String packageName)995     private boolean canModifyNetwork(WifiConfiguration config, int uid,
996             @Nullable String packageName) {
997         // Passpoint configurations are generated and managed by PasspointManager. They can be
998         // added by either PasspointNetworkNominator (for auto connection) or Settings app
999         // (for manual connection), and need to be removed once the connection is completed.
1000         // Since it is "owned" by us, so always allow us to modify them.
1001         if (config.isPasspoint() && uid == Process.WIFI_UID) {
1002             return true;
1003         }
1004 
1005         // EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
1006         // by authenticator back to the WifiConfiguration object.
1007         // Since it is "owned" by us, so always allow us to modify them.
1008         if (config.enterpriseConfig != null
1009                 && uid == Process.WIFI_UID
1010                 && config.enterpriseConfig.isAuthenticationSimBased()) {
1011             return true;
1012         }
1013 
1014         // TODO: ideally package should not be null here (and hence we wouldn't need the
1015         // isDeviceOwner(uid) method), but it would require changing  many methods to pass the
1016         // package name around (for example, all methods called by
1017         // WifiServiceImpl.triggerConnectAndReturnStatus(netId, callingUid)
1018         final boolean isOrganizationOwnedDeviceAdmin =
1019                 mWifiPermissionsUtil.isOrganizationOwnedDeviceAdmin(uid, packageName);
1020 
1021         // If |uid| corresponds to the device owner or the profile owner of an organization owned
1022         // device, allow all modifications.
1023         if (isOrganizationOwnedDeviceAdmin) {
1024             return true;
1025         }
1026 
1027         final boolean isCreator = (config.creatorUid == uid);
1028 
1029         // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner
1030         // or a Profile Owner of an organization owned device.
1031         final boolean isConfigEligibleForLockdown =
1032                 mWifiPermissionsUtil.isOrganizationOwnedDeviceAdmin(config.creatorUid,
1033                         config.creatorName);
1034         if (!isConfigEligibleForLockdown) {
1035             // App that created the network or settings app (i.e user) has permission to
1036             // modify the network.
1037             return isCreator
1038                     || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1039                     || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)
1040                     || mWifiPermissionsUtil.checkConfigOverridePermission(uid);
1041         }
1042 
1043         final ContentResolver resolver = mContext.getContentResolver();
1044         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
1045                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
1046         return !isLockdownFeatureEnabled
1047                 // If not locked down, settings app (i.e user) has permission to modify the network.
1048                 && (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1049                 || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)
1050                 || mWifiPermissionsUtil.checkConfigOverridePermission(uid));
1051     }
1052 
mergeSecurityParamsListWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)1053     private void mergeSecurityParamsListWithInternalWifiConfiguration(
1054             WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
1055         // If not set, just copy over all list.
1056         if (internalConfig.getSecurityParamsList().isEmpty()) {
1057             internalConfig.setSecurityParams(externalConfig.getSecurityParamsList());
1058             return;
1059         }
1060 
1061         WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary(externalConfig);
1062 
1063         // An external caller is only allowed to set one type manually.
1064         // As a result, only default type matters.
1065         // There might be 3 cases:
1066         // 1. Existing config with new upgradable type config,
1067         //    ex. PSK/SAE config with SAE config.
1068         // 2. Existing configuration with downgradable type config,
1069         //    ex. SAE config with PSK config.
1070         // 3. The new type is not a compatible type of existing config.
1071         //    ex. Open config with PSK config.
1072         //    This might happen when updating a config via network ID directly.
1073         int oldType = internalConfig.getDefaultSecurityParams().getSecurityType();
1074         int newType = externalConfig.getDefaultSecurityParams().getSecurityType();
1075         if (oldType != newType) {
1076             if (internalConfig.isSecurityType(newType)) {
1077                 internalConfig.setSecurityParamsIsAddedByAutoUpgrade(newType,
1078                         externalConfig.getDefaultSecurityParams().isAddedByAutoUpgrade());
1079             } else if (externalConfig.isSecurityType(oldType)) {
1080                 internalConfig.setSecurityParams(newType);
1081                 internalConfig.addSecurityParams(oldType);
1082             } else {
1083                 internalConfig.setSecurityParams(externalConfig.getSecurityParamsList());
1084             }
1085         }
1086     }
1087 
mergeDppSecurityParamsWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)1088     private void mergeDppSecurityParamsWithInternalWifiConfiguration(
1089             WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
1090         // Do not update for non-DPP network
1091         if (!externalConfig.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP)) {
1092             return;
1093         }
1094 
1095         if (externalConfig.getDppConnector().length != 0
1096                 && externalConfig.getDppCSignKey().length != 0
1097                 && externalConfig.getDppNetAccessKey().length != 0) {
1098             internalConfig.setDppConnectionKeys(externalConfig.getDppConnector(),
1099                     externalConfig.getDppCSignKey(), externalConfig.getDppNetAccessKey());
1100         }
1101 
1102         if (externalConfig.getDppPrivateEcKey().length != 0) {
1103             internalConfig.setDppConfigurator(externalConfig.getDppPrivateEcKey());
1104         }
1105     }
1106 
1107     /**
1108      * Copy over public elements from an external WifiConfiguration object to the internal
1109      * configuration object if element has been set in the provided external WifiConfiguration.
1110      * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
1111      * for every update.
1112      *
1113      * This method updates all elements that are common to both network addition & update.
1114      * The following fields of {@link WifiConfiguration} are not copied from external configs:
1115      *  > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
1116      *  > status - The status needs to be explicitly updated using
1117      *             {@link WifiManager#enableNetwork(int, boolean)} or
1118      *             {@link WifiManager#disableNetwork(int)}.
1119      *
1120      * @param internalConfig WifiConfiguration object in our internal map.
1121      * @param externalConfig WifiConfiguration object provided from the external API.
1122      */
mergeWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)1123     private void mergeWithInternalWifiConfiguration(
1124             WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
1125         if (externalConfig.SSID != null) {
1126             // Translate the SSID in case it is in hexadecimal for a translatable charset.
1127             if (externalConfig.SSID.length() > 0 && externalConfig.SSID.charAt(0) != '\"') {
1128                 internalConfig.SSID = mWifiInjector.getSsidTranslator().getTranslatedSsid(
1129                         WifiSsid.fromString(externalConfig.SSID)).toString();
1130             } else {
1131                 internalConfig.SSID = externalConfig.SSID;
1132             }
1133         }
1134         internalConfig.BSSID = externalConfig.BSSID == null ? null
1135                 : externalConfig.BSSID.toLowerCase();
1136         internalConfig.hiddenSSID = externalConfig.hiddenSSID;
1137 
1138         if (externalConfig.preSharedKey != null
1139                 && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
1140             internalConfig.preSharedKey = externalConfig.preSharedKey;
1141         }
1142         // Modify only wep keys are present in the provided configuration. This is a little tricky
1143         // because there is no easy way to tell if the app is actually trying to null out the
1144         // existing keys or not.
1145         if (externalConfig.wepKeys != null) {
1146             boolean hasWepKey = false;
1147             for (int i = 0; i < internalConfig.wepKeys.length; i++) {
1148                 if (externalConfig.wepKeys[i] != null
1149                         && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
1150                     internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
1151                     hasWepKey = true;
1152                 }
1153             }
1154             if (hasWepKey) {
1155                 internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
1156             }
1157         }
1158         if (externalConfig.FQDN != null) {
1159             internalConfig.FQDN = externalConfig.FQDN;
1160         }
1161         if (externalConfig.providerFriendlyName != null) {
1162             internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
1163         }
1164         if (externalConfig.roamingConsortiumIds != null) {
1165             internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone();
1166         }
1167 
1168         mergeSecurityParamsListWithInternalWifiConfiguration(internalConfig, externalConfig);
1169         mergeDppSecurityParamsWithInternalWifiConfiguration(internalConfig, externalConfig);
1170 
1171         // Copy over the |IpConfiguration| parameters if set.
1172         if (externalConfig.getIpConfiguration() != null) {
1173             IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
1174             if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
1175                 internalConfig.setIpAssignment(ipAssignment);
1176                 if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
1177                     internalConfig.setStaticIpConfiguration(
1178                             new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
1179                 }
1180             }
1181             IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
1182             if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
1183                 internalConfig.setProxySettings(proxySettings);
1184                 if (proxySettings == IpConfiguration.ProxySettings.PAC
1185                         || proxySettings == IpConfiguration.ProxySettings.STATIC) {
1186                     internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
1187                 }
1188             }
1189         }
1190 
1191         internalConfig.allowAutojoin = externalConfig.allowAutojoin;
1192         // Copy over the |WifiEnterpriseConfig| parameters if set.
1193         if (externalConfig.enterpriseConfig != null) {
1194             internalConfig.enterpriseConfig.copyFromExternal(
1195                     externalConfig.enterpriseConfig, PASSWORD_MASK);
1196         }
1197 
1198         // Copy over any metered information.
1199         internalConfig.meteredHint = externalConfig.meteredHint;
1200         internalConfig.meteredOverride = externalConfig.meteredOverride;
1201 
1202         internalConfig.trusted = externalConfig.trusted;
1203         internalConfig.oemPaid = externalConfig.oemPaid;
1204         internalConfig.oemPrivate = externalConfig.oemPrivate;
1205         internalConfig.carrierMerged = externalConfig.carrierMerged;
1206         internalConfig.restricted = externalConfig.restricted;
1207 
1208         // Copy over macRandomizationSetting
1209         internalConfig.macRandomizationSetting = externalConfig.macRandomizationSetting;
1210         internalConfig.carrierId = externalConfig.carrierId;
1211         internalConfig.isHomeProviderNetwork = externalConfig.isHomeProviderNetwork;
1212         internalConfig.subscriptionId = externalConfig.subscriptionId;
1213         internalConfig.setSubscriptionGroup(externalConfig.getSubscriptionGroup());
1214         internalConfig.getNetworkSelectionStatus()
1215                 .setConnectChoice(externalConfig.getNetworkSelectionStatus().getConnectChoice());
1216         internalConfig.getNetworkSelectionStatus().setConnectChoiceRssi(
1217                 externalConfig.getNetworkSelectionStatus().getConnectChoiceRssi());
1218         internalConfig.setBssidAllowlist(externalConfig.getBssidAllowlistInternal());
1219         internalConfig.setRepeaterEnabled(externalConfig.isRepeaterEnabled());
1220     }
1221 
1222     /**
1223      * Set all the exposed defaults in the newly created WifiConfiguration object.
1224      * These fields have a default value advertised in our public documentation. The only exception
1225      * is the hidden |IpConfiguration| parameters, these have a default value even though they're
1226      * hidden.
1227      *
1228      * @param configuration provided WifiConfiguration object.
1229      */
setDefaultsInWifiConfiguration(WifiConfiguration configuration)1230     private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
1231         configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
1232         configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
1233 
1234         configuration.status = WifiConfiguration.Status.DISABLED;
1235         configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
1236                 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1237         configuration.getNetworkSelectionStatus().setNetworkSelectionDisableReason(
1238                 NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
1239     }
1240 
1241     /**
1242      * Create a new internal WifiConfiguration object by copying over parameters from the provided
1243      * external configuration and set defaults for the appropriate parameters.
1244      *
1245      * @param externalConfig WifiConfiguration object provided from the external API.
1246      * @return New WifiConfiguration object with parameters merged from the provided external
1247      * configuration.
1248      */
createNewInternalWifiConfigurationFromExternal( WifiConfiguration externalConfig, int uid, @Nullable String packageName)1249     private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
1250             WifiConfiguration externalConfig, int uid, @Nullable String packageName) {
1251         WifiConfiguration newInternalConfig = new WifiConfiguration();
1252 
1253         // First allocate a new network ID for the configuration.
1254         newInternalConfig.networkId = mNextNetworkId++;
1255 
1256         // First set defaults in the new configuration created.
1257         setDefaultsInWifiConfiguration(newInternalConfig);
1258 
1259         // Convert legacy fields to new security params
1260         externalConfig.convertLegacyFieldsToSecurityParamsIfNeeded();
1261 
1262         // Copy over all the public elements from the provided configuration.
1263         mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
1264 
1265         // Copy over the hidden configuration parameters. These are the only parameters used by
1266         // system apps to indicate some property about the network being added.
1267         // These are only copied over for network additions and ignored for network updates.
1268         newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
1269         newInternalConfig.ephemeral = externalConfig.ephemeral;
1270         newInternalConfig.osu = externalConfig.osu;
1271         newInternalConfig.fromWifiNetworkSuggestion = externalConfig.fromWifiNetworkSuggestion;
1272         newInternalConfig.fromWifiNetworkSpecifier = externalConfig.fromWifiNetworkSpecifier;
1273         newInternalConfig.useExternalScores = externalConfig.useExternalScores;
1274         newInternalConfig.shared = externalConfig.shared;
1275         newInternalConfig.updateIdentifier = externalConfig.updateIdentifier;
1276         newInternalConfig.setPasspointUniqueId(externalConfig.getPasspointUniqueId());
1277 
1278         // Add debug information for network addition.
1279         newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
1280         newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
1281                 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid);
1282         newInternalConfig.lastUpdated = mClock.getWallClockMillis();
1283         newInternalConfig.numRebootsSinceLastUse = 0;
1284         initRandomizedMacForInternalConfig(newInternalConfig);
1285         return newInternalConfig;
1286     }
1287 
1288     /**
1289      * Create a new internal WifiConfiguration object by copying over parameters from the provided
1290      * external configuration to a copy of the existing internal WifiConfiguration object.
1291      *
1292      * @param internalConfig WifiConfiguration object in our internal map.
1293      * @param externalConfig WifiConfiguration object provided from the external API.
1294      * @param overrideCreator when this set to true, will overrider the creator to the current
1295      *                        modifier.
1296      * @return Copy of existing WifiConfiguration object with parameters merged from the provided
1297      * configuration.
1298      */
updateExistingInternalWifiConfigurationFromExternal( @onNull WifiConfiguration internalConfig, @NonNull WifiConfiguration externalConfig, int uid, @Nullable String packageName, boolean overrideCreator)1299     private @NonNull WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
1300             @NonNull WifiConfiguration internalConfig, @NonNull WifiConfiguration externalConfig,
1301             int uid, @Nullable String packageName, boolean overrideCreator) {
1302         WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
1303 
1304         // Copy over all the public elements from the provided configuration.
1305         mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
1306 
1307         // Add debug information for network update.
1308         newInternalConfig.lastUpdateUid = uid;
1309         newInternalConfig.lastUpdateName =
1310                 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid);
1311         newInternalConfig.lastUpdated = mClock.getWallClockMillis();
1312         newInternalConfig.numRebootsSinceLastUse = 0;
1313         if (overrideCreator) {
1314             newInternalConfig.creatorName = newInternalConfig.lastUpdateName;
1315             newInternalConfig.creatorUid = uid;
1316         }
1317         return newInternalConfig;
1318     }
1319 
logUserActionEvents(WifiConfiguration before, WifiConfiguration after)1320     private void logUserActionEvents(WifiConfiguration before, WifiConfiguration after) {
1321         // Logs changes in meteredOverride.
1322         if (before.meteredOverride != after.meteredOverride) {
1323             mWifiMetrics.logUserActionEvent(
1324                     WifiMetrics.convertMeteredOverrideEnumToUserActionEventType(
1325                             after.meteredOverride),
1326                     after.networkId);
1327         }
1328 
1329         // Logs changes in macRandomizationSetting.
1330         if (before.macRandomizationSetting != after.macRandomizationSetting) {
1331             mWifiMetrics.logUserActionEvent(
1332                     after.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE
1333                             ? UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF
1334                             : UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_ON,
1335                     after.networkId);
1336         }
1337     }
1338 
1339     /**
1340      * Add a network or update a network configuration to our database.
1341      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1342      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1343      *
1344      * @param config provided WifiConfiguration object.
1345      * @param uid UID of the app requesting the network addition/modification.
1346      * @param packageName Package name of the app requesting the network addition/modification.
1347      * @param overrideCreator when this set to true, will overrider the creator to the current
1348      *                        modifier.
1349      * @return NetworkUpdateResult object representing status of the update.
1350      *         WifiConfiguration object representing the existing configuration matching
1351      *         the new config, or null if none matches.
1352      */
addOrUpdateNetworkInternal( @onNull WifiConfiguration config, int uid, @Nullable String packageName, boolean overrideCreator)1353     private @NonNull Pair<NetworkUpdateResult, WifiConfiguration> addOrUpdateNetworkInternal(
1354             @NonNull WifiConfiguration config, int uid, @Nullable String packageName,
1355             boolean overrideCreator) {
1356         if (mVerboseLoggingEnabled) {
1357             Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
1358         }
1359         WifiConfiguration newInternalConfig = null;
1360 
1361         long supportedFeatures = mWifiInjector.getActiveModeWarden()
1362                 .getPrimaryClientModeManager().getSupportedFeatures();
1363 
1364         // First check if we already have a network with the provided network id or configKey.
1365         WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
1366         // No existing network found. So, potentially a network add.
1367         if (existingInternalConfig == null) {
1368             if (!WifiConfigurationUtil.validate(config, supportedFeatures,
1369                     WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
1370                 Log.e(TAG, "Cannot add network with invalid config");
1371                 return new Pair<>(
1372                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID,
1373                                 STATUS_INVALID_CONFIGURATION),
1374                         existingInternalConfig);
1375             }
1376             newInternalConfig =
1377                     createNewInternalWifiConfigurationFromExternal(config, uid, packageName);
1378             // Since the original config provided may have had an empty
1379             // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
1380             // network with the the same configkey.
1381             existingInternalConfig =
1382                     getInternalConfiguredNetwork(newInternalConfig.getProfileKey());
1383         }
1384         // Existing network found. So, a network update.
1385         if (existingInternalConfig != null) {
1386             if (!WifiConfigurationUtil.validate(
1387                     config, supportedFeatures, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
1388                 Log.e(TAG, "Cannot update network with invalid config");
1389                 return new Pair<>(
1390                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID,
1391                                 STATUS_INVALID_CONFIGURATION),
1392                         existingInternalConfig);
1393             }
1394             // Check for the app's permission before we let it update this network.
1395             if (!canModifyNetwork(existingInternalConfig, uid, packageName)) {
1396                 Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1397                         + config.getProfileKey());
1398                 return new Pair<>(
1399                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID,
1400                                 STATUS_NO_PERMISSION_MODIFY_CONFIG),
1401                         existingInternalConfig);
1402             }
1403             if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1404                     && !config.isPasspoint()) {
1405                 logUserActionEvents(existingInternalConfig, config);
1406             }
1407             newInternalConfig =
1408                     updateExistingInternalWifiConfigurationFromExternal(
1409                             existingInternalConfig, config, uid, packageName, overrideCreator);
1410         }
1411 
1412         if (!WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary(newInternalConfig)) {
1413             return new Pair<>(
1414                     new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1415                     existingInternalConfig);
1416         }
1417 
1418         // Only add networks with proxy settings if the user has permission to
1419         if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
1420                 && !canModifyProxySettings(uid, packageName)) {
1421             Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
1422                     + config.getProfileKey() + ". Must have NETWORK_SETTINGS,"
1423                     + " or be device or profile owner.");
1424             return new Pair<>(
1425                     new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1426                     existingInternalConfig);
1427         }
1428 
1429         // Only allow changes in Repeater Enabled flag if the user has permission to
1430         if (WifiConfigurationUtil.hasRepeaterEnabledChanged(
1431                 existingInternalConfig, newInternalConfig)
1432                 && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1433                 && !mWifiPermissionsUtil.checkConfigOverridePermission(uid)) {
1434             Log.e(TAG, "UID " + uid
1435                     + " does not have permission to modify Repeater Enabled Settings "
1436                     + " , or add a network with Repeater Enabled set to true "
1437                     + config.getProfileKey() + ". Must have NETWORK_SETTINGS.");
1438             return new Pair<>(
1439                     new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1440                     existingInternalConfig);
1441         }
1442 
1443         if (WifiConfigurationUtil.hasMacRandomizationSettingsChanged(existingInternalConfig,
1444                 newInternalConfig) && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
1445                 && !mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)
1446                 && !mWifiPermissionsUtil.checkConfigOverridePermission(uid)
1447                 && !(newInternalConfig.isPasspoint() && uid == newInternalConfig.creatorUid)
1448                 && !config.fromWifiNetworkSuggestion
1449                 && !mWifiPermissionsUtil.isDeviceInDemoMode(mContext)
1450                 && !(mWifiPermissionsUtil.isAdmin(uid, packageName)
1451                 && uid == newInternalConfig.creatorUid)) {
1452             Log.e(TAG, "UID " + uid + " does not have permission to modify MAC randomization "
1453                     + "Settings " + config.getProfileKey() + ". Must have "
1454                     + "NETWORK_SETTINGS or NETWORK_SETUP_WIZARD or be in Demo Mode "
1455                     + "or be the creator adding or updating a passpoint network "
1456                     + "or be an admin updating their own network.");
1457             return new Pair<>(
1458                     new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1459                     existingInternalConfig);
1460         }
1461 
1462         if (config.isEnterprise()
1463                 && config.enterpriseConfig.isEapMethodServerCertUsed()
1464                 && !config.enterpriseConfig.isMandatoryParameterSetForServerCertValidation()
1465                 && !config.enterpriseConfig.isTrustOnFirstUseEnabled()) {
1466             boolean isSettingsOrSuw = mContext.checkPermission(Manifest.permission.NETWORK_SETTINGS,
1467                     -1 /* pid */, uid) == PERMISSION_GRANTED
1468                     || mContext.checkPermission(Manifest.permission.NETWORK_SETUP_WIZARD,
1469                     -1 /* pid */, uid) == PERMISSION_GRANTED;
1470             if (!(mWifiInjector.getWifiGlobals().isInsecureEnterpriseConfigurationAllowed()
1471                     && isSettingsOrSuw)) {
1472                 Log.e(TAG, "Enterprise network configuration is missing either a Root CA "
1473                         + "or a domain name");
1474                 return new Pair<>(
1475                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID,
1476                                 STATUS_INVALID_CONFIGURATION_ENTERPRISE),
1477                         existingInternalConfig);
1478             }
1479             Log.w(TAG, "Insecure Enterprise network " + config.SSID
1480                     + " configured by Settings/SUW");
1481 
1482             // Implicit user approval, when creating an insecure connection which is allowed
1483             // in the configuration of the device
1484             newInternalConfig.enterpriseConfig.setUserApproveNoCaCert(true);
1485         }
1486 
1487         // Update the keys for saved enterprise networks. For Passpoint, the certificates
1488         // and keys are installed at the time the provider is installed. For suggestion enterprise
1489         // network the certificates and keys are installed at the time the suggestion is added
1490         if (!config.isPasspoint() && !config.fromWifiNetworkSuggestion && config.isEnterprise()) {
1491             if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
1492                 return new Pair<>(
1493                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1494                         existingInternalConfig);
1495             }
1496         }
1497 
1498         // Validate an Enterprise network with Trust On First Use.
1499         if (config.isEnterprise() && config.enterpriseConfig.isTrustOnFirstUseEnabled()) {
1500             if ((supportedFeatures & WIFI_FEATURE_TRUST_ON_FIRST_USE) == 0) {
1501                 Log.e(TAG, "Trust On First Use could not be set "
1502                         + "when Trust On First Use is not supported.");
1503                 return new Pair<>(
1504                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1505                         existingInternalConfig);
1506             }
1507             if (!config.enterpriseConfig.isEapMethodServerCertUsed()) {
1508                 Log.e(TAG, "Trust On First Use could not be set "
1509                         + "when the server certificate is not used.");
1510                 return new Pair<>(
1511                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1512                         existingInternalConfig);
1513             } else if (config.enterpriseConfig.hasCaCertificate()) {
1514                 Log.e(TAG, "Trust On First Use could not be set "
1515                         + "when Root CA certificate is set.");
1516                 return new Pair<>(
1517                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1518                         existingInternalConfig);
1519             }
1520         }
1521 
1522         boolean newNetwork = (existingInternalConfig == null);
1523         // This is needed to inform IpClient about any IP configuration changes.
1524         boolean hasIpChanged =
1525                 newNetwork || WifiConfigurationUtil.hasIpChanged(
1526                         existingInternalConfig, newInternalConfig);
1527         boolean hasProxyChanged =
1528                 newNetwork || WifiConfigurationUtil.hasProxyChanged(
1529                         existingInternalConfig, newInternalConfig);
1530         // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
1531         boolean hasCredentialChanged =
1532                 newNetwork || WifiConfigurationUtil.hasCredentialChanged(
1533                         existingInternalConfig, newInternalConfig);
1534         if (hasCredentialChanged) {
1535             newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
1536             newInternalConfig.setHasPreSharedKeyChanged(true);
1537             Log.i(TAG, "Credential changed for netId=" + newInternalConfig.networkId);
1538         }
1539 
1540         // Add it to our internal map. This will replace any existing network configuration for
1541         // updates.
1542         try {
1543             if (null != existingInternalConfig) {
1544                 mConfiguredNetworks.remove(existingInternalConfig.networkId);
1545             }
1546             mConfiguredNetworks.put(newInternalConfig);
1547         } catch (IllegalArgumentException e) {
1548             Log.e(TAG, "Failed to add network to config map", e);
1549             return new Pair<>(
1550                     new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1551                     existingInternalConfig);
1552         }
1553         if (removeExcessNetworks(uid, packageName)) {
1554             if (mConfiguredNetworks.getForAllUsers(newInternalConfig.networkId) == null) {
1555                 Log.e(TAG, "Cannot add network because number of configured networks is maxed.");
1556                 return new Pair<>(
1557                         new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID),
1558                         existingInternalConfig);
1559             }
1560         }
1561 
1562         // Only re-enable network: 1. add or update user saved network; 2. add or update a user
1563         // saved passpoint network framework consider it is a new network.
1564         if (!newInternalConfig.fromWifiNetworkSuggestion
1565                 && (!newInternalConfig.isPasspoint() || newNetwork)) {
1566             userEnabledNetwork(newInternalConfig.networkId);
1567         }
1568 
1569         // Stage the backup of the SettingsProvider package which backs this up.
1570         mBackupManagerProxy.notifyDataChanged();
1571 
1572         NetworkUpdateResult result = new NetworkUpdateResult(
1573                 newInternalConfig.networkId,
1574                 STATUS_SUCCESS,
1575                 hasIpChanged,
1576                 hasProxyChanged,
1577                 hasCredentialChanged,
1578                 newNetwork);
1579 
1580         localLog("addOrUpdateNetworkInternal: added/updated config."
1581                 + " netId=" + newInternalConfig.networkId
1582                 + " configKey=" + newInternalConfig.getProfileKey()
1583                 + " uid=" + Integer.toString(newInternalConfig.creatorUid)
1584                 + " name=" + newInternalConfig.creatorName);
1585         return new Pair<>(result, existingInternalConfig);
1586     }
1587 
1588     /**
1589      * Add a network or update a network configuration to our database.
1590      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1591      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1592      *
1593      * @param config provided WifiConfiguration object.
1594      * @param uid UID of the app requesting the network addition/modification.
1595      * @param packageName Package name of the app requesting the network addition/modification.
1596      * @param overrideCreator when this set to true, will overrider the creator to the current
1597      *                        modifier.
1598      * @return NetworkUpdateResult object representing status of the update.
1599      */
addOrUpdateNetwork(WifiConfiguration config, int uid, @Nullable String packageName, boolean overrideCreator)1600     public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid,
1601             @Nullable String packageName, boolean overrideCreator) {
1602         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) {
1603             Log.e(TAG, "UID " + uid + " not visible to the current user");
1604             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1605         }
1606         if (config == null) {
1607             Log.e(TAG, "Cannot add/update network with null config");
1608             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1609         }
1610         if (mPendingStoreRead) {
1611             Log.e(TAG, "Cannot add/update network before store is read!");
1612             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1613         }
1614         config.convertLegacyFieldsToSecurityParamsIfNeeded();
1615         WifiConfiguration existingConfig = getInternalConfiguredNetwork(config);
1616         if (!config.isEphemeral()) {
1617             // Removes the existing ephemeral network if it exists to add this configuration.
1618             if (existingConfig != null && existingConfig.isEphemeral()) {
1619                 // In this case, new connection for this config won't happen because same
1620                 // network is already registered as an ephemeral network.
1621                 // Clear the Ephemeral Network to address the situation.
1622                 removeNetwork(
1623                         existingConfig.networkId, existingConfig.creatorUid, config.creatorName);
1624             }
1625         }
1626 
1627         Pair<NetworkUpdateResult, WifiConfiguration> resultPair = addOrUpdateNetworkInternal(
1628                 config, uid, packageName, overrideCreator);
1629         NetworkUpdateResult result = resultPair.first;
1630         existingConfig = resultPair.second;
1631         if (!result.isSuccess()) {
1632             Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
1633             return result;
1634         }
1635         WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
1636         sendConfiguredNetworkChangedBroadcast(
1637                 result.isNewNetwork()
1638                         ? WifiManager.CHANGE_REASON_ADDED
1639                         : WifiManager.CHANGE_REASON_CONFIG_CHANGE, newConfig);
1640         // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
1641         if (!config.ephemeral && !config.isPasspoint()) {
1642             saveToStore(true);
1643         }
1644 
1645         for (OnNetworkUpdateListener listener : mListeners) {
1646             if (result.isNewNetwork()) {
1647                 listener.onNetworkAdded(
1648                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID));
1649             } else {
1650                 listener.onNetworkUpdated(
1651                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID),
1652                         createExternalWifiConfiguration(existingConfig, true, Process.WIFI_UID));
1653             }
1654         }
1655         return result;
1656     }
1657 
1658     /**
1659      * Adds a network configuration to our database if a matching configuration cannot be found.
1660      * @param config provided WifiConfiguration object.
1661      * @param uid    UID of the app requesting the network addition.
1662      * @return
1663      */
addNetwork(WifiConfiguration config, int uid)1664     public NetworkUpdateResult addNetwork(WifiConfiguration config, int uid) {
1665         config.convertLegacyFieldsToSecurityParamsIfNeeded();
1666         if (getInternalConfiguredNetwork(config) == null) {
1667             return addOrUpdateNetwork(config, uid);
1668         }
1669         return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1670     }
1671 
1672     /**
1673      * Add a network or update a network configuration to our database.
1674      * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1675      * network configuration. Otherwise, the networkId should refer to an existing configuration.
1676      *
1677      * @param config provided WifiConfiguration object.
1678      * @param uid    UID of the app requesting the network addition/modification.
1679      * @return NetworkUpdateResult object representing status of the update.
1680      */
addOrUpdateNetwork(WifiConfiguration config, int uid)1681     public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
1682         return addOrUpdateNetwork(config, uid, null, false);
1683     }
1684 
1685     /**
1686      * Increments the number of reboots since last use for each configuration.
1687      *
1688      * @see {@link WifiConfiguration#numRebootsSinceLastUse}
1689      */
incrementNumRebootsSinceLastUse()1690     public void incrementNumRebootsSinceLastUse() {
1691         getInternalConfiguredNetworks().forEach(config -> config.numRebootsSinceLastUse++);
1692         saveToStore(false);
1693     }
1694 
isDeviceOwnerProfileOwnerOrSystem(int uid, String packageName)1695     private boolean isDeviceOwnerProfileOwnerOrSystem(int uid, String packageName) {
1696         return mWifiPermissionsUtil.isDeviceOwner(uid, packageName)
1697                 || mWifiPermissionsUtil.isProfileOwner(uid, packageName)
1698                 || mWifiPermissionsUtil.isSystem(packageName, uid)
1699                 || mWifiPermissionsUtil.isSignedWithPlatformKey(uid);
1700     }
1701 
1702     /**
1703      * Removes excess networks in case the number of saved networks exceeds the max limit
1704      * specified in config_wifiMaxNumWifiConfigurations.
1705      *
1706      * If called by a non DO/PO/system app, and a limit on app-added networks is specified in
1707      * config_wifiMaxNumWifiConfigurationsForAppAddedNetworks, only removes excess
1708      * app-added networks.
1709      *
1710      * Configs are removed in ascending order of
1711      *     1. Non-carrier networks before carrier networks
1712      *     2. Non-connected networks before connected networks.
1713      *     3. Deletion priority {@see WifiConfiguration#getDeletionPriority()}
1714      *     4. Last use/creation/update time (lastUpdated/lastConnected or numRebootsSinceLastUse)
1715      *     5. Open and OWE networks before networks with other security types.
1716      *     6. Number of associations
1717      *
1718      * @param uid    UID of the app requesting the network addition/modification.
1719      * @param packageName Package name of the app requesting the network addition/modification.
1720      * @return {@code true} if networks were removed, {@code false} otherwise.
1721      */
removeExcessNetworks(int uid, String packageName)1722     private boolean removeExcessNetworks(int uid, String packageName) {
1723         final int maxNumTotalConfigs = mContext.getResources().getInteger(
1724                 R.integer.config_wifiMaxNumWifiConfigurations);
1725         final int maxNumAppAddedConfigs = mContext.getResources().getInteger(
1726                 R.integer.config_wifiMaxNumWifiConfigurationsAddedByAllApps);
1727 
1728         boolean callerIsApp = !isDeviceOwnerProfileOwnerOrSystem(uid, packageName);
1729         if (maxNumTotalConfigs < 0 && (!callerIsApp || maxNumAppAddedConfigs < 0)) {
1730             // Max number of saved networks not specified or does not need to be checked.
1731             return false;
1732         }
1733 
1734         int numExcessNetworks = -1;
1735         List<WifiConfiguration> networkList = getSavedNetworks(Process.WIFI_UID);
1736         if (maxNumTotalConfigs >= 0) {
1737             numExcessNetworks = networkList.size() - maxNumTotalConfigs;
1738         }
1739 
1740         if (callerIsApp && maxNumAppAddedConfigs >= 0) {
1741             List<WifiConfiguration> appAddedNetworks = networkList
1742                     .stream()
1743                     .filter(n -> !isDeviceOwnerProfileOwnerOrSystem(n.creatorUid, n.creatorName))
1744                     .collect(Collectors.toList());
1745             int numExcessAppAddedNetworks = appAddedNetworks.size() - maxNumAppAddedConfigs;
1746             if (numExcessAppAddedNetworks > 0) {
1747                 // Only enforce the limit on app-added networks if it has been exceeded.
1748                 // Otherwise, default to checking the limit on the total number of networks.
1749                 numExcessNetworks = numExcessAppAddedNetworks;
1750                 networkList = appAddedNetworks;
1751             }
1752         }
1753 
1754         if (numExcessNetworks <= 0) {
1755             return false;
1756         }
1757 
1758         List<WifiConfiguration> configsToDelete = networkList
1759                 .stream()
1760                 .sorted(Comparator.comparing((WifiConfiguration config) -> config.carrierId
1761                         != TelephonyManager.UNKNOWN_CARRIER_ID)
1762                         .thenComparing((WifiConfiguration config) -> config.isCurrentlyConnected)
1763                         .thenComparing((WifiConfiguration config) -> config.getDeletionPriority())
1764                         .thenComparing((WifiConfiguration config) -> -config.numRebootsSinceLastUse)
1765                         .thenComparing((WifiConfiguration config) ->
1766                                 Math.max(config.lastConnected, config.lastUpdated))
1767                         .thenComparing((WifiConfiguration config) -> {
1768                             try {
1769                                 int authType = config.getAuthType();
1770                                 return !(authType == WifiConfiguration.KeyMgmt.NONE
1771                                         || authType == WifiConfiguration.KeyMgmt.OWE);
1772                             } catch (IllegalStateException e) {
1773                                 // An invalid keymgmt configuration should be pruned first.
1774                                 return false;
1775                             }
1776                         })
1777                         .thenComparing((WifiConfiguration config) -> config.numAssociation))
1778                 .limit(numExcessNetworks)
1779                 .collect(Collectors.toList());
1780         for (WifiConfiguration config : configsToDelete) {
1781             mConfiguredNetworks.remove(config.networkId);
1782             localLog("removeExcessNetworks: removed config."
1783                     + " netId=" + config.networkId
1784                     + " configKey=" + config.getProfileKey());
1785         }
1786         return true;
1787     }
1788 
1789     /**
1790      * Removes the specified network configuration from our database.
1791      *
1792      * @param config provided WifiConfiguration object.
1793      * @param uid UID of the app requesting the network deletion.
1794      * @return true if successful, false otherwise.
1795      */
removeNetworkInternal(WifiConfiguration config, int uid)1796     private boolean removeNetworkInternal(WifiConfiguration config, int uid) {
1797         if (mVerboseLoggingEnabled) {
1798             Log.v(TAG, "Removing network " + config.getPrintableSsid());
1799         }
1800         // Remove any associated enterprise keys for saved enterprise networks. Passpoint network
1801         // will remove the enterprise keys when provider is uninstalled. Suggestion enterprise
1802         // networks will remove the enterprise keys when suggestion is removed.
1803         if (!config.fromWifiNetworkSuggestion && !config.isPasspoint() && config.isEnterprise()) {
1804             mWifiKeyStore.removeKeys(config.enterpriseConfig, false);
1805         }
1806 
1807         // Do not remove the user choice when passpoint or suggestion networks are removed from
1808         // WifiConfigManager. Will remove that when profile is deleted from PassointManager or
1809         // WifiNetworkSuggestionsManager.
1810         if (!config.isPasspoint() && !config.fromWifiNetworkSuggestion) {
1811             removeConnectChoiceFromAllNetworks(config.getProfileKey());
1812         }
1813         mConfiguredNetworks.remove(config.networkId);
1814         mScanDetailCaches.remove(config.networkId);
1815         // Stage the backup of the SettingsProvider package which backs this up.
1816         mBackupManagerProxy.notifyDataChanged();
1817         mWifiBlocklistMonitor.handleNetworkRemoved(config.SSID);
1818 
1819         localLog("removeNetworkInternal: removed config."
1820                 + " netId=" + config.networkId
1821                 + " configKey=" + config.getProfileKey()
1822                 + " uid=" + Integer.toString(uid)
1823                 + " name=" + mContext.getPackageManager().getNameForUid(uid));
1824         return true;
1825     }
1826 
1827     /**
1828      * Removes the specified network configuration from our database.
1829      *
1830      * @param networkId network ID of the provided network.
1831      * @param uid       UID of the app requesting the network deletion.
1832      * @return true if successful, false otherwise.
1833      */
removeNetwork(int networkId, int uid, String packageName)1834     public boolean removeNetwork(int networkId, int uid, String packageName) {
1835         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) {
1836             Log.e(TAG, "UID " + uid + " not visible to the current user");
1837             return false;
1838         }
1839         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1840         if (config == null) {
1841             return false;
1842         }
1843         if (!canModifyNetwork(config, uid, packageName)) {
1844             Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1845                     + config.getProfileKey());
1846             return false;
1847         }
1848         if (!removeNetworkInternal(config, uid)) {
1849             Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1850             return false;
1851         }
1852         if (networkId == mLastSelectedNetworkId) {
1853             clearLastSelectedNetwork();
1854         }
1855         if (!config.ephemeral && !config.isPasspoint()) {
1856             mLruConnectionTracker.removeNetwork(config);
1857         }
1858         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED, config);
1859         // Unless the removed network is ephemeral or Passpoint, persist the network removal.
1860         if (!config.ephemeral && !config.isPasspoint()) {
1861             saveToStore(true);
1862         }
1863         for (OnNetworkUpdateListener listener : mListeners) {
1864             listener.onNetworkRemoved(
1865                     createExternalWifiConfiguration(config, true, Process.WIFI_UID));
1866         }
1867         return true;
1868     }
1869 
getCreatorPackageName(WifiConfiguration config)1870     private String getCreatorPackageName(WifiConfiguration config) {
1871         String creatorName = config.creatorName;
1872         // getNameForUid (Stored in WifiConfiguration.creatorName) returns a concatenation of name
1873         // and uid for shared UIDs ("name:uid").
1874         if (!creatorName.contains(":")) {
1875             return creatorName; // regular app not using shared UID.
1876         }
1877         // Separate the package name from the string for app using shared UID.
1878         return creatorName.substring(0, creatorName.indexOf(":"));
1879     }
1880 
1881     /**
1882      * Remove all networks associated with an application.
1883      *
1884      * @param app Application info of the package of networks to remove.
1885      * @return the {@link Set} of networks that were removed by this call. Networks which matched
1886      *         but failed to remove are omitted from this set.
1887      */
removeNetworksForApp(ApplicationInfo app)1888     public Set<Integer> removeNetworksForApp(ApplicationInfo app) {
1889         if (app == null || app.packageName == null) {
1890             return Collections.<Integer>emptySet();
1891         }
1892         Log.d(TAG, "Remove all networks for app " + app);
1893         Set<Integer> removedNetworks = new ArraySet<>();
1894         WifiConfiguration[] copiedConfigs =
1895                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1896         for (WifiConfiguration config : copiedConfigs) {
1897             if (app.uid != config.creatorUid
1898                     || !app.packageName.equals(getCreatorPackageName(config))) {
1899                 continue;
1900             }
1901             localLog("Removing network " + config.SSID
1902                     + ", application \"" + app.packageName + "\" uninstalled"
1903                     + " from user " + UserHandle.getUserHandleForUid(app.uid));
1904             if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) {
1905                 removedNetworks.add(config.networkId);
1906             }
1907         }
1908         return removedNetworks;
1909     }
1910 
1911     /**
1912      * Remove all networks associated with a user.
1913      *
1914      * @param userId The identifier of the user which is being removed.
1915      * @return the {@link Set} of networks that were removed by this call. Networks which matched
1916      *         but failed to remove are omitted from this set.
1917      */
removeNetworksForUser(int userId)1918     Set<Integer> removeNetworksForUser(int userId) {
1919         Log.d(TAG, "Remove all networks for user " + userId);
1920         Set<Integer> removedNetworks = new ArraySet<>();
1921         WifiConfiguration[] copiedConfigs =
1922                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1923         for (WifiConfiguration config : copiedConfigs) {
1924             if (userId != UserHandle.getUserHandleForUid(config.creatorUid).getIdentifier()) {
1925                 continue;
1926             }
1927             localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1928             if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) {
1929                 removedNetworks.add(config.networkId);
1930             }
1931         }
1932         return removedNetworks;
1933     }
1934 
1935     /**
1936      * Iterates through the internal list of configured networks and removes any ephemeral or
1937      * passpoint network configurations which are transient in nature.
1938      *
1939      * @return true if a network was removed, false otherwise.
1940      */
removeAllEphemeralOrPasspointConfiguredNetworks()1941     public boolean removeAllEphemeralOrPasspointConfiguredNetworks() {
1942         if (mVerboseLoggingEnabled) {
1943             Log.v(TAG, "Removing all passpoint or ephemeral configured networks");
1944         }
1945         boolean didRemove = false;
1946         WifiConfiguration[] copiedConfigs =
1947                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1948         for (WifiConfiguration config : copiedConfigs) {
1949             if (config.isPasspoint()) {
1950                 Log.d(TAG, "Removing passpoint network config " + config.getProfileKey());
1951                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1952                 didRemove = true;
1953             } else if (config.ephemeral) {
1954                 Log.d(TAG, "Removing ephemeral network config " + config.getProfileKey());
1955                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1956                 didRemove = true;
1957             }
1958         }
1959         return didRemove;
1960     }
1961 
1962     /**
1963      * Removes the suggestion network configuration matched with WifiConfiguration provided.
1964      * @param suggestion WifiConfiguration for suggestion which needs to remove
1965      * @return true if a network was removed, false otherwise.
1966      */
removeSuggestionConfiguredNetwork(@onNull WifiConfiguration suggestion)1967     public boolean removeSuggestionConfiguredNetwork(@NonNull WifiConfiguration suggestion) {
1968         WifiConfiguration config = getInternalConfiguredNetwork(
1969                 suggestion.getProfileKey());
1970         if (config != null && config.ephemeral && config.fromWifiNetworkSuggestion) {
1971             Log.d(TAG, "Removing suggestion network config " + config.getProfileKey());
1972             return removeNetwork(config.networkId, suggestion.creatorUid, suggestion.creatorName);
1973         }
1974         return false;
1975     }
1976 
1977     /**
1978      * Removes the passpoint network configuration matched with {@code configKey} provided.
1979      *
1980      * @param configKey Config Key for the corresponding passpoint.
1981      * @return true if a network was removed, false otherwise.
1982      */
removePasspointConfiguredNetwork(@onNull String configKey)1983     public boolean removePasspointConfiguredNetwork(@NonNull String configKey) {
1984         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
1985         if (config != null && config.isPasspoint()) {
1986             Log.d(TAG, "Removing passpoint network config " + config.getProfileKey());
1987             return removeNetwork(config.networkId, config.creatorUid, config.creatorName);
1988         }
1989         return false;
1990     }
1991 
1992     /**
1993      * Removes all save networks configurations not created by the caller.
1994      *
1995      * @param callerUid the uid of the caller
1996      * @return {@code true} if at least one network is removed.
1997      */
removeNonCallerConfiguredNetwork(int callerUid)1998     public boolean removeNonCallerConfiguredNetwork(int callerUid) {
1999         if (mVerboseLoggingEnabled) {
2000             Log.v(TAG, "removeNonCallerConfiguredNetwork caller = " + callerUid);
2001         }
2002         boolean didRemove = false;
2003         WifiConfiguration[] copiedConfigs =
2004                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
2005         for (WifiConfiguration config : copiedConfigs) {
2006             if (config.creatorUid != callerUid) {
2007                 Log.d(TAG, "Removing non-caller network config " + config.getProfileKey());
2008                 removeNetwork(config.networkId, config.creatorUid, config.creatorName);
2009                 didRemove = true;
2010             }
2011         }
2012         return didRemove;
2013     }
2014 
2015     /**
2016      * Check whether a network belong to a known list of networks that may not support randomized
2017      * MAC.
2018      * @param networkId
2019      * @return true if the network is in the hotlist and MAC randomization is enabled.
2020      */
isInFlakyRandomizationSsidHotlist(int networkId)2021     public boolean isInFlakyRandomizationSsidHotlist(int networkId) {
2022         WifiConfiguration config = getConfiguredNetwork(networkId);
2023         return config != null
2024                 && config.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_NONE
2025                 && mDeviceConfigFacade.getRandomizationFlakySsidHotlist().contains(config.SSID);
2026     }
2027 
2028     /**
2029      * Helper method to set the publicly exposed status for the network and send out the network
2030      * status change broadcast.
2031      */
setNetworkStatus(WifiConfiguration config, int status)2032     private void setNetworkStatus(WifiConfiguration config, int status) {
2033         config.status = status;
2034         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE, config);
2035     }
2036 
2037     /**
2038      * Update a network's status (both internal and public) according to the update reason and
2039      * its current state.
2040      *
2041      * Each network has 2 status:
2042      * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
2043      * for temporarily disabling a network for Network Selector.
2044      * 2. Status: This is the exposed status for a network. This is mostly set by
2045      * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
2046      * {@link WifiManager#disableNetwork(int)}.
2047      *
2048      * @param networkId network ID of the network that needs the update.
2049      * @param reason    reason to update the network.
2050      * @return true if the input configuration has been updated, false otherwise.
2051      */
updateNetworkSelectionStatus(int networkId, int reason)2052     public boolean updateNetworkSelectionStatus(int networkId, int reason) {
2053         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2054         if (config == null) {
2055             return false;
2056         }
2057         return updateNetworkSelectionStatus(config, reason);
2058     }
2059 
updateNetworkSelectionStatus(@onNull WifiConfiguration config, int reason)2060     private boolean updateNetworkSelectionStatus(@NonNull WifiConfiguration config, int reason) {
2061         int prevNetworkSelectionStatus = config.getNetworkSelectionStatus()
2062                 .getNetworkSelectionStatus();
2063         if (!mWifiBlocklistMonitor.updateNetworkSelectionStatus(config, reason)) {
2064             return false;
2065         }
2066         int newNetworkSelectionStatus = config.getNetworkSelectionStatus()
2067                 .getNetworkSelectionStatus();
2068         if (prevNetworkSelectionStatus != newNetworkSelectionStatus) {
2069             sendNetworkSelectionStatusChangedUpdate(config, newNetworkSelectionStatus, reason);
2070             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE, config);
2071         }
2072         saveToStore(false);
2073         return true;
2074     }
2075 
sendNetworkSelectionStatusChangedUpdate(@onNull WifiConfiguration config, int newNetworkSelectionStatus, int disableReason)2076     private void sendNetworkSelectionStatusChangedUpdate(@NonNull WifiConfiguration config,
2077             int newNetworkSelectionStatus, int disableReason) {
2078         switch (newNetworkSelectionStatus) {
2079             case NetworkSelectionStatus.NETWORK_SELECTION_ENABLED:
2080                 for (OnNetworkUpdateListener listener : mListeners) {
2081                     listener.onNetworkEnabled(
2082                             createExternalWifiConfiguration(config, true, Process.WIFI_UID));
2083                 }
2084                 break;
2085             case NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED:
2086                 for (OnNetworkUpdateListener listener : mListeners) {
2087                     listener.onNetworkTemporarilyDisabled(
2088                             createExternalWifiConfiguration(config, true, Process.WIFI_UID),
2089                             disableReason);
2090                 }
2091                 break;
2092             case NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED:
2093                 for (OnNetworkUpdateListener listener : mListeners) {
2094                     WifiConfiguration configForListener = new WifiConfiguration(config);
2095                     listener.onNetworkPermanentlyDisabled(
2096                             createExternalWifiConfiguration(config, true, Process.WIFI_UID),
2097                             disableReason);
2098                 }
2099                 break;
2100             default:
2101                 // all cases covered
2102         }
2103     }
2104 
2105     /**
2106      * Re-enable all temporary disabled configured networks.
2107      */
enableTemporaryDisabledNetworks()2108     public void enableTemporaryDisabledNetworks() {
2109         mWifiBlocklistMonitor.clearBssidBlocklist();
2110         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2111             if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
2112                 updateNetworkSelectionStatus(config,
2113                         NetworkSelectionStatus.DISABLED_NONE);
2114             }
2115         }
2116     }
2117 
2118     /**
2119      * Attempt to re-enable a network for network selection, if this network was either:
2120      * a) Previously temporarily disabled, but its disable timeout has expired, or
2121      * b) Previously disabled because of a user switch, but is now visible to the current
2122      * user.
2123      *
2124      * @param networkId the id of the network to be checked for possible unblock (due to timeout)
2125      * @return true if the network identified by {@param networkId} was re-enabled for qualified
2126      * network selection, false otherwise.
2127      */
tryEnableNetwork(int networkId)2128     public boolean tryEnableNetwork(int networkId) {
2129         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2130         if (config == null) {
2131             return false;
2132         }
2133         if (mWifiBlocklistMonitor.shouldEnableNetwork(config)) {
2134             return updateNetworkSelectionStatus(config, NetworkSelectionStatus.DISABLED_NONE);
2135         }
2136         return false;
2137     }
2138 
2139     /**
2140      * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
2141      *
2142      * @param networkId     network ID of the network that needs the update.
2143      * @param disableOthers Whether to disable all other networks or not. This is used to indicate
2144      *                      that the app requested connection to a specific network.
2145      * @param uid           uid of the app requesting the update.
2146      * @param packageName   Package name of calling apps
2147      * @return true if it succeeds, false otherwise
2148      */
enableNetwork(int networkId, boolean disableOthers, int uid, @NonNull String packageName)2149     public boolean enableNetwork(int networkId, boolean disableOthers, int uid,
2150                                  @NonNull String packageName) {
2151         if (mVerboseLoggingEnabled) {
2152             Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
2153         }
2154         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) {
2155             Log.e(TAG, "UID " + uid + " not visible to the current user");
2156             return false;
2157         }
2158         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2159         if (config == null) {
2160             return false;
2161         }
2162         // Set the "last selected" flag even if the app does not have permissions to modify this
2163         // network config. Apps are allowed to connect to networks even if they don't have
2164         // permission to modify it.
2165         if (disableOthers) {
2166             setLastSelectedNetwork(networkId);
2167         }
2168         if (!canModifyNetwork(config, uid, packageName)) {
2169             Log.e(TAG, "UID " + uid +  " package " + packageName
2170                     + " does not have permission to update configuration "
2171                     + config.getProfileKey());
2172             return false;
2173         }
2174         if (!updateNetworkSelectionStatus(
2175                 networkId, WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)) {
2176             return false;
2177         }
2178         mWifiBlocklistMonitor.clearBssidBlocklistForSsid(config.SSID);
2179         saveToStore(true);
2180         return true;
2181     }
2182 
2183     /**
2184      * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
2185      *
2186      * @param networkId network ID of the network that needs the update.
2187      * @param uid       uid of the app requesting the update.
2188      * @return true if it succeeds, false otherwise
2189      */
disableNetwork(int networkId, int uid, @NonNull String packageName)2190     public boolean disableNetwork(int networkId, int uid, @NonNull String packageName) {
2191         if (mVerboseLoggingEnabled) {
2192             Log.v(TAG, "Disabling network " + networkId);
2193         }
2194         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) {
2195             Log.e(TAG, "UID " + uid + " package " + packageName
2196                     + " not visible to the current user");
2197             return false;
2198         }
2199         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2200         if (config == null) {
2201             return false;
2202         }
2203         // Reset the "last selected" flag even if the app does not have permissions to modify this
2204         // network config.
2205         if (networkId == mLastSelectedNetworkId) {
2206             clearLastSelectedNetwork();
2207         }
2208         if (!canModifyNetwork(config, uid, packageName)) {
2209             Log.e(TAG, "UID " + uid + " package " + packageName
2210                     + " does not have permission to update configuration "
2211                     + config.getProfileKey());
2212             return false;
2213         }
2214         if (!updateNetworkSelectionStatus(
2215                 networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
2216             return false;
2217         }
2218         saveToStore(true);
2219         return true;
2220     }
2221 
2222     /**
2223      * Changes the user's choice to allow auto-join using the
2224      * {@link WifiManager#allowAutojoin(int, boolean)} API.
2225      *
2226      * @param networkId network ID of the network that needs the update.
2227      * @param choice the choice to allow auto-join or not
2228      * @return true if it succeeds, false otherwise
2229      */
allowAutojoin(int networkId, boolean choice)2230     public boolean allowAutojoin(int networkId, boolean choice) {
2231         if (mVerboseLoggingEnabled) {
2232             Log.v(TAG, "Setting allowAutojoin to " + choice + " for netId " + networkId);
2233         }
2234         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2235         if (config == null) {
2236             Log.e(TAG, "allowAutojoin: Supplied networkId " + networkId
2237                     + " has no matching config");
2238             return false;
2239         }
2240 
2241         config.allowAutojoin = choice;
2242         if (!choice) {
2243             removeConnectChoiceFromAllNetworks(config.getProfileKey());
2244             clearConnectChoiceInternal(config);
2245         }
2246         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE, config);
2247         if (!config.ephemeral) {
2248             saveToStore(true);
2249         }
2250         return true;
2251     }
2252 
2253     /**
2254      * Updates the last connected UID for the provided configuration.
2255      *
2256      * @param networkId network ID corresponding to the network.
2257      * @param uid       uid of the app requesting the connection.
2258      * @return true if the network was found, false otherwise.
2259      */
updateLastConnectUid(int networkId, int uid)2260     private boolean updateLastConnectUid(int networkId, int uid) {
2261         if (mVerboseLoggingEnabled) {
2262             Log.v(TAG, "Update network last connect UID for " + networkId);
2263         }
2264         if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) {
2265             Log.e(TAG, "UID " + uid + " not visible to the current user");
2266             return false;
2267         }
2268         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2269         if (config == null) {
2270             return false;
2271         }
2272         config.lastConnectUid = uid;
2273         return true;
2274     }
2275 
2276     /**
2277      * Updates a network configuration after a successful connection to it.
2278      *
2279      * This method updates the following WifiConfiguration elements:
2280      * 1. Set the |lastConnected| timestamp.
2281      * 2. Increment |numAssociation| counter.
2282      * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
2283      * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
2284      * 5. Set the status of network to |CURRENT|.
2285      * 6. Set the |isCurrentlyConnected| flag to true.
2286      * 7. Set the |isUserSelected| flag.
2287      *
2288      * @param networkId network ID corresponding to the network.
2289      * @param isUserSelected network is user selected.
2290      * @param shouldSetUserConnectChoice setup user connect choice on this network.
2291      * @param rssi signal strength of the connected network.
2292      * @return true if the network was found, false otherwise.
2293      */
updateNetworkAfterConnect(int networkId, boolean isUserSelected, boolean shouldSetUserConnectChoice, int rssi)2294     public boolean updateNetworkAfterConnect(int networkId, boolean isUserSelected,
2295             boolean shouldSetUserConnectChoice, int rssi) {
2296         if (mVerboseLoggingEnabled) {
2297             Log.v(TAG, "Update network after connect for " + networkId);
2298         }
2299         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2300         if (config == null) {
2301             return false;
2302         }
2303 
2304         // Only record connection order for non-passpoint from user saved or suggestion.
2305         if (!config.isPasspoint() && (config.fromWifiNetworkSuggestion || !config.ephemeral)) {
2306             mLruConnectionTracker.addNetwork(config);
2307         }
2308         if (shouldSetUserConnectChoice) {
2309             setUserConnectChoice(config.networkId, rssi);
2310         }
2311         config.lastConnected = mClock.getWallClockMillis();
2312         config.numRebootsSinceLastUse = 0;
2313         config.numAssociation++;
2314         config.getNetworkSelectionStatus().clearDisableReasonCounter();
2315         config.getNetworkSelectionStatus().setHasEverConnected(true);
2316         setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
2317         config.isCurrentlyConnected = true;
2318         config.setIsUserSelected(isUserSelected);
2319         saveToStore(false);
2320         return true;
2321     }
2322 
2323     /**
2324      * Set captive portal to be detected for this network.
2325      * @param networkId
2326      */
noteCaptivePortalDetected(int networkId)2327     public void noteCaptivePortalDetected(int networkId) {
2328         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2329         if (config != null) {
2330             config.getNetworkSelectionStatus().setHasNeverDetectedCaptivePortal(false);
2331         }
2332     }
2333 
2334     /**
2335      * Updates a network configuration after disconnection from it.
2336      *
2337      * This method updates the following WifiConfiguration elements:
2338      * 1. Set the |lastDisconnected| timestamp.
2339      * 2. Set the status of network back to |ENABLED|.
2340      * 3. Set the |isCurrentlyConnected| flag to false.
2341      *
2342      * @param networkId network ID corresponding to the network.
2343      * @return true if the network was found, false otherwise.
2344      */
updateNetworkAfterDisconnect(int networkId)2345     public boolean updateNetworkAfterDisconnect(int networkId) {
2346         if (mVerboseLoggingEnabled) {
2347             Log.v(TAG, "Update network after disconnect for " + networkId);
2348         }
2349         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2350         if (config == null) {
2351             return false;
2352         }
2353         config.lastDisconnected = mClock.getWallClockMillis();
2354         config.randomizedMacExpirationTimeMs = Math.max(config.randomizedMacExpirationTimeMs,
2355                 config.lastDisconnected + NON_PERSISTENT_MAC_WAIT_AFTER_DISCONNECT_MS);
2356         // If the network hasn't been disabled, mark it back as
2357         // enabled after disconnection.
2358         if (config.status == WifiConfiguration.Status.CURRENT) {
2359             setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
2360         }
2361         config.isCurrentlyConnected = false;
2362         config.setIsUserSelected(false);
2363         saveToStore(false);
2364         return true;
2365     }
2366 
2367     /**
2368      * Set default GW MAC address for the provided network.
2369      *
2370      * @param networkId network ID corresponding to the network.
2371      * @param macAddress MAC address of the gateway to be set.
2372      * @return true if the network was found, false otherwise.
2373      */
setNetworkDefaultGwMacAddress(int networkId, String macAddress)2374     public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
2375         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2376         if (config == null) {
2377             return false;
2378         }
2379         config.defaultGwMacAddress = macAddress;
2380         return true;
2381     }
2382 
2383     /**
2384      * Clear the {@link NetworkSelectionStatus#mCandidate},
2385      * {@link NetworkSelectionStatus#mCandidateScore} &
2386      * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
2387      *
2388      * This is invoked by Network Selector at the start of every selection procedure to clear all
2389      * configured networks' scan-result-candidates.
2390      *
2391      * @param networkId network ID corresponding to the network.
2392      * @return true if the network was found, false otherwise.
2393      */
clearNetworkCandidateScanResult(int networkId)2394     public boolean clearNetworkCandidateScanResult(int networkId) {
2395         if (mVerboseLoggingEnabled) {
2396             Log.v(TAG, "Clear network candidate scan result for " + networkId);
2397         }
2398         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2399         if (config == null) {
2400             return false;
2401         }
2402         config.getNetworkSelectionStatus().setCandidate(null);
2403         config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
2404         config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
2405         config.getNetworkSelectionStatus().setCandidateSecurityParams(null);
2406         return true;
2407     }
2408 
2409     /**
2410      * Set the {@link NetworkSelectionStatus#mCandidate},
2411      * {@link NetworkSelectionStatus#mCandidateScore} &
2412      * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
2413      *
2414      * This is invoked by Network Selector when it sees a network during network selection procedure
2415      * to set the scan result candidate.
2416      *
2417      * @param networkId  network ID corresponding to the network.
2418      * @param scanResult Candidate ScanResult associated with this network.
2419      * @param score      Score assigned to the candidate.
2420      * @param params     Security params for this candidate.
2421      * @return true if the network was found, false otherwise.
2422      */
setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score, SecurityParams params)2423     public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score,
2424             SecurityParams params) {
2425         if (mVerboseLoggingEnabled) {
2426             Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId
2427                     + " with security params " + params);
2428         }
2429         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2430         if (config == null) {
2431             Log.e(TAG, "Cannot find network for " + networkId);
2432             return false;
2433         }
2434         config.getNetworkSelectionStatus().setCandidate(scanResult);
2435         config.getNetworkSelectionStatus().setCandidateScore(score);
2436         config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
2437         config.getNetworkSelectionStatus().setCandidateSecurityParams(params);
2438         return true;
2439     }
2440 
2441     /**
2442      * Set the {@link NetworkSelectionStatus#mLastUsedSecurityParams}.
2443      *
2444      * @param networkId  network ID corresponding to the network.
2445      * @param params     Security params for this candidate.
2446      * @return true if the network was found, false otherwise.
2447      */
setNetworkLastUsedSecurityParams(int networkId, SecurityParams params)2448     public boolean setNetworkLastUsedSecurityParams(int networkId, SecurityParams params) {
2449         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2450         if (config == null) {
2451             Log.e(TAG, "Cannot find network for " + networkId);
2452             return false;
2453         }
2454         config.getNetworkSelectionStatus().setLastUsedSecurityParams(params);
2455         if (mVerboseLoggingEnabled) {
2456             Log.v(TAG, "Update last used security param for " + config.getProfileKey()
2457                     + " with security type " + params.getSecurityType());
2458         }
2459         return true;
2460     }
2461 
2462     /**
2463      * Iterate through all the saved networks and remove the provided configuration from the
2464      * {@link NetworkSelectionStatus#mConnectChoice} from them.
2465      *
2466      * This is invoked when a network is removed from our records.
2467      *
2468      * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
2469      */
removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey)2470     public void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
2471         if (mVerboseLoggingEnabled) {
2472             Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
2473         }
2474         if (connectChoiceConfigKey == null) {
2475             return;
2476         }
2477         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2478             WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
2479             String connectChoice = status.getConnectChoice();
2480             if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
2481                 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
2482                         + " : " + config.networkId);
2483                 clearConnectChoiceInternal(config);
2484             }
2485         }
2486         for (OnNetworkUpdateListener listener : mListeners) {
2487             listener.onConnectChoiceRemoved(connectChoiceConfigKey);
2488         }
2489     }
2490 
2491     /**
2492      * Increments the number of no internet access reports in the provided network.
2493      *
2494      * @param networkId network ID corresponding to the network.
2495      * @return true if the network was found, false otherwise.
2496      */
incrementNetworkNoInternetAccessReports(int networkId)2497     public boolean incrementNetworkNoInternetAccessReports(int networkId) {
2498         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2499         if (config == null) {
2500             return false;
2501         }
2502         config.numNoInternetAccessReports++;
2503         config.validatedInternetAccess = false;
2504         return true;
2505     }
2506 
2507     /**
2508      * Sets the internet access is validated or not in the provided network.
2509      *
2510      * @param networkId network ID corresponding to the network.
2511      * @param validated Whether access is validated or not.
2512      * @return true if the network was found, false otherwise.
2513      */
setNetworkValidatedInternetAccess(int networkId, boolean validated)2514     public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
2515         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2516         if (config == null) {
2517             return false;
2518         }
2519         config.validatedInternetAccess = validated;
2520         if (validated) {
2521             config.numNoInternetAccessReports = 0;
2522         }
2523         saveToStore(false);
2524         return true;
2525     }
2526 
2527     /**
2528      * Sets whether the internet access is expected or not in the provided network.
2529      *
2530      * @param networkId network ID corresponding to the network.
2531      * @param expected  Whether access is expected or not.
2532      * @return true if the network was found, false otherwise.
2533      */
setNetworkNoInternetAccessExpected(int networkId, boolean expected)2534     public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
2535         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2536         if (config == null) {
2537             return false;
2538         }
2539         config.noInternetAccessExpected = expected;
2540         return true;
2541     }
2542 
2543     /**
2544      * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
2545      * is done when either the corresponding network is either removed or disabled.
2546      */
clearLastSelectedNetwork()2547     public void clearLastSelectedNetwork() {
2548         if (mVerboseLoggingEnabled) {
2549             Log.v(TAG, "Clearing last selected network");
2550         }
2551         mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2552         mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
2553     }
2554 
2555     /**
2556      * Helper method to mark a network as the last selected one by an app/user. This is set
2557      * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
2558      * This is used by network selector to assign a special bonus during network selection.
2559      */
setLastSelectedNetwork(int networkId)2560     private void setLastSelectedNetwork(int networkId) {
2561         if (mVerboseLoggingEnabled) {
2562             Log.v(TAG, "Setting last selected network to " + networkId);
2563         }
2564         mLastSelectedNetworkId = networkId;
2565         mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
2566     }
2567 
2568     /**
2569      * Retrieve the network Id corresponding to the last network that was explicitly selected by
2570      * an app/user.
2571      *
2572      * @return network Id corresponding to the last selected network.
2573      */
getLastSelectedNetwork()2574     public int getLastSelectedNetwork() {
2575         return mLastSelectedNetworkId;
2576     }
2577 
2578     /**
2579      * Retrieve the configKey corresponding to the last network that was explicitly selected by
2580      * an app/user.
2581      *
2582      * @return network Id corresponding to the last selected network.
2583      */
getLastSelectedNetworkConfigKey()2584     public String getLastSelectedNetworkConfigKey() {
2585         if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
2586             return "";
2587         }
2588         WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
2589         if (config == null) {
2590             return "";
2591         }
2592         return config.getProfileKey();
2593     }
2594 
2595     /**
2596      * Retrieve the time stamp at which a network was explicitly selected by an app/user.
2597      *
2598      * @return timestamp in milliseconds from boot when this was set.
2599      */
getLastSelectedTimeStamp()2600     public long getLastSelectedTimeStamp() {
2601         return mLastSelectedTimeStamp;
2602     }
2603 
2604     /**
2605      * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
2606      * network.
2607      *
2608      * @param networkId network ID corresponding to the network.
2609      * @return existing {@link ScanDetailCache} entry if one exists or null.
2610      */
getScanDetailCacheForNetwork(int networkId)2611     public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
2612         return mScanDetailCaches.get(networkId);
2613     }
2614 
2615     /**
2616      * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
2617      * the provided network.
2618      *
2619      * @param config configuration corresponding to the the network.
2620      * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
2621      * this network.
2622      */
getOrCreateScanDetailCacheForNetwork(WifiConfiguration config)2623     private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
2624         if (config == null) return null;
2625         ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
2626         if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
2627             cache = new ScanDetailCache(
2628                     config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
2629             mScanDetailCaches.put(config.networkId, cache);
2630         }
2631         return cache;
2632     }
2633 
2634     /**
2635      * Saves the provided ScanDetail into the corresponding scan detail cache entry
2636      * {@link #mScanDetailCaches} for the provided network.
2637      *
2638      * @param config     configuration corresponding to the the network.
2639      * @param scanDetail new scan detail instance to be saved into the cache.
2640      */
saveToScanDetailCacheForNetwork( WifiConfiguration config, ScanDetail scanDetail)2641     private void saveToScanDetailCacheForNetwork(
2642             WifiConfiguration config, ScanDetail scanDetail) {
2643         ScanResult scanResult = scanDetail.getScanResult();
2644 
2645         WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID);
2646         network.addFrequency(scanResult.frequency);
2647         ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
2648         if (scanDetailCache == null) {
2649             Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
2650             return;
2651         }
2652 
2653         // Adding a new BSSID
2654         if (config.ephemeral) {
2655             // For an ephemeral Wi-Fi config, the ScanResult should be considered
2656             // untrusted.
2657             scanResult.untrusted = true;
2658         }
2659 
2660         // Add the scan detail to this network's scan detail cache.
2661         scanDetailCache.put(scanDetail);
2662     }
2663 
2664     /**
2665      * Retrieves a configured network corresponding to the provided scan detail if one exists.
2666      *
2667      * @param scanDetail ScanDetail instance  to use for looking up the network.
2668      * @return WifiConfiguration object representing the network corresponding to the scanDetail,
2669      * null if none exists.
2670      */
getSavedNetworkForScanDetail(ScanDetail scanDetail)2671     public WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) {
2672         ScanResult scanResult = scanDetail.getScanResult();
2673         if (scanResult == null) {
2674             Log.e(TAG, "No scan result found in scan detail");
2675             return null;
2676         }
2677         return getSavedNetworkForScanResult(scanResult);
2678     }
2679 
2680     /**
2681      * Retrieves a configured network corresponding to the provided scan result if one exists.
2682      *
2683      * @param scanResult ScanResult instance to use for looking up the network.
2684      * @return WifiConfiguration object representing the network corresponding to the scanResult,
2685      * null if none exists.
2686      */
getSavedNetworkForScanResult(@onNull ScanResult scanResult)2687     public WifiConfiguration getSavedNetworkForScanResult(@NonNull ScanResult scanResult) {
2688         WifiConfiguration config = null;
2689         try {
2690             config = mConfiguredNetworks.getByScanResultForCurrentUser(scanResult);
2691         } catch (IllegalArgumentException e) {
2692             Log.e(TAG, "Failed to lookup network from config map", e);
2693         }
2694         if (config != null) {
2695             if (mVerboseLoggingEnabled) {
2696                 Log.v(TAG, "getSavedNetworkFromScanResult Found " + config.getProfileKey()
2697                         + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
2698             }
2699         }
2700         return config;
2701     }
2702 
2703     /**
2704      * Caches the provided |scanDetail| into the corresponding scan detail cache entry
2705      * {@link #mScanDetailCaches} for the retrieved network.
2706      *
2707      * @param scanDetail input a scanDetail from the scan result
2708      */
updateScanDetailCacheFromScanDetailForSavedNetwork(ScanDetail scanDetail)2709     public void updateScanDetailCacheFromScanDetailForSavedNetwork(ScanDetail scanDetail) {
2710         WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
2711         if (network == null) {
2712             return;
2713         }
2714         saveToScanDetailCacheForNetwork(network, scanDetail);
2715     }
2716     /**
2717      * Retrieves a configured network corresponding to the provided scan detail if one exists and
2718      * caches the provided |scanDetail| into the corresponding scan detail cache entry
2719      * {@link #mScanDetailCaches} for the retrieved network.
2720      *
2721      * @param scanDetail input a scanDetail from the scan result
2722      * @return WifiConfiguration object representing the network corresponding to the scanDetail,
2723      * null if none exists.
2724      */
getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail)2725     public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) {
2726         WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
2727         if (network == null) {
2728             return null;
2729         }
2730         saveToScanDetailCacheForNetwork(network, scanDetail);
2731         // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
2732         // Information Element (IE), into the associated WifiConfigurations. Most of the
2733         // time there is no TIM IE in the scan result (Probe Response instead of Beacon
2734         // Frame), these scanResult DTIM's are negative and ignored.
2735         // Used for metrics collection.
2736         if (scanDetail.getNetworkDetail() != null
2737                 && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
2738             network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
2739         }
2740         return createExternalWifiConfiguration(network, true, Process.WIFI_UID);
2741     }
2742 
2743     /**
2744      * Update the scan detail cache associated with current connected network with latest
2745      * RSSI value in the provided WifiInfo.
2746      * This is invoked when we get an RSSI poll update after connection.
2747      *
2748      * @param info WifiInfo instance pointing to the current connected network.
2749      */
updateScanDetailCacheFromWifiInfo(WifiInfo info)2750     public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
2751         WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
2752         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
2753         if (config != null && scanDetailCache != null) {
2754             ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
2755             if (scanDetail != null) {
2756                 ScanResult result = scanDetail.getScanResult();
2757                 long previousSeen = result.seen;
2758                 int previousRssi = result.level;
2759                 // Update the scan result
2760                 scanDetail.setSeen();
2761                 result.level = info.getRssi();
2762                 // Average the RSSI value
2763                 long maxAge = SCAN_RESULT_MAXIMUM_AGE_MS;
2764                 long age = result.seen - previousSeen;
2765                 if (previousSeen > 0 && age > 0 && age < maxAge / 2) {
2766                     // Average the RSSI with previously seen instances of this scan result
2767                     double alpha = 0.5 - (double) age / (double) maxAge;
2768                     result.level = (int) ((double) result.level * (1 - alpha)
2769                                         + (double) previousRssi * alpha);
2770                 }
2771                 if (mVerboseLoggingEnabled) {
2772                     Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
2773                             + " BSSID=" + result.BSSID
2774                             + " RSSI=" + result.level
2775                             + " for " + config.getProfileKey());
2776                 }
2777             }
2778         }
2779     }
2780 
2781     /**
2782      * Save the ScanDetail to the ScanDetailCache of the given network.  This is used
2783      * by {@link PasspointNetworkNominator} for caching
2784      * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
2785      *
2786      * @param networkId The ID of the network to save ScanDetail to
2787      * @param scanDetail The ScanDetail to cache
2788      */
updateScanDetailForNetwork(int networkId, ScanDetail scanDetail)2789     public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
2790         WifiConfiguration network = getInternalConfiguredNetwork(networkId);
2791         if (network == null) {
2792             return;
2793         }
2794         saveToScanDetailCacheForNetwork(network, scanDetail);
2795     }
2796 
2797     /**
2798      * Helper method to check if the 2 provided networks can be linked or not.
2799      * Networks are considered for linking if:
2800      * 1. Share the same GW MAC address.
2801      * 2. Scan results for the networks have AP's with MAC address which differ only in the last
2802      * nibble.
2803      *
2804      * @param network1         WifiConfiguration corresponding to network 1.
2805      * @param network2         WifiConfiguration corresponding to network 2.
2806      * @param scanDetailCache1 ScanDetailCache entry for network 1.
2807      * @param scanDetailCache1 ScanDetailCache entry for network 2.
2808      * @return true if the networks should be linked, false if the networks should be unlinked.
2809      */
shouldNetworksBeLinked( WifiConfiguration network1, WifiConfiguration network2, ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2)2810     private boolean shouldNetworksBeLinked(
2811             WifiConfiguration network1, WifiConfiguration network2,
2812             ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
2813         // Check if networks should not be linked due to credential mismatch
2814         if (mContext.getResources().getBoolean(
2815                 R.bool.config_wifi_only_link_same_credential_configurations)) {
2816             if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
2817                 return false;
2818             }
2819         }
2820 
2821         // Skip VRRP MAC addresses since they are likely to correspond to different networks even if
2822         // they match.
2823         if ((network1.defaultGwMacAddress != null && network1.defaultGwMacAddress
2824                 .regionMatches(true, 0, VRRP_MAC_ADDRESS_PREFIX, 0,
2825                         VRRP_MAC_ADDRESS_PREFIX.length()))
2826                 || (network2.defaultGwMacAddress != null && network2.defaultGwMacAddress
2827                 .regionMatches(true, 0, VRRP_MAC_ADDRESS_PREFIX, 0,
2828                         VRRP_MAC_ADDRESS_PREFIX.length()))) {
2829             return false;
2830         }
2831 
2832         // Check if networks should be linked due to default gateway match
2833         if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
2834             // If both default GW are known, link only if they are equal
2835             if (network1.defaultGwMacAddress.equalsIgnoreCase(network2.defaultGwMacAddress)) {
2836                 if (mVerboseLoggingEnabled) {
2837                     Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
2838                             + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
2839                 }
2840                 return true;
2841             }
2842             return false;
2843         }
2844 
2845         // We do not know BOTH default gateways yet, but if the first 16 ASCII characters of BSSID
2846         // match then we can assume this is a DBDC with the same gateway. Once both gateways become
2847         // known, we will unlink the networks if it turns out the gateways are actually different.
2848         if (!mContext.getResources().getBoolean(
2849                 R.bool.config_wifiAllowLinkingUnknownDefaultGatewayConfigurations)) {
2850             return false;
2851         }
2852         if (scanDetailCache1 != null && scanDetailCache2 != null) {
2853             for (String abssid : scanDetailCache1.keySet()) {
2854                 for (String bbssid : scanDetailCache2.keySet()) {
2855                     if (abssid.regionMatches(
2856                             true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
2857                         if (mVerboseLoggingEnabled) {
2858                             Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
2859                                     + network2.SSID + " and " + network1.SSID
2860                                     + " bssida " + abssid + " bssidb " + bbssid);
2861                         }
2862                         return true;
2863                     }
2864                 }
2865             }
2866         }
2867         return false;
2868     }
2869 
2870     /**
2871      * Helper methods to link 2 networks together.
2872      *
2873      * @param network1 WifiConfiguration corresponding to network 1.
2874      * @param network2 WifiConfiguration corresponding to network 2.
2875      */
linkNetworks(WifiConfiguration network1, WifiConfiguration network2)2876     private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2877         if (mVerboseLoggingEnabled) {
2878             Log.v(TAG, "linkNetworks will link " + network2.getProfileKey()
2879                     + " and " + network1.getProfileKey());
2880         }
2881         if (network2.linkedConfigurations == null) {
2882             network2.linkedConfigurations = new HashMap<>();
2883         }
2884         if (network1.linkedConfigurations == null) {
2885             network1.linkedConfigurations = new HashMap<>();
2886         }
2887         // TODO (b/30638473): This needs to become a set instead of map, but it will need
2888         // public interface changes and need some migration of existing store data.
2889         network2.linkedConfigurations.put(network1.getProfileKey(), 1);
2890         network1.linkedConfigurations.put(network2.getProfileKey(), 1);
2891     }
2892 
2893     /**
2894      * Helper methods to unlink 2 networks from each other.
2895      *
2896      * @param network1 WifiConfiguration corresponding to network 1.
2897      * @param network2 WifiConfiguration corresponding to network 2.
2898      */
unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2)2899     private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2900         if (network2.linkedConfigurations != null
2901                 && (network2.linkedConfigurations.get(network1.getProfileKey()) != null)) {
2902             if (mVerboseLoggingEnabled) {
2903                 Log.v(TAG, "unlinkNetworks un-link " + network1.getProfileKey()
2904                         + " from " + network2.getProfileKey());
2905             }
2906             network2.linkedConfigurations.remove(network1.getProfileKey());
2907         }
2908         if (network1.linkedConfigurations != null
2909                 && (network1.linkedConfigurations.get(network2.getProfileKey()) != null)) {
2910             if (mVerboseLoggingEnabled) {
2911                 Log.v(TAG, "unlinkNetworks un-link " + network2.getProfileKey()
2912                         + " from " + network1.getProfileKey());
2913             }
2914             network1.linkedConfigurations.remove(network2.getProfileKey());
2915         }
2916     }
2917 
2918     /**
2919      * This method runs through all the saved networks and checks if the provided network can be
2920      * linked with any of them.
2921      *
2922      * @param config WifiConfiguration object corresponding to the network that needs to be
2923      *               checked for potential links.
2924      */
attemptNetworkLinking(WifiConfiguration config)2925     private void attemptNetworkLinking(WifiConfiguration config) {
2926         if (!WifiConfigurationUtil.isConfigLinkable(config)) return;
2927 
2928         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2929         // Ignore configurations with large number of BSSIDs.
2930         if (scanDetailCache != null
2931                 && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2932             return;
2933         }
2934         for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
2935             if (linkConfig.getProfileKey().equals(config.getProfileKey())) {
2936                 continue;
2937             }
2938             if (linkConfig.ephemeral) {
2939                 continue;
2940             }
2941             if (!linkConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
2942                 continue;
2943             }
2944             // Network Selector will be allowed to dynamically jump from a linked configuration
2945             // to another, hence only link configurations that have WPA_PSK/SAE security type
2946             // if auto upgrade enabled (OR) WPA_PSK if auto upgrade disabled.
2947             if (!WifiConfigurationUtil.isConfigLinkable(linkConfig)) continue;
2948             ScanDetailCache linkScanDetailCache =
2949                     getScanDetailCacheForNetwork(linkConfig.networkId);
2950             // Ignore configurations with large number of BSSIDs.
2951             if (linkScanDetailCache != null
2952                     && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2953                 continue;
2954             }
2955             // Check if the networks should be linked/unlinked.
2956             if (shouldNetworksBeLinked(
2957                     config, linkConfig, scanDetailCache, linkScanDetailCache)) {
2958                 linkNetworks(config, linkConfig);
2959             } else {
2960                 unlinkNetworks(config, linkConfig);
2961             }
2962         }
2963     }
2964 
2965     /**
2966      * Retrieves a list of all the saved hidden networks for scans
2967      *
2968      * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2969      * networks, this list will be truncated and we might end up not sending the networks
2970      * with the highest chance of connecting to the firmware.
2971      * So, re-sort the network list based on the frequency of connection to those networks
2972      * and whether it was last seen in the scan results.
2973      *
2974      * @param autoJoinOnly retrieve hidden network autojoin enabled only.
2975      * @return list of hidden networks in the order of priority.
2976      */
retrieveHiddenNetworkList( boolean autoJoinOnly)2977     public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList(
2978             boolean autoJoinOnly) {
2979         List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2980         List<WifiConfiguration> networks = getConfiguredNetworks();
2981         // Remove any non hidden networks.
2982         networks.removeIf(config -> !config.hiddenSSID);
2983         networks.sort(mScanListComparator);
2984         // The most frequently connected network has the highest priority now.
2985         Set<WifiSsid> ssidSet = new LinkedHashSet<>();
2986         for (WifiConfiguration config : networks) {
2987             if (autoJoinOnly && !config.allowAutojoin) {
2988                 continue;
2989             }
2990             ssidSet.addAll(mWifiInjector.getSsidTranslator()
2991                     .getAllPossibleOriginalSsids(WifiSsid.fromString(config.SSID)));
2992         }
2993         for (WifiSsid ssid : ssidSet) {
2994             hiddenList.add(new WifiScanner.ScanSettings.HiddenNetwork(ssid.toString()));
2995         }
2996         return hiddenList;
2997     }
2998 
2999     /**
3000      * Check if the provided network was temporarily disabled by the user and still blocked.
3001      *
3002      * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru
3003      *                this API matched the WifiConfiguration.SSID rules, and thus be surrounded by
3004      *                quotes.
3005      * @return true if network is blocking, otherwise false.
3006      */
isNetworkTemporarilyDisabledByUser(String network)3007     public boolean isNetworkTemporarilyDisabledByUser(String network) {
3008         if (mUserTemporarilyDisabledList.isLocked(network)) {
3009             return true;
3010         }
3011         mUserTemporarilyDisabledList.remove(network);
3012         return false;
3013     }
3014 
3015     /**
3016      * Check if the provided network should be disabled because it's a non-carrier-merged network.
3017      * @param config WifiConfiguration
3018      * @return true if the network is a non-carrier-merged network and it should be disabled,
3019      * otherwise false.
3020      */
isNonCarrierMergedNetworkTemporarilyDisabled( @onNull WifiConfiguration config)3021     public boolean isNonCarrierMergedNetworkTemporarilyDisabled(
3022             @NonNull WifiConfiguration config) {
3023         return mNonCarrierMergedNetworksStatusTracker.isNetworkDisabled(config);
3024     }
3025 
3026     /**
3027      * User temporarily disable a network and will be block to auto-join when network is still
3028      * nearby.
3029      *
3030      * The network will be re-enabled when:
3031      * a) User select to connect the network.
3032      * b) The network is not in range for {@link #USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS}
3033      * c) The maximum disable duration configured by
3034      * config_wifiAllNonCarrierMergedWifiMaxDisableDurationMinutes has passed.
3035      * d) Toggle wifi off, reset network settings or device reboot.
3036      *
3037      * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru
3038      *                this API matched the WifiConfiguration.SSID rules, and thus be surrounded by
3039      *                quotes.
3040      *        uid     UID of the calling process.
3041      */
userTemporarilyDisabledNetwork(String network, int uid)3042     public void userTemporarilyDisabledNetwork(String network, int uid) {
3043         int maxDisableDurationMinutes = mContext.getResources().getInteger(R.integer
3044                 .config_wifiAllNonCarrierMergedWifiMaxDisableDurationMinutes);
3045         mUserTemporarilyDisabledList.add(network, USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS,
3046                 maxDisableDurationMinutes * 60 * 1000);
3047         Log.d(TAG, "Temporarily disable network: " + network + " uid=" + uid + " num="
3048                 + mUserTemporarilyDisabledList.size() + ", maxDisableDurationMinutes:"
3049                 + maxDisableDurationMinutes);
3050         removeUserChoiceFromDisabledNetwork(network, uid);
3051         saveToStore(false);
3052     }
3053 
3054     /**
3055      * Temporarily disable visible and configured networks except for carrier merged networks for
3056      * the given subscriptionId.
3057      * @param subscriptionId
3058      */
startRestrictingAutoJoinToSubscriptionId(int subscriptionId)3059     public void startRestrictingAutoJoinToSubscriptionId(int subscriptionId) {
3060         int minDisableDurationMinutes = mContext.getResources().getInteger(R.integer
3061                 .config_wifiAllNonCarrierMergedWifiMinDisableDurationMinutes);
3062         int maxDisableDurationMinutes = mContext.getResources().getInteger(R.integer
3063                 .config_wifiAllNonCarrierMergedWifiMaxDisableDurationMinutes);
3064         localLog("startRestrictingAutoJoinToSubscriptionId: " + subscriptionId
3065                 + " minDisableDurationMinutes:" + minDisableDurationMinutes
3066                 + " maxDisableDurationMinutes:" + maxDisableDurationMinutes);
3067         long maxDisableDurationMs = maxDisableDurationMinutes * 60 * 1000;
3068         // do a clear to make sure we start at a clean state.
3069         mNonCarrierMergedNetworksStatusTracker.clear();
3070         mNonCarrierMergedNetworksStatusTracker.disableAllNonCarrierMergedNetworks(subscriptionId,
3071                 minDisableDurationMinutes * 60 * 1000,
3072                 maxDisableDurationMs);
3073         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3074             ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
3075             if (scanDetailCache == null) {
3076                 continue;
3077             }
3078             ScanResult scanResult = scanDetailCache.getMostRecentScanResult();
3079             if (scanResult == null) {
3080                 continue;
3081             }
3082             if (mClock.getWallClockMillis() - scanResult.seen
3083                     < NON_CARRIER_MERGED_NETWORKS_SCAN_CACHE_QUERY_DURATION_MS) {
3084                 // do not disable if this is a carrier-merged-network with the given subscriptionId
3085                 if (config.carrierMerged && config.subscriptionId == subscriptionId) {
3086                     continue;
3087                 }
3088                 mNonCarrierMergedNetworksStatusTracker.temporarilyDisableNetwork(config,
3089                         USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS, maxDisableDurationMs);
3090             }
3091         }
3092     }
3093 
3094     /**
3095      * Resets the effects of startTemporarilyDisablngAllNonCarrierMergedWifi.
3096      */
stopRestrictingAutoJoinToSubscriptionId()3097     public void stopRestrictingAutoJoinToSubscriptionId() {
3098         mNonCarrierMergedNetworksStatusTracker.clear();
3099     }
3100 
3101     /**
3102      * Update the user temporarily disabled network list with networks in range.
3103      * @param networks networks in range in String format, FQDN or SSID. And caller must ensure
3104      *                 that the SSID passed thru this API matched the WifiConfiguration.SSID rules,
3105      *                 and thus be surrounded by quotes.
3106      */
updateUserDisabledList(List<String> networks)3107     public void updateUserDisabledList(List<String> networks) {
3108         mUserTemporarilyDisabledList.update(new HashSet<>(networks));
3109         mNonCarrierMergedNetworksStatusTracker.update(new HashSet<>(networks));
3110     }
3111 
removeUserChoiceFromDisabledNetwork( @onNull String network, int uid)3112     private void removeUserChoiceFromDisabledNetwork(
3113             @NonNull String network, int uid) {
3114         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3115             if (TextUtils.equals(config.SSID, network) || TextUtils.equals(config.FQDN, network)) {
3116                 if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
3117                     mWifiMetrics.logUserActionEvent(
3118                             UserActionEvent.EVENT_DISCONNECT_WIFI, config.networkId);
3119                 }
3120                 removeConnectChoiceFromAllNetworks(config.getProfileKey());
3121             }
3122         }
3123     }
3124 
3125     /**
3126      * User enabled network manually, maybe trigger by user select to connect network.
3127      * @param networkId enabled network id.
3128      * @return true if the operation succeeded, false otherwise.
3129      */
userEnabledNetwork(int networkId)3130     public boolean userEnabledNetwork(int networkId) {
3131         WifiConfiguration configuration = getInternalConfiguredNetwork(networkId);
3132         if (configuration == null) {
3133             return false;
3134         }
3135         final String network;
3136         if (configuration.isPasspoint()) {
3137             network = configuration.FQDN;
3138         } else {
3139             network = configuration.SSID;
3140         }
3141         mUserTemporarilyDisabledList.remove(network);
3142         mWifiBlocklistMonitor.clearBssidBlocklistForSsid(configuration.SSID);
3143         Log.d(TAG, "Enable disabled network: " + network + " num="
3144                 + mUserTemporarilyDisabledList.size());
3145         return true;
3146     }
3147 
3148     /**
3149      * Clear all user temporarily disabled networks.
3150      */
clearUserTemporarilyDisabledList()3151     public void clearUserTemporarilyDisabledList() {
3152         mUserTemporarilyDisabledList.clear();
3153     }
3154 
3155     /**
3156      * Resets all sim networks state.
3157      */
resetSimNetworks()3158     public void resetSimNetworks() {
3159         if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
3160         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3161             if (config.enterpriseConfig == null
3162                     || !config.enterpriseConfig.isAuthenticationSimBased()) {
3163                 continue;
3164             }
3165             if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) {
3166                 Pair<String, String> currentIdentity =
3167                         mWifiCarrierInfoManager.getSimIdentity(config);
3168                 if (mVerboseLoggingEnabled) {
3169                     Log.d(TAG, "New identity for config " + config + ": " + currentIdentity);
3170                 }
3171                 // Update the loaded config
3172                 if (currentIdentity == null) {
3173                     Log.d(TAG, "Identity is null");
3174                 } else {
3175                     config.enterpriseConfig.setIdentity(currentIdentity.first);
3176                 }
3177                 // do not reset anonymous identity since it may be dependent on user-entry
3178                 // (i.e. cannot re-request on every reboot/SIM re-entry)
3179             } else {
3180                 // reset identity as well: supplicant will ask us for it
3181                 config.enterpriseConfig.setIdentity("");
3182                 if (!WifiCarrierInfoManager.isAnonymousAtRealmIdentity(
3183                         config.enterpriseConfig.getAnonymousIdentity())) {
3184                     config.enterpriseConfig.setAnonymousIdentity("");
3185                 }
3186             }
3187         }
3188     }
3189 
3190     /**
3191      * Clear all ephemeral carrier networks from the app without carrier privilege, which leads to
3192      * a disconnection.
3193      * Disconnection and removing networks installed by privileged apps is handled by will be
3194      * cleaned when privilege revokes.
3195      */
removeEphemeralCarrierNetworks(Set<String> carrierPrivilegedPackages)3196     public void removeEphemeralCarrierNetworks(Set<String> carrierPrivilegedPackages) {
3197         if (mVerboseLoggingEnabled) localLog("removeEphemeralCarrierNetwork");
3198         WifiConfiguration[] copiedConfigs =
3199                 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
3200         for (WifiConfiguration config : copiedConfigs) {
3201             if (!config.ephemeral
3202                     || config.subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
3203                 continue;
3204             }
3205             if (carrierPrivilegedPackages.contains(config.creatorName)) {
3206                 continue;
3207             }
3208             removeNetwork(config.networkId, config.creatorUid, config.creatorName);
3209         }
3210     }
3211 
3212     /**
3213      * Helper method to perform the following operations during user switch/unlock:
3214      * - Remove private networks of the old user.
3215      * - Load from the new user store file.
3216      * - Save the store files again to migrate any user specific networks from the shared store
3217      *   to user store.
3218      * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
3219      * should ensure that the stores are accessible before invocation.
3220      *
3221      * @param userId The identifier of the new foreground user, after the unlock or switch.
3222      */
handleUserUnlockOrSwitch(int userId)3223     private void handleUserUnlockOrSwitch(int userId) {
3224         if (mVerboseLoggingEnabled) {
3225             Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
3226         }
3227         // Switch out the user store file.
3228         if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
3229             saveToStore(true);
3230             mPendingUnlockStoreRead = false;
3231         }
3232     }
3233 
3234     /**
3235      * Handles the switch to a different foreground user:
3236      * - Flush the current state to the old user's store file.
3237      * - Switch the user specific store file.
3238      * - Reload the networks from the store files (shared & user).
3239      * - Write the store files to move any user specific private networks from shared store to user
3240      *   store.
3241      *
3242      * Need to be called when {@link com.android.server.SystemService#onUserSwitching} is invoked.
3243      *
3244      * @param userId The identifier of the new foreground user, after the switch.
3245      * @return List of network ID's of all the private networks of the old user which will be
3246      * removed from memory.
3247      */
handleUserSwitch(int userId)3248     public Set<Integer> handleUserSwitch(int userId) {
3249         if (mVerboseLoggingEnabled) {
3250             Log.v(TAG, "Handling user switch for " + userId);
3251         }
3252         if (userId == mCurrentUserId) {
3253             Log.w(TAG, "User already in foreground " + userId);
3254             return new HashSet<>();
3255         }
3256         if (mPendingStoreRead) {
3257             Log.w(TAG, "User switch before store is read!");
3258             mConfiguredNetworks.setNewUser(userId);
3259             mCurrentUserId = userId;
3260             // Reset any state from previous user unlock.
3261             mDeferredUserUnlockRead = false;
3262             // Cannot read data from new user's CE store file before they log-in.
3263             mPendingUnlockStoreRead = true;
3264             return new HashSet<>();
3265         }
3266         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
3267             saveToStore(true);
3268         }
3269         // Remove any private networks of the old user before switching the userId.
3270         Set<Integer> removedNetworkIds = clearInternalDataForUser(mCurrentUserId);
3271         mConfiguredNetworks.setNewUser(userId);
3272         mCurrentUserId = userId;
3273 
3274         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
3275             handleUserUnlockOrSwitch(mCurrentUserId);
3276             // only handle the switching of unlocked users in {@link WifiCarrierInfoManager}.
3277             mWifiCarrierInfoManager.onUnlockedUserSwitching(mCurrentUserId);
3278         } else {
3279             // Cannot read data from new user's CE store file before they log-in.
3280             mPendingUnlockStoreRead = true;
3281             Log.i(TAG, "Waiting for user unlock to load from store");
3282         }
3283         return removedNetworkIds;
3284     }
3285 
3286     /**
3287      * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
3288      * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
3289      *
3290      * Need to be called when {@link com.android.server.SystemService#onUserUnlocking} is invoked.
3291      *
3292      * @param userId The identifier of the user that unlocked.
3293      */
handleUserUnlock(int userId)3294     public void handleUserUnlock(int userId) {
3295         if (mVerboseLoggingEnabled) {
3296             Log.v(TAG, "Handling user unlock for " + userId);
3297         }
3298         if (userId != mCurrentUserId) {
3299             Log.e(TAG, "Ignore user unlock for non current user " + userId);
3300             return;
3301         }
3302         if (mPendingStoreRead) {
3303             Log.w(TAG, "Ignore user unlock until store is read!");
3304             mDeferredUserUnlockRead = true;
3305             return;
3306         }
3307         if (mPendingUnlockStoreRead) {
3308             handleUserUnlockOrSwitch(mCurrentUserId);
3309         }
3310     }
3311 
3312     /**
3313      * Handles the stop of foreground user. This is needed to write the store file to flush
3314      * out any pending data before the user's CE store storage is unavailable.
3315      *
3316      * Need to be called when {@link com.android.server.SystemService#onUserStopping} is invoked.
3317      *
3318      * @param userId The identifier of the user that stopped.
3319      */
handleUserStop(int userId)3320     public void handleUserStop(int userId) {
3321         if (mVerboseLoggingEnabled) {
3322             Log.v(TAG, "Handling user stop for " + userId);
3323         }
3324         if (userId == mCurrentUserId
3325                 && mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
3326             saveToStore(true);
3327             clearInternalDataForUser(mCurrentUserId);
3328         }
3329     }
3330 
3331     /**
3332      * Helper method to clear internal databases.
3333      * This method clears the:
3334      *  - List of configured networks.
3335      *  - Map of scan detail caches.
3336      *  - List of deleted ephemeral networks.
3337      */
clearInternalData()3338     private void clearInternalData() {
3339         localLog("clearInternalData: Clearing all internal data");
3340         mConfiguredNetworks.clear();
3341         mUserTemporarilyDisabledList.clear();
3342         mNonCarrierMergedNetworksStatusTracker.clear();
3343         mRandomizedMacAddressMapping.clear();
3344         mScanDetailCaches.clear();
3345         clearLastSelectedNetwork();
3346     }
3347 
3348     /**
3349      * Helper method to clear internal databases of the specified user.
3350      * This method clears the:
3351      *  - Private configured configured networks of the specified user.
3352      *  - Map of scan detail caches.
3353      *  - List of deleted ephemeral networks.
3354      *
3355      * @return List of network ID's of all the private networks of the old user which will be
3356      * removed from memory.
3357      */
clearInternalDataForUser(int user)3358     private Set<Integer> clearInternalDataForUser(int user) {
3359         localLog("clearInternalUserData: Clearing user internal data for " + user);
3360         Set<Integer> removedNetworkIds = new HashSet<>();
3361         // Remove any private networks of the old user before switching the userId.
3362         for (WifiConfiguration config : getConfiguredNetworks()) {
3363             if ((!config.shared
3364                     && mWifiPermissionsUtil.doesUidBelongToUser(config.creatorUid, user))
3365                     || config.ephemeral) {
3366                 removedNetworkIds.add(config.networkId);
3367                 localLog("clearInternalUserData: removed config."
3368                         + " netId=" + config.networkId
3369                         + " configKey=" + config.getProfileKey());
3370                 mConfiguredNetworks.remove(config.networkId);
3371                 for (OnNetworkUpdateListener listener : mListeners) {
3372                     listener.onNetworkRemoved(
3373                             createExternalWifiConfiguration(config, true, Process.WIFI_UID));
3374                 }
3375             }
3376         }
3377         if (!removedNetworkIds.isEmpty()) {
3378             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED, null);
3379         }
3380         mUserTemporarilyDisabledList.clear();
3381         mNonCarrierMergedNetworksStatusTracker.clear();
3382         mScanDetailCaches.clear();
3383         clearLastSelectedNetwork();
3384         return removedNetworkIds;
3385     }
3386 
3387     /**
3388      * Helper function to populate the internal (in-memory) data from the retrieved shared store
3389      * (file) data.
3390      *
3391      * @param configurations list of configurations retrieved from store.
3392      */
loadInternalDataFromSharedStore( List<WifiConfiguration> configurations, Map<String, String> macAddressMapping)3393     private void loadInternalDataFromSharedStore(
3394             List<WifiConfiguration> configurations,
3395             Map<String, String> macAddressMapping) {
3396 
3397         long supportedFeatures = mWifiInjector.getActiveModeWarden()
3398                 .getPrimaryClientModeManager().getSupportedFeatures();
3399 
3400         for (WifiConfiguration configuration : configurations) {
3401             if (!WifiConfigurationUtil.validate(
3402                     configuration, supportedFeatures, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
3403                 Log.e(TAG, "Skipping malformed network from shared store: " + configuration);
3404                 continue;
3405             }
3406 
3407             WifiConfiguration existingConfiguration = getInternalConfiguredNetwork(configuration);
3408             if (null != existingConfiguration) {
3409                 Log.d(TAG, "Merging network from shared store "
3410                         + configuration.getProfileKey());
3411                 mergeWithInternalWifiConfiguration(existingConfiguration, configuration);
3412                 continue;
3413             }
3414 
3415             configuration.networkId = mNextNetworkId++;
3416             if (mVerboseLoggingEnabled) {
3417                 Log.v(TAG, "Adding network from shared store "
3418                         + configuration.getProfileKey());
3419             }
3420             try {
3421                 mConfiguredNetworks.put(configuration);
3422             } catch (IllegalArgumentException e) {
3423                 Log.e(TAG, "Failed to add network to config map", e);
3424             }
3425         }
3426         mRandomizedMacAddressMapping.putAll(macAddressMapping);
3427     }
3428 
3429     /**
3430      * Helper function to populate the internal (in-memory) data from the retrieved user store
3431      * (file) data.
3432      *
3433      * @param configurations list of configurations retrieved from store.
3434      */
loadInternalDataFromUserStore(List<WifiConfiguration> configurations)3435     private void loadInternalDataFromUserStore(List<WifiConfiguration> configurations) {
3436         long supportedFeatures = mWifiInjector.getActiveModeWarden()
3437                 .getPrimaryClientModeManager().getSupportedFeatures();
3438 
3439         for (WifiConfiguration configuration : configurations) {
3440             if (!WifiConfigurationUtil.validate(
3441                     configuration, supportedFeatures, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
3442                 Log.e(TAG, "Skipping malformed network from user store: " + configuration);
3443                 continue;
3444             }
3445 
3446             WifiConfiguration existingConfiguration = getInternalConfiguredNetwork(configuration);
3447             if (null != existingConfiguration) {
3448                 Log.d(TAG, "Merging network from user store "
3449                         + configuration.getProfileKey());
3450                 mergeWithInternalWifiConfiguration(existingConfiguration, configuration);
3451                 continue;
3452             }
3453 
3454             configuration.networkId = mNextNetworkId++;
3455             if (mVerboseLoggingEnabled) {
3456                 Log.v(TAG, "Adding network from user store "
3457                         + configuration.getProfileKey());
3458             }
3459             try {
3460                 mConfiguredNetworks.put(configuration);
3461             } catch (IllegalArgumentException e) {
3462                 Log.e(TAG, "Failed to add network to config map", e);
3463             }
3464 
3465             if (configuration.isMostRecentlyConnected) {
3466                 mLruConnectionTracker.addNetwork(configuration);
3467             }
3468         }
3469     }
3470 
3471     /**
3472      * Initializes the randomized MAC address for an internal WifiConfiguration depending on
3473      * whether it should use non-persistent randomization.
3474      * @param config
3475      */
initRandomizedMacForInternalConfig(WifiConfiguration internalConfig)3476     private void initRandomizedMacForInternalConfig(WifiConfiguration internalConfig) {
3477         MacAddress randomizedMac = shouldUseNonPersistentRandomization(internalConfig)
3478                 ? MacAddressUtils.createRandomUnicastAddress()
3479                 : getPersistentMacAddress(internalConfig);
3480         if (randomizedMac != null) {
3481             setRandomizedMacAddress(internalConfig, randomizedMac);
3482         }
3483     }
3484 
3485     /**
3486      * Assign randomized MAC addresses for configured networks.
3487      * This is needed to generate persistent randomized MAC address for existing networks when
3488      * a device updates to Q+ for the first time since we are not calling addOrUpdateNetwork when
3489      * we load configuration at boot.
3490      */
generateRandomizedMacAddresses()3491     private void generateRandomizedMacAddresses() {
3492         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3493             if (DEFAULT_MAC_ADDRESS.equals(config.getRandomizedMacAddress())) {
3494                 initRandomizedMacForInternalConfig(config);
3495             }
3496         }
3497     }
3498 
3499     /**
3500      * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
3501      * data.
3502      * This method:
3503      * 1. Clears all existing internal data.
3504      * 2. Sends out the networks changed broadcast after loading all the data.
3505      *
3506      * @param sharedConfigurations list of network configurations retrieved from shared store.
3507      * @param userConfigurations list of network configurations retrieved from user store.
3508      * @param macAddressMapping
3509      */
loadInternalData( List<WifiConfiguration> sharedConfigurations, List<WifiConfiguration> userConfigurations, Map<String, String> macAddressMapping)3510     private void loadInternalData(
3511             List<WifiConfiguration> sharedConfigurations,
3512             List<WifiConfiguration> userConfigurations,
3513             Map<String, String> macAddressMapping) {
3514         // Clear out all the existing in-memory lists and load the lists from what was retrieved
3515         // from the config store.
3516         clearInternalData();
3517         loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping);
3518         loadInternalDataFromUserStore(userConfigurations);
3519         generateRandomizedMacAddresses();
3520         if (mConfiguredNetworks.sizeForAllUsers() == 0) {
3521             Log.w(TAG, "No stored networks found.");
3522         }
3523         // reset identity & anonymous identity for networks using SIM-based authentication
3524         // on load (i.e. boot) so that if the user changed SIMs while the device was powered off,
3525         // we do not reuse stale credentials that would lead to authentication failure.
3526         resetSimNetworks();
3527         sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_ADDED, null);
3528         mPendingStoreRead = false;
3529     }
3530 
3531     /**
3532      * Helper method to handle any config store errors on user builds vs other debuggable builds.
3533      */
handleConfigStoreFailure(boolean onlyUserStore)3534     private boolean handleConfigStoreFailure(boolean onlyUserStore) {
3535         // On eng/userdebug builds, return failure to leave the device in a debuggable state.
3536         if (!mBuildProperties.isUserBuild()) return false;
3537 
3538         // On user builds, ignore the failure and let the user create new networks.
3539         Log.w(TAG, "Ignoring config store errors on user build");
3540         if (!onlyUserStore) {
3541             loadInternalData(Collections.emptyList(), Collections.emptyList(),
3542                     Collections.emptyMap());
3543         } else {
3544             loadInternalDataFromUserStore(Collections.emptyList());
3545         }
3546         return true;
3547     }
3548 
3549     /**
3550      * Read the config store and load the in-memory lists from the store data retrieved and sends
3551      * out the networks changed broadcast.
3552      *
3553      * This reads all the network configurations from:
3554      * 1. Shared WifiConfigStore.xml
3555      * 2. User WifiConfigStore.xml
3556      *
3557      * @return true on success or not needed (fresh install), false otherwise.
3558      */
loadFromStore()3559     public boolean loadFromStore() {
3560         // If the user unlock comes in before we load from store, which means the user store have
3561         // not been setup yet for the current user. Setup the user store before the read so that
3562         // configurations for the current user will also being loaded.
3563         if (mDeferredUserUnlockRead) {
3564             Log.i(TAG, "Handling user unlock before loading from store.");
3565             List<WifiConfigStore.StoreFile> userStoreFiles =
3566                     WifiConfigStore.createUserFiles(
3567                             mCurrentUserId, mFrameworkFacade.isNiapModeOn(mContext));
3568             if (userStoreFiles == null) {
3569                 Log.wtf(TAG, "Failed to create user store files");
3570                 return false;
3571             }
3572             mWifiConfigStore.setUserStores(userStoreFiles);
3573             mDeferredUserUnlockRead = false;
3574         }
3575         try {
3576             mWifiConfigStore.read();
3577         } catch (IOException | IllegalStateException e) {
3578             Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
3579             return handleConfigStoreFailure(false);
3580         } catch (XmlPullParserException e) {
3581             Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
3582             return handleConfigStoreFailure(false);
3583         }
3584         loadInternalData(mNetworkListSharedStoreData.getConfigurations(),
3585                 mNetworkListUserStoreData.getConfigurations(),
3586                 mRandomizedMacStoreData.getMacMapping());
3587         return true;
3588     }
3589 
3590     /**
3591      * Read the user config store and load the in-memory lists from the store data retrieved and
3592      * sends out the networks changed broadcast.
3593      * This should be used for all user switches/unlocks to only load networks from the user
3594      * specific store and avoid reloading the shared networks.
3595      *
3596      * This reads all the network configurations from:
3597      * 1. User WifiConfigStore.xml
3598      *
3599      * @param userId The identifier of the foreground user.
3600      * @return true on success, false otherwise.
3601      */
loadFromUserStoreAfterUnlockOrSwitch(int userId)3602     private boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
3603         try {
3604             List<WifiConfigStore.StoreFile> userStoreFiles =
3605                     WifiConfigStore.createUserFiles(
3606                             userId, mFrameworkFacade.isNiapModeOn(mContext));
3607             if (userStoreFiles == null) {
3608                 Log.e(TAG, "Failed to create user store files");
3609                 return false;
3610             }
3611             mWifiConfigStore.switchUserStoresAndRead(userStoreFiles);
3612         } catch (IOException | IllegalStateException e) {
3613             Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
3614             return handleConfigStoreFailure(true);
3615         } catch (XmlPullParserException e) {
3616             Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are "
3617                     + "lost!", e);
3618             return handleConfigStoreFailure(true);
3619         }
3620         loadInternalDataFromUserStore(mNetworkListUserStoreData.getConfigurations());
3621         return true;
3622     }
3623 
3624     /**
3625      * Save the current snapshot of the in-memory lists to the config store.
3626      *
3627      * @param forceWrite Whether the write needs to be forced or not.
3628      * @return Whether the write was successful or not, this is applicable only for force writes.
3629      */
saveToStore(boolean forceWrite)3630     public synchronized boolean saveToStore(boolean forceWrite) {
3631         if (mPendingStoreRead) {
3632             Log.e(TAG, "Cannot save to store before store is read!");
3633             return false;
3634         }
3635         ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
3636         ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
3637         // List of network IDs for legacy Passpoint configuration to be removed.
3638         List<Integer> legacyPasspointNetId = new ArrayList<>();
3639         for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
3640             // Ignore ephemeral networks and non-legacy Passpoint configurations.
3641             if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
3642                 continue;
3643             }
3644 
3645             // Migrate the legacy Passpoint configurations owned by the current user to
3646             // {@link PasspointManager}.
3647             if (config.isLegacyPasspointConfig && mWifiPermissionsUtil
3648                     .doesUidBelongToUser(config.creatorUid, mCurrentUserId)) {
3649                 legacyPasspointNetId.add(config.networkId);
3650                 // Migrate the legacy Passpoint configuration and add it to PasspointManager.
3651                 if (!PasspointManager.addLegacyPasspointConfig(config)) {
3652                     Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
3653                 }
3654                 // This will prevent adding |config| to the |sharedConfigurations|.
3655                 continue;
3656             }
3657 
3658             config.isMostRecentlyConnected =
3659                     mLruConnectionTracker.isMostRecentlyConnected(config);
3660 
3661             // We push all shared networks & private networks not belonging to the current
3662             // user to the shared store. Ideally, private networks for other users should
3663             // not even be in memory,
3664             // But, this logic is in place to deal with store migration from N to O
3665             // because all networks were previously stored in a central file. We cannot
3666             // write these private networks to the user specific store until the corresponding
3667             // user logs in.
3668             if (config.shared || !mWifiPermissionsUtil
3669                     .doesUidBelongToUser(config.creatorUid, mCurrentUserId)) {
3670                 sharedConfigurations.add(config);
3671             } else {
3672                 userConfigurations.add(config);
3673             }
3674         }
3675 
3676         // Remove the configurations for migrated Passpoint configurations.
3677         for (int networkId : legacyPasspointNetId) {
3678             mConfiguredNetworks.remove(networkId);
3679         }
3680 
3681         // Setup store data for write.
3682         mNetworkListSharedStoreData.setConfigurations(sharedConfigurations);
3683         mNetworkListUserStoreData.setConfigurations(userConfigurations);
3684         mRandomizedMacStoreData.setMacMapping(mRandomizedMacAddressMapping);
3685 
3686         try {
3687             long start = mClock.getElapsedSinceBootMillis();
3688             mWifiConfigStore.write(forceWrite);
3689             mWifiMetrics.wifiConfigStored((int) (mClock.getElapsedSinceBootMillis() - start));
3690         } catch (IOException | IllegalStateException e) {
3691             Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
3692             return false;
3693         } catch (XmlPullParserException e) {
3694             Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
3695             return false;
3696         }
3697         return true;
3698     }
3699 
3700     /**
3701      * Helper method for logging into local log buffer.
3702      */
localLog(String s)3703     private void localLog(String s) {
3704         if (mLocalLog != null) {
3705             mLocalLog.log(s);
3706         }
3707     }
3708 
3709     /**
3710      * Dump the local log buffer and other internal state of WifiConfigManager.
3711      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)3712     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3713         pw.println("Dump of WifiConfigManager");
3714         pw.println("WifiConfigManager - Log Begin ----");
3715         mLocalLog.dump(fd, pw, args);
3716         pw.println("WifiConfigManager - Log End ----");
3717         pw.println("WifiConfigManager - Configured networks Begin ----");
3718         for (WifiConfiguration network : getInternalConfiguredNetworks()) {
3719             pw.println(network);
3720         }
3721         pw.println("WifiConfigManager - Configured networks End ----");
3722         pw.println("WifiConfigManager - ConfigurationMap Begin ----");
3723         mConfiguredNetworks.dump(fd, pw, args);
3724         pw.println("WifiConfigManager - ConfigurationMap End ----");
3725         pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
3726         pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
3727         pw.println("WifiConfigManager - PNO scan frequency culling enabled = "
3728                 + mContext.getResources().getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled));
3729         pw.println("WifiConfigManager - PNO scan recency sorting enabled = "
3730                 + mContext.getResources().getBoolean(R.bool.config_wifiPnoRecencySortingEnabled));
3731         mWifiConfigStore.dump(fd, pw, args);
3732         mWifiCarrierInfoManager.dump(fd, pw, args);
3733         mNonCarrierMergedNetworksStatusTracker.dump(fd, pw, args);
3734     }
3735 
3736     /**
3737      * Returns true if the given uid has permission to add, update or remove proxy settings
3738      */
canModifyProxySettings(int uid, String packageName)3739     private boolean canModifyProxySettings(int uid, String packageName) {
3740         final boolean isAdmin = mWifiPermissionsUtil.isAdmin(uid, packageName);
3741         final boolean hasNetworkSettingsPermission =
3742                 mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
3743         final boolean hasNetworkSetupWizardPermission =
3744                 mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid);
3745         final boolean hasNetworkManagedProvisioningPermission =
3746                 mWifiPermissionsUtil.checkNetworkManagedProvisioningPermission(uid);
3747         // If |uid| corresponds to the admin, allow all modifications.
3748         if (isAdmin || hasNetworkSettingsPermission
3749                 || hasNetworkSetupWizardPermission || hasNetworkManagedProvisioningPermission
3750                 || mWifiPermissionsUtil.checkConfigOverridePermission(uid)) {
3751             return true;
3752         }
3753         if (mVerboseLoggingEnabled) {
3754             Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
3755                     + " hasNetworkSettings=" + hasNetworkSettingsPermission
3756                     + " hasNetworkSetupWizard=" + hasNetworkSetupWizardPermission
3757                     + " Admin=" + isAdmin);
3758         }
3759         return false;
3760     }
3761 
3762     /**
3763      * Add the network update event listener
3764      */
addOnNetworkUpdateListener(@onNull OnNetworkUpdateListener listener)3765     public void addOnNetworkUpdateListener(@NonNull OnNetworkUpdateListener listener) {
3766         if (listener == null) {
3767             Log.wtf(TAG, "addOnNetworkUpdateListener: listener must not be null");
3768             return;
3769         }
3770         mListeners.add(listener);
3771     }
3772 
3773     /**
3774      * Remove the network update event listener
3775      */
removeOnNetworkUpdateListener(@onNull OnNetworkUpdateListener listener)3776     public void removeOnNetworkUpdateListener(@NonNull OnNetworkUpdateListener listener) {
3777         if (listener == null) {
3778             Log.wtf(TAG, "removeOnNetworkUpdateListener: listener must not be null");
3779             return;
3780         }
3781         mListeners.remove(listener);
3782     }
3783 
3784     /**
3785      * Set extra failure reason for given config. Used to surface extra failure details to the UI
3786      * @param netId The network ID of the config to set the extra failure reason for
3787      * @param reason the WifiConfiguration.ExtraFailureReason failure code representing the most
3788      *               recent failure reason
3789      */
setRecentFailureAssociationStatus(int netId, int reason)3790     public void setRecentFailureAssociationStatus(int netId, int reason) {
3791         WifiConfiguration config = getInternalConfiguredNetwork(netId);
3792         if (config == null) {
3793             return;
3794         }
3795         mWifiMetrics.incrementRecentFailureAssociationStatusCount(reason);
3796         int previousReason = config.recentFailure.getAssociationStatus();
3797         config.recentFailure.setAssociationStatus(reason, mClock.getElapsedSinceBootMillis());
3798         if (previousReason != reason) {
3799             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE, config);
3800         }
3801     }
3802 
3803     /**
3804      * @param netId The network ID of the config to clear the extra failure reason from
3805      */
clearRecentFailureReason(int netId)3806     public void clearRecentFailureReason(int netId) {
3807         WifiConfiguration config = getInternalConfiguredNetwork(netId);
3808         if (config == null) {
3809             return;
3810         }
3811         config.recentFailure.clear();
3812     }
3813 
3814     /**
3815      * Clear all recent failure reasons that have timed out.
3816      */
cleanupExpiredRecentFailureReasons()3817     public void cleanupExpiredRecentFailureReasons() {
3818         long timeoutDuration = mContext.getResources().getInteger(
3819                 R.integer.config_wifiRecentFailureReasonExpirationMinutes) * 60 * 1000;
3820         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3821             if (config.recentFailure.getAssociationStatus()
3822                     != WifiConfiguration.RECENT_FAILURE_NONE
3823                     && mClock.getElapsedSinceBootMillis()
3824                     >= config.recentFailure.getLastUpdateTimeSinceBootMillis() + timeoutDuration) {
3825                 config.recentFailure.clear();
3826                 sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE,
3827                         config);
3828             }
3829         }
3830     }
3831 
3832     /**
3833      * Find the highest RSSI among all valid scanDetails in current network's scanDetail cache.
3834      * If scanDetail is too old, it is not considered to be valid.
3835      * @param netId The network ID of the config to find scan RSSI
3836      * @params scanRssiValidTimeMs The valid time for scan RSSI
3837      * @return The highest RSSI in dBm found with current network's scanDetail cache.
3838      */
findScanRssi(int netId, int scanRssiValidTimeMs)3839     public int findScanRssi(int netId, int scanRssiValidTimeMs) {
3840         int scanMaxRssi = WifiInfo.INVALID_RSSI;
3841         ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(netId);
3842         if (scanDetailCache == null || scanDetailCache.size() == 0) return scanMaxRssi;
3843         long nowInMillis = mClock.getWallClockMillis();
3844         for (ScanDetail scanDetail : scanDetailCache.values()) {
3845             ScanResult result = scanDetail.getScanResult();
3846             if (result == null) continue;
3847             boolean valid = (nowInMillis - result.seen) < scanRssiValidTimeMs;
3848 
3849             if (valid) {
3850                 scanMaxRssi = Math.max(scanMaxRssi, result.level);
3851             }
3852         }
3853         return scanMaxRssi;
3854     }
3855 
3856     public Comparator<WifiConfiguration> getScanListComparator() {
3857         return mScanListComparator;
3858     }
3859 
3860     /**
3861      * This API is called when a connection successfully completes on an existing network
3862      * selected by the user. It is not called after the first connection of a newly added network.
3863      * Following actions will be triggered:
3864      * 1. If this network is disabled, we need re-enable it again.
3865      * 2. This network is favored over all the other networks visible in latest network
3866      * selection procedure.
3867      *
3868      * @param netId ID for the network chosen by the user
3869      * @param rssi the signal strength of the user selected network
3870      * @return true -- There is change made to connection choice of any saved network.
3871      * false -- There is no change made to connection choice of any saved network.
3872      */
3873     private boolean setUserConnectChoice(int netId, int rssi) {
3874         localLog("userSelectNetwork: network ID=" + netId);
3875         WifiConfiguration selected = getInternalConfiguredNetwork(netId);
3876 
3877         if (selected == null || selected.getProfileKey() == null) {
3878             localLog("userSelectNetwork: Invalid configuration with nid=" + netId);
3879             return false;
3880         }
3881 
3882         // Enable the network if it is disabled.
3883         if (!selected.getNetworkSelectionStatus().isNetworkEnabled()) {
3884             updateNetworkSelectionStatus(selected,
3885                     WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE);
3886         }
3887         boolean changed = setLegacyUserConnectChoice(selected, rssi);
3888         return changed;
3889     }
3890 
3891     /**
3892      * This maintains the legacy user connect choice state in the config store
3893      */
3894     public boolean setLegacyUserConnectChoice(@NonNull final WifiConfiguration selected,
3895             int rssi) {
3896         boolean change = false;
3897         Collection<WifiConfiguration> configuredNetworks = getInternalConfiguredNetworks();
3898         ArrayList<WifiConfiguration> networksInRange = new ArrayList<>();
3899         String key = selected.getProfileKey();
3900         for (WifiConfiguration network : configuredNetworks) {
3901             WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
3902             if (network.networkId == selected.networkId) {
3903                 if (status.getConnectChoice() != null) {
3904                     localLog("Remove user selection preference of " + status.getConnectChoice()
3905                             + " from " + network.SSID + " : " + network.networkId);
3906                     clearConnectChoiceInternal(network);
3907                     change = true;
3908                 }
3909                 continue;
3910             }
3911 
3912             if (status.getSeenInLastQualifiedNetworkSelection()) {
3913                 setConnectChoiceInternal(network, key, rssi);
3914                 change = true;
3915                 networksInRange.add(network);
3916             }
3917         }
3918 
3919         for (OnNetworkUpdateListener listener : mListeners) {
3920             listener.onConnectChoiceSet(networksInRange, key, rssi);
3921         }
3922         return change;
3923     }
3924 
3925     private void clearConnectChoiceInternal(WifiConfiguration config) {
3926         config.getNetworkSelectionStatus().setConnectChoice(null);
3927         config.getNetworkSelectionStatus().setConnectChoiceRssi(0);
3928     }
3929 
3930     private void setConnectChoiceInternal(WifiConfiguration config, String key, int rssi) {
3931         config.getNetworkSelectionStatus().setConnectChoice(key);
3932         config.getNetworkSelectionStatus().setConnectChoiceRssi(rssi);
3933         localLog("Add connect choice key: " + key + " rssi: " + rssi + " to "
3934                 + WifiNetworkSelector.toNetworkString(config));
3935     }
3936 
3937     /** Update WifiConfigManager before connecting to a network. */
3938     public void updateBeforeConnect(int networkId, int callingUid, @NonNull String packageName) {
3939         userEnabledNetwork(networkId);
3940         if (!enableNetwork(networkId, true, callingUid, null)
3941                 || !updateLastConnectUid(networkId, callingUid)) {
3942             Log.i(TAG, "connect Allowing uid " + callingUid + " packageName " + packageName
3943                     + " with insufficient permissions to connect=" + networkId);
3944         }
3945     }
3946 
3947     /** See {@link WifiManager#save(WifiConfiguration, WifiManager.ActionListener)} */
3948     public NetworkUpdateResult updateBeforeSaveNetwork(WifiConfiguration config, int callingUid,
3949             @NonNull String packageName) {
3950         NetworkUpdateResult result = addOrUpdateNetwork(config, callingUid);
3951         if (!result.isSuccess()) {
3952             Log.e(TAG, "saveNetwork adding/updating config=" + config + " failed");
3953             return result;
3954         }
3955         if (!enableNetwork(result.getNetworkId(), false, callingUid, null)) {
3956             Log.e(TAG, "saveNetwork enabling config=" + config + " failed");
3957             return NetworkUpdateResult.makeFailed();
3958         }
3959         return result;
3960     }
3961 
3962     /**
3963      * Gets the most recent scan result that is newer than maxAgeMillis for each configured network.
3964      * @param maxAgeMillis scan results older than this parameter will get filtered out.
3965      */
3966     public @NonNull List<ScanResult> getMostRecentScanResultsForConfiguredNetworks(
3967             int maxAgeMillis) {
3968         List<ScanResult> results = new ArrayList<>();
3969         long timeNowMs = mClock.getWallClockMillis();
3970         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
3971             ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
3972             if (scanDetailCache == null) {
3973                 continue;
3974             }
3975             ScanResult scanResult = scanDetailCache.getMostRecentScanResult();
3976             if (scanResult == null) {
3977                 continue;
3978             }
3979             if (timeNowMs - scanResult.seen < maxAgeMillis) {
3980                 results.add(scanResult);
3981             }
3982         }
3983         return results;
3984     }
3985 
3986     /**
3987      * Update the configuration according to transition disable indications.
3988      *
3989      * @param networkId network ID corresponding to the network.
3990      * @param indicationBit transition disable indication bits.
3991      * @return true if the network was found, false otherwise.
3992      */
3993     public boolean updateNetworkTransitionDisable(int networkId,
3994             @WifiMonitor.TransitionDisableIndication int indicationBit) {
3995         localLog("updateNetworkTransitionDisable: network ID=" + networkId
3996                 + " indication: " + indicationBit);
3997         WifiConfiguration config = getInternalConfiguredNetwork(networkId);
3998         if (config == null) {
3999             Log.e(TAG, "Cannot find network for " + networkId);
4000             return false;
4001         }
4002         WifiConfiguration copy = new WifiConfiguration(config);
4003         boolean changed = false;
4004         if (0 != (indicationBit & WifiMonitor.TDI_USE_WPA3_PERSONAL)
4005                 && config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) {
4006             config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_PSK, false);
4007             changed = true;
4008         }
4009         if (0 != (indicationBit & WifiMonitor.TDI_USE_SAE_PK)) {
4010             config.enableSaePkOnlyMode(true);
4011             changed = true;
4012         }
4013         if (0 != (indicationBit & WifiMonitor.TDI_USE_WPA3_ENTERPRISE)
4014                 && config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) {
4015             config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_EAP, false);
4016             changed = true;
4017         }
4018         if (0 != (indicationBit & WifiMonitor.TDI_USE_ENHANCED_OPEN)
4019                 && config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) {
4020             config.setSecurityParamsEnabled(WifiConfiguration.SECURITY_TYPE_OPEN, false);
4021             changed = true;
4022         }
4023         if (changed) {
4024             for (OnNetworkUpdateListener listener : mListeners) {
4025                 listener.onSecurityParamsUpdate(copy, config.getSecurityParamsList());
4026             }
4027         }
4028 
4029         return true;
4030     }
4031 
4032     /**
4033      * Retrieves the configured network corresponding to the provided configKey
4034      * without any masking.
4035      *
4036      * WARNING: Don't use this to pass network configurations except in the wifi stack, when
4037      * there is a need for passwords and randomized MAC address.
4038      *
4039      * @param configKey configKey of the requested network.
4040      * @return Copy of WifiConfiguration object if found, null otherwise.
4041      */
4042     private WifiConfiguration getConfiguredNetworkWithoutMasking(String configKey) {
4043         WifiConfiguration config = getInternalConfiguredNetwork(configKey);
4044         if (config == null) {
4045             return null;
4046         }
4047         return new WifiConfiguration(config);
4048     }
4049 
4050     /**
4051      * This method links the config of the provided network id to every linkable saved network.
4052      *
4053      * @param networkId networkId corresponding to the network to be potentially linked.
4054      */
4055     public void updateLinkedNetworks(int networkId) {
4056         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4057         if (internalConfig == null) {
4058             return;
4059         }
4060         internalConfig.linkedConfigurations = new HashMap<>();
4061         attemptNetworkLinking(internalConfig);
4062     }
4063 
4064     /**
4065      * This method returns a map containing each config key and unmasked WifiConfiguration of every
4066      * network linked to the provided network id.
4067      * @param networkId networkId to get the linked configs of.
4068      * @return HashMap of config key to unmasked WifiConfiguration
4069      */
4070     public Map<String, WifiConfiguration> getLinkedNetworksWithoutMasking(int networkId) {
4071         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4072         if (internalConfig == null) {
4073             return null;
4074         }
4075 
4076         Map<String, WifiConfiguration> linkedNetworks = new HashMap<>();
4077         Map<String, Integer> linkedConfigurations = internalConfig.linkedConfigurations;
4078         if (linkedConfigurations == null) {
4079             return null;
4080         }
4081         for (String configKey : linkedConfigurations.keySet()) {
4082             WifiConfiguration linkConfig = getConfiguredNetworkWithoutMasking(configKey);
4083             if (linkConfig == null) continue;
4084 
4085             if (!WifiConfigurationUtil.isConfigLinkable(linkConfig)) continue;
4086 
4087             SecurityParams defaultParams =
4088                      SecurityParams.createSecurityParamsBySecurityType(
4089                              WifiConfiguration.SECURITY_TYPE_PSK);
4090 
4091             if (!linkConfig.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)
4092                     || !linkConfig.getSecurityParams(
4093                             WifiConfiguration.SECURITY_TYPE_PSK).isEnabled()) {
4094                 defaultParams = SecurityParams.createSecurityParamsBySecurityType(
4095                         WifiConfiguration.SECURITY_TYPE_SAE);
4096             }
4097 
4098             linkConfig.getNetworkSelectionStatus().setCandidateSecurityParams(defaultParams);
4099             linkedNetworks.put(configKey, linkConfig);
4100         }
4101         return linkedNetworks;
4102     }
4103 
4104     /**
4105      * This method updates FILS AKMs to the internal network.
4106      *
4107      * @param networkId networkId corresponding to the network to be updated.
4108      */
4109     public void updateFilsAkms(int networkId,
4110             boolean isFilsSha256Supported, boolean isFilsSha384Supported) {
4111         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4112         if (internalConfig == null) {
4113             return;
4114         }
4115         internalConfig.enableFils(isFilsSha256Supported, isFilsSha384Supported);
4116     }
4117 
4118     /**
4119      * This method updates auto-upgrade flag to the internal network.
4120      *
4121      * @param networkId networkId corresponding to the network to be updated.
4122      * @param securityType the target security type
4123      * @param isAddedByAutoUpgrade indicate whether the target security type is added
4124      *        by auto-upgrade or not.
4125      */
4126     public void updateIsAddedByAutoUpgradeFlag(int networkId,
4127             int securityType, boolean isAddedByAutoUpgrade) {
4128         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4129         if (internalConfig == null) {
4130             return;
4131         }
4132         internalConfig.setSecurityParamsIsAddedByAutoUpgrade(securityType, isAddedByAutoUpgrade);
4133         saveToStore(true);
4134     }
4135 
4136     private static final int SUBJECT_ALTERNATIVE_NAMES_EMAIL = 1;
4137     private static final int SUBJECT_ALTERNATIVE_NAMES_DNS = 2;
4138     private static final int SUBJECT_ALTERNATIVE_NAMES_URI = 6;
4139     /** altSubjectMatch only matches EMAIL, DNS, and URI. */
4140     private static String getAltSubjectMatchFromAltSubjectName(X509Certificate cert) {
4141         Collection<List<?>> col = null;
4142         try {
4143             col = cert.getSubjectAlternativeNames();
4144         } catch (CertificateParsingException ex) {
4145             col = null;
4146         }
4147 
4148         if (null == col) return null;
4149         if (0 == col.size()) return null;
4150 
4151         List<String> altSubjectNameList = new ArrayList<>();
4152         for (List<?> item: col) {
4153             if (2 != item.size()) continue;
4154             if (!(item.get(0) instanceof Integer)) continue;
4155             if (!(item.get(1) instanceof String)) continue;
4156 
4157             StringBuilder sb = new StringBuilder();
4158             int type = (Integer) item.get(0);
4159             if (SUBJECT_ALTERNATIVE_NAMES_EMAIL == type) {
4160                 sb.append("EMAIL:");
4161             } else if (SUBJECT_ALTERNATIVE_NAMES_DNS == type) {
4162                 sb.append("DNS:");
4163             } else if (SUBJECT_ALTERNATIVE_NAMES_URI == type) {
4164                 sb.append("URI:");
4165             } else {
4166                 Log.d(TAG, "Ignore type " + type + " for altSubjectMatch");
4167                 continue;
4168             }
4169             sb.append((String) item.get(1));
4170             altSubjectNameList.add(sb.toString());
4171         }
4172         if (altSubjectNameList.size() > 0) {
4173             // wpa_supplicant uses ';' as the separator.
4174             return String.join(";", altSubjectNameList);
4175         }
4176         return null;
4177     }
4178 
4179     /**
4180      * This method updates the Root CA certificate and the domain name of the
4181      * server in the internal network.
4182      *
4183      * @param networkId networkId corresponding to the network to be updated.
4184      * @param caCert Root CA certificate to be updated.
4185      * @param serverCert Server certificate to be updated.
4186      * @param certHash Server certificate hash (for TOFU case with no Root CA). Replaces the use of
4187      *                 a Root CA certificate for authentication.
4188      * @param useSystemTrustStore Indicate if to use the system trust store for authentication. If
4189      *                            this flag is set, then any Root CA or certificate hash specified
4190      *                            is not used.
4191      * @return true if updating Root CA certificate successfully; otherwise, false.
4192      */
4193     public boolean updateCaCertificate(int networkId, @NonNull X509Certificate caCert,
4194             @NonNull X509Certificate serverCert, String certHash, boolean useSystemTrustStore) {
4195         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4196         if (internalConfig == null) {
4197             Log.e(TAG, "No network for network ID " + networkId);
4198             return false;
4199         }
4200         if (!internalConfig.isEnterprise()) {
4201             Log.e(TAG, "Network " + networkId + " is not an Enterprise network");
4202             return false;
4203         }
4204         if (!internalConfig.enterpriseConfig.isEapMethodServerCertUsed()) {
4205             Log.e(TAG, "Network " + networkId + " does not need verifying server cert");
4206             return false;
4207         }
4208         if (null == caCert) {
4209             Log.e(TAG, "Root CA cert is null");
4210             return false;
4211         }
4212         if (null == serverCert) {
4213             Log.e(TAG, "Server cert is null");
4214             return false;
4215         }
4216         CertificateSubjectInfo serverCertInfo = CertificateSubjectInfo.parse(
4217                 serverCert.getSubjectX500Principal().getName());
4218         if (null == serverCertInfo) {
4219             Log.e(TAG, "Invalid Server CA cert subject");
4220             return false;
4221         }
4222 
4223         WifiConfiguration newConfig = new WifiConfiguration(internalConfig);
4224         try {
4225             if (newConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) {
4226                 if (useSystemTrustStore) {
4227                     newConfig.enterpriseConfig
4228                             .setCaPath(WifiConfigurationUtil.getSystemTrustStorePath());
4229                 } else if (TextUtils.isEmpty(certHash)) {
4230                     newConfig.enterpriseConfig.setCaCertificateForTrustOnFirstUse(caCert);
4231                 } else {
4232                     newConfig.enterpriseConfig.setServerCertificateHash(certHash);
4233                 }
4234                 newConfig.enterpriseConfig.enableTrustOnFirstUse(false);
4235             } else {
4236                 // setCaCertificate will mark that this CA certificate should be removed on
4237                 // removing this configuration.
4238                 newConfig.enterpriseConfig.setCaCertificate(caCert);
4239             }
4240         } catch (IllegalArgumentException ex) {
4241             Log.e(TAG, "Failed to set CA cert: " + caCert);
4242             return false;
4243         }
4244 
4245         // If there is a subject alternative name, it should be matched first.
4246         String altSubjectNames = getAltSubjectMatchFromAltSubjectName(serverCert);
4247         if (!TextUtils.isEmpty(altSubjectNames)) {
4248             if (mVerboseLoggingEnabled) {
4249                 Log.d(TAG, "Set altSubjectMatch to " + altSubjectNames);
4250             }
4251             newConfig.enterpriseConfig.setAltSubjectMatch(altSubjectNames);
4252         } else {
4253             if (mVerboseLoggingEnabled) {
4254                 Log.d(TAG, "Set domainSuffixMatch to " + serverCertInfo.commonName);
4255             }
4256             newConfig.enterpriseConfig.setDomainSuffixMatch(serverCertInfo.commonName);
4257         }
4258         newConfig.enterpriseConfig.setUserApproveNoCaCert(false);
4259         // Trigger an update to install CA certificate and the corresponding configuration.
4260         NetworkUpdateResult result = addOrUpdateNetwork(newConfig, internalConfig.creatorUid);
4261         if (!result.isSuccess()) {
4262             Log.e(TAG, "Failed to install CA cert for network " + internalConfig.SSID);
4263             mFrameworkFacade.showToast(mContext, mContext.getResources().getString(
4264                     R.string.wifi_ca_cert_failed_to_install_ca_cert));
4265             return false;
4266         }
4267         return true;
4268     }
4269 
4270     /**
4271      * This method updates Trust On First Use flag according to
4272      * Trust On First Use support and No-Ca-Cert Approval.
4273      */
4274     public void updateTrustOnFirstUseFlag(boolean enableTrustOnFirstUse) {
4275         getInternalConfiguredNetworks().stream()
4276                 .filter(config -> config.isEnterprise())
4277                 .filter(config -> config.enterpriseConfig.isEapMethodServerCertUsed())
4278                 .filter(config -> !config.enterpriseConfig.hasCaCertificate())
4279                 .forEach(config ->
4280                         config.enterpriseConfig.enableTrustOnFirstUse(enableTrustOnFirstUse));
4281     }
4282 
4283     /**
4284      * This method updates that a network could has no CA cert as a user approves it.
4285      *
4286      * @param networkId networkId corresponding to the network to be updated.
4287      * @param approved true for the approval; otherwise, false.
4288      */
setUserApproveNoCaCert(int networkId, boolean approved)4289     public void setUserApproveNoCaCert(int networkId, boolean approved) {
4290         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4291         if (internalConfig == null) return;
4292         if (!internalConfig.isEnterprise()) return;
4293         if (!internalConfig.enterpriseConfig.isEapMethodServerCertUsed()) return;
4294         internalConfig.enterpriseConfig.setUserApproveNoCaCert(approved);
4295     }
4296 
4297     /**
4298      * This method updates that a network uses Trust On First Use.
4299      *
4300      * @param networkId networkId corresponding to the network to be updated.
4301      * @param enable true to enable Trust On First Use; otherwise, disable Trust On First Use.
4302      */
enableTrustOnFirstUse(int networkId, boolean enable)4303     public void enableTrustOnFirstUse(int networkId, boolean enable) {
4304         WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
4305         if (internalConfig == null) return;
4306         if (!internalConfig.isEnterprise()) return;
4307         if (!internalConfig.enterpriseConfig.isEapMethodServerCertUsed()) return;
4308         internalConfig.enterpriseConfig.enableTrustOnFirstUse(enable);
4309     }
4310 
4311     /**
4312      * Add custom DHCP options.
4313      *
4314      * @param ssid the network SSID.
4315      * @param oui the 3-byte OUI.
4316      * @param options the list of DHCP options.
4317      */
addCustomDhcpOptions(@onNull WifiSsid ssid, @NonNull byte[] oui, @NonNull List<DhcpOption> options)4318     public void addCustomDhcpOptions(@NonNull WifiSsid ssid, @NonNull byte[] oui,
4319             @NonNull List<DhcpOption> options) {
4320         mCustomDhcpOptions.put(new NetworkIdentifier(ssid, oui), options);
4321     }
4322 
4323     /**
4324      * Remove custom DHCP options.
4325      *
4326      * @param ssid the network SSID.
4327      * @param oui the 3-byte OUI.
4328      */
removeCustomDhcpOptions(@onNull WifiSsid ssid, @NonNull byte[] oui)4329     public void removeCustomDhcpOptions(@NonNull WifiSsid ssid, @NonNull byte[] oui) {
4330         mCustomDhcpOptions.remove(new NetworkIdentifier(ssid, oui));
4331     }
4332 
4333     /**
4334      * Get custom DHCP options.
4335      *
4336      * @param ssid the network SSID.
4337      * @param ouiList the list of OUIs.
4338      *
4339      * @return null if no entry in the map is keyed by the SSID and any OUI in the list.
4340      *         all the DHCP options keyed by the SSID and the OUIs in the list.
4341      */
getCustomDhcpOptions(@onNull WifiSsid ssid, @NonNull List<byte[]> ouiList)4342     public List<DhcpOption> getCustomDhcpOptions(@NonNull WifiSsid ssid,
4343             @NonNull List<byte[]> ouiList) {
4344         Set<DhcpOption> results = new HashSet<>();
4345         for (byte[] oui : ouiList) {
4346             List<DhcpOption> options = mCustomDhcpOptions.get(new NetworkIdentifier(ssid, oui));
4347             if (options != null) {
4348                 results.addAll(options);
4349             }
4350         }
4351         return new ArrayList<>(results);
4352     }
4353 }
4354