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