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