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