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