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