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