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 * Check if the given UID belongs to the current foreground user. This is 917 * used to prevent apps running in background users from modifying network 918 * configurations. 919 * <p> 920 * UIDs belonging to system internals (such as SystemUI) are always allowed, 921 * since they always run as {@link UserHandle#USER_SYSTEM}. 922 * 923 * @param uid uid of the app. 924 * @return true if the given UID belongs to the current foreground user, 925 * otherwise false. 926 */ doesUidBelongToCurrentUser(int uid)927 private boolean doesUidBelongToCurrentUser(int uid) { 928 if (uid == android.os.Process.SYSTEM_UID 929 // UIDs with the NETWORK_SETTINGS permission are always allowed since they are 930 // acting on behalf of the user. 931 || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { 932 return true; 933 } else { 934 UserHandle currentUser = UserHandle.of(mCurrentUserId); 935 UserHandle callingUser = UserHandle.getUserHandleForUid(uid); 936 return currentUser.equals(callingUser) 937 || mUserManager.isSameProfileGroup(currentUser, callingUser); 938 } 939 } 940 941 /** 942 * Copy over public elements from an external WifiConfiguration object to the internal 943 * configuration object if element has been set in the provided external WifiConfiguration. 944 * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over 945 * for every update. 946 * 947 * This method updates all elements that are common to both network addition & update. 948 * The following fields of {@link WifiConfiguration} are not copied from external configs: 949 * > networkId - These are allocated by Wi-Fi stack internally for any new configurations. 950 * > status - The status needs to be explicitly updated using 951 * {@link WifiManager#enableNetwork(int, boolean)} or 952 * {@link WifiManager#disableNetwork(int)}. 953 * 954 * @param internalConfig WifiConfiguration object in our internal map. 955 * @param externalConfig WifiConfiguration object provided from the external API. 956 */ mergeWithInternalWifiConfiguration( WifiConfiguration internalConfig, WifiConfiguration externalConfig)957 private void mergeWithInternalWifiConfiguration( 958 WifiConfiguration internalConfig, WifiConfiguration externalConfig) { 959 if (externalConfig.SSID != null) { 960 internalConfig.SSID = externalConfig.SSID; 961 } 962 if (externalConfig.BSSID != null) { 963 internalConfig.BSSID = externalConfig.BSSID.toLowerCase(); 964 } 965 internalConfig.hiddenSSID = externalConfig.hiddenSSID; 966 internalConfig.requirePmf = externalConfig.requirePmf; 967 968 if (externalConfig.preSharedKey != null 969 && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) { 970 internalConfig.preSharedKey = externalConfig.preSharedKey; 971 } 972 // Modify only wep keys are present in the provided configuration. This is a little tricky 973 // because there is no easy way to tell if the app is actually trying to null out the 974 // existing keys or not. 975 if (externalConfig.wepKeys != null) { 976 boolean hasWepKey = false; 977 for (int i = 0; i < internalConfig.wepKeys.length; i++) { 978 if (externalConfig.wepKeys[i] != null 979 && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) { 980 internalConfig.wepKeys[i] = externalConfig.wepKeys[i]; 981 hasWepKey = true; 982 } 983 } 984 if (hasWepKey) { 985 internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex; 986 } 987 } 988 if (externalConfig.FQDN != null) { 989 internalConfig.FQDN = externalConfig.FQDN; 990 } 991 if (externalConfig.providerFriendlyName != null) { 992 internalConfig.providerFriendlyName = externalConfig.providerFriendlyName; 993 } 994 if (externalConfig.roamingConsortiumIds != null) { 995 internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone(); 996 } 997 998 // Copy over all the auth/protocol/key mgmt parameters if set. 999 if (externalConfig.allowedAuthAlgorithms != null 1000 && !externalConfig.allowedAuthAlgorithms.isEmpty()) { 1001 internalConfig.allowedAuthAlgorithms = 1002 (BitSet) externalConfig.allowedAuthAlgorithms.clone(); 1003 } 1004 if (externalConfig.allowedProtocols != null 1005 && !externalConfig.allowedProtocols.isEmpty()) { 1006 internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone(); 1007 } 1008 if (externalConfig.allowedKeyManagement != null 1009 && !externalConfig.allowedKeyManagement.isEmpty()) { 1010 internalConfig.allowedKeyManagement = 1011 (BitSet) externalConfig.allowedKeyManagement.clone(); 1012 } 1013 if (externalConfig.allowedPairwiseCiphers != null 1014 && !externalConfig.allowedPairwiseCiphers.isEmpty()) { 1015 internalConfig.allowedPairwiseCiphers = 1016 (BitSet) externalConfig.allowedPairwiseCiphers.clone(); 1017 } 1018 if (externalConfig.allowedGroupCiphers != null 1019 && !externalConfig.allowedGroupCiphers.isEmpty()) { 1020 internalConfig.allowedGroupCiphers = 1021 (BitSet) externalConfig.allowedGroupCiphers.clone(); 1022 } 1023 if (externalConfig.allowedGroupManagementCiphers != null 1024 && !externalConfig.allowedGroupManagementCiphers.isEmpty()) { 1025 internalConfig.allowedGroupManagementCiphers = 1026 (BitSet) externalConfig.allowedGroupManagementCiphers.clone(); 1027 } 1028 // allowedSuiteBCiphers is set internally according to the certificate type 1029 1030 // Copy over the |IpConfiguration| parameters if set. 1031 if (externalConfig.getIpConfiguration() != null) { 1032 IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment(); 1033 if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) { 1034 internalConfig.setIpAssignment(ipAssignment); 1035 if (ipAssignment == IpConfiguration.IpAssignment.STATIC) { 1036 internalConfig.setStaticIpConfiguration( 1037 new StaticIpConfiguration(externalConfig.getStaticIpConfiguration())); 1038 } 1039 } 1040 IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings(); 1041 if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) { 1042 internalConfig.setProxySettings(proxySettings); 1043 if (proxySettings == IpConfiguration.ProxySettings.PAC 1044 || proxySettings == IpConfiguration.ProxySettings.STATIC) { 1045 internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy())); 1046 } 1047 } 1048 } 1049 1050 // Copy over the |WifiEnterpriseConfig| parameters if set. 1051 if (externalConfig.enterpriseConfig != null) { 1052 internalConfig.enterpriseConfig.copyFromExternal( 1053 externalConfig.enterpriseConfig, PASSWORD_MASK); 1054 } 1055 1056 // Copy over any metered information. 1057 internalConfig.meteredHint = externalConfig.meteredHint; 1058 internalConfig.meteredOverride = externalConfig.meteredOverride; 1059 1060 // Copy trusted bit 1061 internalConfig.trusted = externalConfig.trusted; 1062 1063 // Copy over macRandomizationSetting 1064 internalConfig.macRandomizationSetting = externalConfig.macRandomizationSetting; 1065 internalConfig.carrierId = externalConfig.carrierId; 1066 internalConfig.isHomeProviderNetwork = externalConfig.isHomeProviderNetwork; 1067 } 1068 1069 /** 1070 * Set all the exposed defaults in the newly created WifiConfiguration object. 1071 * These fields have a default value advertised in our public documentation. The only exception 1072 * is the hidden |IpConfiguration| parameters, these have a default value even though they're 1073 * hidden. 1074 * 1075 * @param configuration provided WifiConfiguration object. 1076 */ setDefaultsInWifiConfiguration(WifiConfiguration configuration)1077 private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) { 1078 configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); 1079 configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA); 1080 1081 configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); 1082 configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 1083 1084 configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); 1085 configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); 1086 configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); 1087 1088 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); 1089 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); 1090 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); 1091 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); 1092 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); 1093 1094 configuration.allowedGroupManagementCiphers 1095 .set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); 1096 1097 configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP); 1098 configuration.setProxySettings(IpConfiguration.ProxySettings.NONE); 1099 1100 configuration.status = WifiConfiguration.Status.DISABLED; 1101 configuration.getNetworkSelectionStatus().setNetworkSelectionStatus( 1102 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); 1103 configuration.getNetworkSelectionStatus().setNetworkSelectionDisableReason( 1104 NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); 1105 } 1106 1107 /** 1108 * Create a new internal WifiConfiguration object by copying over parameters from the provided 1109 * external configuration and set defaults for the appropriate parameters. 1110 * 1111 * @param externalConfig WifiConfiguration object provided from the external API. 1112 * @return New WifiConfiguration object with parameters merged from the provided external 1113 * configuration. 1114 */ createNewInternalWifiConfigurationFromExternal( WifiConfiguration externalConfig, int uid, @Nullable String packageName)1115 private WifiConfiguration createNewInternalWifiConfigurationFromExternal( 1116 WifiConfiguration externalConfig, int uid, @Nullable String packageName) { 1117 WifiConfiguration newInternalConfig = new WifiConfiguration(); 1118 1119 // First allocate a new network ID for the configuration. 1120 newInternalConfig.networkId = mNextNetworkId++; 1121 1122 // First set defaults in the new configuration created. 1123 setDefaultsInWifiConfiguration(newInternalConfig); 1124 1125 // Copy over all the public elements from the provided configuration. 1126 mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig); 1127 1128 // Copy over the hidden configuration parameters. These are the only parameters used by 1129 // system apps to indicate some property about the network being added. 1130 // These are only copied over for network additions and ignored for network updates. 1131 newInternalConfig.requirePmf = externalConfig.requirePmf; 1132 newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected; 1133 newInternalConfig.ephemeral = externalConfig.ephemeral; 1134 newInternalConfig.osu = externalConfig.osu; 1135 newInternalConfig.trusted = externalConfig.trusted; 1136 newInternalConfig.fromWifiNetworkSuggestion = externalConfig.fromWifiNetworkSuggestion; 1137 newInternalConfig.fromWifiNetworkSpecifier = externalConfig.fromWifiNetworkSpecifier; 1138 newInternalConfig.useExternalScores = externalConfig.useExternalScores; 1139 newInternalConfig.shared = externalConfig.shared; 1140 newInternalConfig.updateIdentifier = externalConfig.updateIdentifier; 1141 newInternalConfig.setPasspointUniqueId(externalConfig.getPasspointUniqueId()); 1142 1143 // Add debug information for network addition. 1144 newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid; 1145 newInternalConfig.creatorName = newInternalConfig.lastUpdateName = 1146 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid); 1147 initRandomizedMacForInternalConfig(newInternalConfig); 1148 return newInternalConfig; 1149 } 1150 1151 /** 1152 * Create a new internal WifiConfiguration object by copying over parameters from the provided 1153 * external configuration to a copy of the existing internal WifiConfiguration object. 1154 * 1155 * @param internalConfig WifiConfiguration object in our internal map. 1156 * @param externalConfig WifiConfiguration object provided from the external API. 1157 * @return Copy of existing WifiConfiguration object with parameters merged from the provided 1158 * configuration. 1159 */ updateExistingInternalWifiConfigurationFromExternal( WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid, @Nullable String packageName)1160 private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal( 1161 WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid, 1162 @Nullable String packageName) { 1163 WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig); 1164 1165 // Copy over all the public elements from the provided configuration. 1166 mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig); 1167 1168 // Add debug information for network update. 1169 newInternalConfig.lastUpdateUid = uid; 1170 newInternalConfig.lastUpdateName = 1171 packageName != null ? packageName : mContext.getPackageManager().getNameForUid(uid); 1172 1173 return newInternalConfig; 1174 } 1175 logUserActionEvents(WifiConfiguration before, WifiConfiguration after)1176 private void logUserActionEvents(WifiConfiguration before, WifiConfiguration after) { 1177 // Logs changes in meteredOverride. 1178 if (before.meteredOverride != after.meteredOverride) { 1179 mWifiInjector.getWifiMetrics().logUserActionEvent( 1180 WifiMetrics.convertMeteredOverrideEnumToUserActionEventType( 1181 after.meteredOverride), 1182 after.networkId); 1183 } 1184 1185 // Logs changes in macRandomizationSetting. 1186 if (before.macRandomizationSetting != after.macRandomizationSetting) { 1187 mWifiInjector.getWifiMetrics().logUserActionEvent( 1188 after.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE 1189 ? UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF 1190 : UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_ON, 1191 after.networkId); 1192 } 1193 } 1194 1195 /** 1196 * Add a network or update a network configuration to our database. 1197 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1198 * network configuration. Otherwise, the networkId should refer to an existing configuration. 1199 * 1200 * @param config provided WifiConfiguration object. 1201 * @param uid UID of the app requesting the network addition/modification. 1202 * @param packageName Package name of the app requesting the network addition/modification. 1203 * @return NetworkUpdateResult object representing status of the update. 1204 */ addOrUpdateNetworkInternal(WifiConfiguration config, int uid, @Nullable String packageName)1205 private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid, 1206 @Nullable String packageName) { 1207 if (mVerboseLoggingEnabled) { 1208 Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid()); 1209 } 1210 WifiConfiguration newInternalConfig = null; 1211 1212 // First check if we already have a network with the provided network id or configKey. 1213 WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config); 1214 // No existing network found. So, potentially a network add. 1215 if (existingInternalConfig == null) { 1216 if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) { 1217 Log.e(TAG, "Cannot add network with invalid config"); 1218 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1219 } 1220 newInternalConfig = 1221 createNewInternalWifiConfigurationFromExternal(config, uid, packageName); 1222 // Since the original config provided may have had an empty 1223 // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a 1224 // network with the the same configkey. 1225 existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.getKey()); 1226 } 1227 // Existing network found. So, a network update. 1228 if (existingInternalConfig != null) { 1229 if (!WifiConfigurationUtil.validate( 1230 config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) { 1231 Log.e(TAG, "Cannot update network with invalid config"); 1232 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1233 } 1234 // Check for the app's permission before we let it update this network. 1235 if (!canModifyNetwork(existingInternalConfig, uid, packageName)) { 1236 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1237 + config.getKey()); 1238 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1239 } 1240 if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid) 1241 && !config.isPasspoint()) { 1242 logUserActionEvents(existingInternalConfig, config); 1243 } 1244 newInternalConfig = 1245 updateExistingInternalWifiConfigurationFromExternal( 1246 existingInternalConfig, config, uid, packageName); 1247 } 1248 1249 // Only add networks with proxy settings if the user has permission to 1250 if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig) 1251 && !canModifyProxySettings(uid, packageName)) { 1252 Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings " 1253 + config.getKey() + ". Must have NETWORK_SETTINGS," 1254 + " or be device or profile owner."); 1255 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1256 } 1257 1258 if (WifiConfigurationUtil.hasMacRandomizationSettingsChanged(existingInternalConfig, 1259 newInternalConfig) && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid) 1260 && !mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid) 1261 && !(newInternalConfig.isPasspoint() && uid == newInternalConfig.creatorUid)) { 1262 Log.e(TAG, "UID " + uid + " does not have permission to modify MAC randomization " 1263 + "Settings " + config.getKey() + ". Must have " 1264 + "NETWORK_SETTINGS or NETWORK_SETUP_WIZARD or be the creator adding or " 1265 + "updating a passpoint network."); 1266 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1267 } 1268 1269 // Update the keys for saved enterprise networks. For Passpoint, the certificates 1270 // and keys are installed at the time the provider is installed. For suggestion enterprise 1271 // network the certificates and keys are installed at the time the suggestion is added 1272 if (!config.isPasspoint() && !config.fromWifiNetworkSuggestion && config.isEnterprise()) { 1273 if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) { 1274 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1275 } 1276 } 1277 1278 boolean newNetwork = (existingInternalConfig == null); 1279 // This is needed to inform IpClient about any IP configuration changes. 1280 boolean hasIpChanged = 1281 newNetwork || WifiConfigurationUtil.hasIpChanged( 1282 existingInternalConfig, newInternalConfig); 1283 boolean hasProxyChanged = 1284 newNetwork || WifiConfigurationUtil.hasProxyChanged( 1285 existingInternalConfig, newInternalConfig); 1286 // Reset the |hasEverConnected| flag if the credential parameters changed in this update. 1287 boolean hasCredentialChanged = 1288 newNetwork || WifiConfigurationUtil.hasCredentialChanged( 1289 existingInternalConfig, newInternalConfig); 1290 if (hasCredentialChanged) { 1291 newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false); 1292 } 1293 1294 // Add it to our internal map. This will replace any existing network configuration for 1295 // updates. 1296 try { 1297 mConfiguredNetworks.put(newInternalConfig); 1298 } catch (IllegalArgumentException e) { 1299 Log.e(TAG, "Failed to add network to config map", e); 1300 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1301 } 1302 // Only re-enable network: 1. add or update user saved network; 2. add or update a user 1303 // saved passpoint network framework consider it is a new network. 1304 if (!newInternalConfig.fromWifiNetworkSuggestion 1305 && (!newInternalConfig.isPasspoint() || newNetwork)) { 1306 userEnabledNetwork(newInternalConfig.networkId); 1307 } 1308 1309 // Stage the backup of the SettingsProvider package which backs this up. 1310 mBackupManagerProxy.notifyDataChanged(); 1311 1312 NetworkUpdateResult result = 1313 new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged); 1314 result.setIsNewNetwork(newNetwork); 1315 result.setNetworkId(newInternalConfig.networkId); 1316 1317 localLog("addOrUpdateNetworkInternal: added/updated config." 1318 + " netId=" + newInternalConfig.networkId 1319 + " configKey=" + newInternalConfig.getKey() 1320 + " uid=" + Integer.toString(newInternalConfig.creatorUid) 1321 + " name=" + newInternalConfig.creatorName); 1322 return result; 1323 } 1324 1325 /** 1326 * Add a network or update a network configuration to our database. 1327 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1328 * network configuration. Otherwise, the networkId should refer to an existing configuration. 1329 * 1330 * @param config provided WifiConfiguration object. 1331 * @param uid UID of the app requesting the network addition/modification. 1332 * @param packageName Package name of the app requesting the network addition/modification. 1333 * @return NetworkUpdateResult object representing status of the update. 1334 */ addOrUpdateNetwork(WifiConfiguration config, int uid, @Nullable String packageName)1335 public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid, 1336 @Nullable String packageName) { 1337 if (!doesUidBelongToCurrentUser(uid)) { 1338 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1339 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1340 } 1341 if (config == null) { 1342 Log.e(TAG, "Cannot add/update network with null config"); 1343 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1344 } 1345 if (mPendingStoreRead) { 1346 Log.e(TAG, "Cannot add/update network before store is read!"); 1347 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1348 } 1349 WifiConfiguration existingConfig = getInternalConfiguredNetwork(config); 1350 if (!config.isEphemeral()) { 1351 // Removes the existing ephemeral network if it exists to add this configuration. 1352 if (existingConfig != null && existingConfig.isEphemeral()) { 1353 // In this case, new connection for this config won't happen because same 1354 // network is already registered as an ephemeral network. 1355 // Clear the Ephemeral Network to address the situation. 1356 removeNetwork( 1357 existingConfig.networkId, existingConfig.creatorUid, config.creatorName); 1358 } 1359 } 1360 1361 NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid, packageName); 1362 if (!result.isSuccess()) { 1363 Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid()); 1364 return result; 1365 } 1366 WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId()); 1367 sendConfiguredNetworkChangedBroadcast( 1368 result.isNewNetwork() 1369 ? WifiManager.CHANGE_REASON_ADDED 1370 : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1371 // Unless the added network is ephemeral or Passpoint, persist the network update/addition. 1372 if (!config.ephemeral && !config.isPasspoint()) { 1373 saveToStore(true); 1374 } 1375 1376 for (OnNetworkUpdateListener listener : mListeners) { 1377 if (result.isNewNetwork()) { 1378 listener.onNetworkAdded( 1379 createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID)); 1380 } else { 1381 listener.onNetworkUpdated( 1382 createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID), 1383 createExternalWifiConfiguration(existingConfig, true, Process.WIFI_UID)); 1384 } 1385 } 1386 return result; 1387 } 1388 1389 /** 1390 * Add a network or update a network configuration to our database. 1391 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1392 * network configuration. Otherwise, the networkId should refer to an existing configuration. 1393 * 1394 * @param config provided WifiConfiguration object. 1395 * @param uid UID of the app requesting the network addition/modification. 1396 * @return NetworkUpdateResult object representing status of the update. 1397 */ addOrUpdateNetwork(WifiConfiguration config, int uid)1398 public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) { 1399 return addOrUpdateNetwork(config, uid, null); 1400 } 1401 1402 /** 1403 * Removes the specified network configuration from our database. 1404 * 1405 * @param config provided WifiConfiguration object. 1406 * @param uid UID of the app requesting the network deletion. 1407 * @return true if successful, false otherwise. 1408 */ removeNetworkInternal(WifiConfiguration config, int uid)1409 private boolean removeNetworkInternal(WifiConfiguration config, int uid) { 1410 if (mVerboseLoggingEnabled) { 1411 Log.v(TAG, "Removing network " + config.getPrintableSsid()); 1412 } 1413 // Remove any associated enterprise keys for saved enterprise networks. Passpoint network 1414 // will remove the enterprise keys when provider is uninstalled. Suggestion enterprise 1415 // networks will remove the enterprise keys when suggestion is removed. 1416 if (!config.fromWifiNetworkSuggestion && !config.isPasspoint() && config.isEnterprise()) { 1417 mWifiKeyStore.removeKeys(config.enterpriseConfig); 1418 } 1419 1420 removeConnectChoiceFromAllNetworks(config.getKey()); 1421 mConfiguredNetworks.remove(config.networkId); 1422 mScanDetailCaches.remove(config.networkId); 1423 // Stage the backup of the SettingsProvider package which backs this up. 1424 mBackupManagerProxy.notifyDataChanged(); 1425 mWifiInjector.getBssidBlocklistMonitor().handleNetworkRemoved(config.SSID); 1426 1427 localLog("removeNetworkInternal: removed config." 1428 + " netId=" + config.networkId 1429 + " configKey=" + config.getKey() 1430 + " uid=" + Integer.toString(uid) 1431 + " name=" + mContext.getPackageManager().getNameForUid(uid)); 1432 return true; 1433 } 1434 1435 /** 1436 * Removes the specified network configuration from our database. 1437 * 1438 * @param networkId network ID of the provided network. 1439 * @param uid UID of the app requesting the network deletion. 1440 * @return true if successful, false otherwise. 1441 */ removeNetwork(int networkId, int uid, String packageName)1442 public boolean removeNetwork(int networkId, int uid, String packageName) { 1443 if (!doesUidBelongToCurrentUser(uid)) { 1444 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1445 return false; 1446 } 1447 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1448 if (config == null) { 1449 return false; 1450 } 1451 if (!canModifyNetwork(config, uid, packageName)) { 1452 Log.e(TAG, "UID " + uid + " does not have permission to delete configuration " 1453 + config.getKey()); 1454 return false; 1455 } 1456 if (!removeNetworkInternal(config, uid)) { 1457 Log.e(TAG, "Failed to remove network " + config.getPrintableSsid()); 1458 return false; 1459 } 1460 if (networkId == mLastSelectedNetworkId) { 1461 clearLastSelectedNetwork(); 1462 } 1463 if (!config.ephemeral && !config.isPasspoint()) { 1464 mLruConnectionTracker.removeNetwork(config); 1465 } 1466 sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_REMOVED); 1467 // Unless the removed network is ephemeral or Passpoint, persist the network removal. 1468 if (!config.ephemeral && !config.isPasspoint()) { 1469 saveToStore(true); 1470 } 1471 for (OnNetworkUpdateListener listener : mListeners) { 1472 listener.onNetworkRemoved( 1473 createExternalWifiConfiguration(config, true, Process.WIFI_UID)); 1474 } 1475 return true; 1476 } 1477 getCreatorPackageName(WifiConfiguration config)1478 private String getCreatorPackageName(WifiConfiguration config) { 1479 String creatorName = config.creatorName; 1480 // getNameForUid (Stored in WifiConfiguration.creatorName) returns a concatenation of name 1481 // and uid for shared UIDs ("name:uid"). 1482 if (!creatorName.contains(":")) { 1483 return creatorName; // regular app not using shared UID. 1484 } 1485 // Separate the package name from the string for app using shared UID. 1486 return creatorName.substring(0, creatorName.indexOf(":")); 1487 } 1488 1489 /** 1490 * Remove all networks associated with an application. 1491 * 1492 * @param app Application info of the package of networks to remove. 1493 * @return the {@link Set} of networks that were removed by this call. Networks which matched 1494 * but failed to remove are omitted from this set. 1495 */ removeNetworksForApp(ApplicationInfo app)1496 public Set<Integer> removeNetworksForApp(ApplicationInfo app) { 1497 if (app == null || app.packageName == null) { 1498 return Collections.<Integer>emptySet(); 1499 } 1500 Log.d(TAG, "Remove all networks for app " + app); 1501 Set<Integer> removedNetworks = new ArraySet<>(); 1502 WifiConfiguration[] copiedConfigs = 1503 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1504 for (WifiConfiguration config : copiedConfigs) { 1505 if (app.uid != config.creatorUid 1506 || !app.packageName.equals(getCreatorPackageName(config))) { 1507 continue; 1508 } 1509 localLog("Removing network " + config.SSID 1510 + ", application \"" + app.packageName + "\" uninstalled" 1511 + " from user " + UserHandle.getUserHandleForUid(app.uid)); 1512 if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) { 1513 removedNetworks.add(config.networkId); 1514 } 1515 } 1516 return removedNetworks; 1517 } 1518 1519 /** 1520 * Remove all networks associated with a user. 1521 * 1522 * @param userId The identifier of the user which is being removed. 1523 * @return the {@link Set} of networks that were removed by this call. Networks which matched 1524 * but failed to remove are omitted from this set. 1525 */ removeNetworksForUser(int userId)1526 Set<Integer> removeNetworksForUser(int userId) { 1527 Log.d(TAG, "Remove all networks for user " + userId); 1528 Set<Integer> removedNetworks = new ArraySet<>(); 1529 WifiConfiguration[] copiedConfigs = 1530 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1531 for (WifiConfiguration config : copiedConfigs) { 1532 if (userId != UserHandle.getUserHandleForUid(config.creatorUid).getIdentifier()) { 1533 continue; 1534 } 1535 localLog("Removing network " + config.SSID + ", user " + userId + " removed"); 1536 if (removeNetwork(config.networkId, config.creatorUid, config.creatorName)) { 1537 removedNetworks.add(config.networkId); 1538 } 1539 } 1540 return removedNetworks; 1541 } 1542 1543 /** 1544 * Iterates through the internal list of configured networks and removes any ephemeral or 1545 * passpoint network configurations which are transient in nature. 1546 * 1547 * @return true if a network was removed, false otherwise. 1548 */ removeAllEphemeralOrPasspointConfiguredNetworks()1549 public boolean removeAllEphemeralOrPasspointConfiguredNetworks() { 1550 if (mVerboseLoggingEnabled) { 1551 Log.v(TAG, "Removing all passpoint or ephemeral configured networks"); 1552 } 1553 boolean didRemove = false; 1554 WifiConfiguration[] copiedConfigs = 1555 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1556 for (WifiConfiguration config : copiedConfigs) { 1557 if (config.isPasspoint()) { 1558 Log.d(TAG, "Removing passpoint network config " + config.getKey()); 1559 removeNetwork(config.networkId, config.creatorUid, config.creatorName); 1560 didRemove = true; 1561 } else if (config.ephemeral) { 1562 Log.d(TAG, "Removing ephemeral network config " + config.getKey()); 1563 removeNetwork(config.networkId, config.creatorUid, config.creatorName); 1564 didRemove = true; 1565 } 1566 } 1567 return didRemove; 1568 } 1569 1570 /** 1571 * Removes the suggestion network configuration matched with {@code configKey} provided. 1572 * 1573 * @param configKey Config Key for the corresponding network suggestion. 1574 * @return true if a network was removed, false otherwise. 1575 */ removeSuggestionConfiguredNetwork(@onNull String configKey)1576 public boolean removeSuggestionConfiguredNetwork(@NonNull String configKey) { 1577 WifiConfiguration config = getInternalConfiguredNetwork(configKey); 1578 if (config != null && config.ephemeral && config.fromWifiNetworkSuggestion) { 1579 Log.d(TAG, "Removing suggestion network config " + config.getKey()); 1580 return removeNetwork(config.networkId, config.creatorUid, config.creatorName); 1581 } 1582 return false; 1583 } 1584 1585 /** 1586 * Removes the passpoint network configuration matched with {@code configKey} provided. 1587 * 1588 * @param configKey Config Key for the corresponding passpoint. 1589 * @return true if a network was removed, false otherwise. 1590 */ removePasspointConfiguredNetwork(@onNull String configKey)1591 public boolean removePasspointConfiguredNetwork(@NonNull String configKey) { 1592 WifiConfiguration config = getInternalConfiguredNetwork(configKey); 1593 if (config != null && config.isPasspoint()) { 1594 Log.d(TAG, "Removing passpoint network config " + config.getKey()); 1595 return removeNetwork(config.networkId, config.creatorUid, config.creatorName); 1596 } 1597 return false; 1598 } 1599 1600 /** 1601 * Check whether a network belong to a known list of networks that may not support randomized 1602 * MAC. 1603 * @param networkId 1604 * @return true if the network is in the hotlist and MAC randomization is enabled. 1605 */ isInFlakyRandomizationSsidHotlist(int networkId)1606 public boolean isInFlakyRandomizationSsidHotlist(int networkId) { 1607 WifiConfiguration config = getConfiguredNetwork(networkId); 1608 return config != null 1609 && config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT 1610 && mDeviceConfigFacade.getRandomizationFlakySsidHotlist().contains(config.SSID); 1611 } 1612 1613 /** 1614 * Helper method to mark a network enabled for network selection. 1615 */ setNetworkSelectionEnabled(WifiConfiguration config)1616 private void setNetworkSelectionEnabled(WifiConfiguration config) { 1617 NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1618 if (status.getNetworkSelectionStatus() 1619 != NetworkSelectionStatus.NETWORK_SELECTION_ENABLED) { 1620 localLog("setNetworkSelectionEnabled: configKey=" + config.getKey() 1621 + " old networkStatus=" + status.getNetworkStatusString() 1622 + " disableReason=" + status.getNetworkSelectionDisableReasonString()); 1623 } 1624 status.setNetworkSelectionStatus( 1625 NetworkSelectionStatus.NETWORK_SELECTION_ENABLED); 1626 status.setDisableTime( 1627 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1628 status.setNetworkSelectionDisableReason(NetworkSelectionStatus.DISABLED_NONE); 1629 1630 // Clear out all the disable reason counters. 1631 status.clearDisableReasonCounter(); 1632 for (OnNetworkUpdateListener listener : mListeners) { 1633 listener.onNetworkEnabled( 1634 createExternalWifiConfiguration(config, true, Process.WIFI_UID)); 1635 } 1636 } 1637 1638 /** 1639 * Helper method to mark a network temporarily disabled for network selection. 1640 */ setNetworkSelectionTemporarilyDisabled( WifiConfiguration config, int disableReason)1641 private void setNetworkSelectionTemporarilyDisabled( 1642 WifiConfiguration config, int disableReason) { 1643 NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1644 status.setNetworkSelectionStatus( 1645 NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); 1646 // Only need a valid time filled in for temporarily disabled networks. 1647 status.setDisableTime(mClock.getElapsedSinceBootMillis()); 1648 status.setNetworkSelectionDisableReason(disableReason); 1649 for (OnNetworkUpdateListener listener : mListeners) { 1650 listener.onNetworkTemporarilyDisabled( 1651 createExternalWifiConfiguration(config, true, Process.WIFI_UID), disableReason); 1652 } 1653 } 1654 1655 /** 1656 * Helper method to mark a network permanently disabled for network selection. 1657 */ setNetworkSelectionPermanentlyDisabled( WifiConfiguration config, int disableReason)1658 private void setNetworkSelectionPermanentlyDisabled( 1659 WifiConfiguration config, int disableReason) { 1660 NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1661 status.setNetworkSelectionStatus( 1662 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); 1663 status.setDisableTime( 1664 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1665 status.setNetworkSelectionDisableReason(disableReason); 1666 for (OnNetworkUpdateListener listener : mListeners) { 1667 WifiConfiguration configForListener = new WifiConfiguration(config); 1668 listener.onNetworkPermanentlyDisabled( 1669 createExternalWifiConfiguration(config, true, Process.WIFI_UID), disableReason); 1670 } 1671 } 1672 1673 /** 1674 * Helper method to set the publicly exposed status for the network and send out the network 1675 * status change broadcast. 1676 */ setNetworkStatus(WifiConfiguration config, int status)1677 private void setNetworkStatus(WifiConfiguration config, int status) { 1678 config.status = status; 1679 sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1680 } 1681 1682 /** 1683 * Sets a network's status (both internal and public) according to the update reason and 1684 * its current state. 1685 * 1686 * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the 1687 * public {@link WifiConfiguration#status} field if the network is either enabled or 1688 * permanently disabled. 1689 * 1690 * @param config network to be updated. 1691 * @param reason reason code for update. 1692 * @return true if the input configuration has been updated, false otherwise. 1693 */ setNetworkSelectionStatus(WifiConfiguration config, int reason)1694 private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) { 1695 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1696 if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) { 1697 Log.e(TAG, "Invalid Network disable reason " + reason); 1698 return false; 1699 } 1700 if (reason == NetworkSelectionStatus.DISABLED_NONE) { 1701 setNetworkSelectionEnabled(config); 1702 setNetworkStatus(config, WifiConfiguration.Status.ENABLED); 1703 } else if (reason < NetworkSelectionStatus.PERMANENTLY_DISABLED_STARTING_INDEX) { 1704 setNetworkSelectionTemporarilyDisabled(config, reason); 1705 } else { 1706 setNetworkSelectionPermanentlyDisabled(config, reason); 1707 setNetworkStatus(config, WifiConfiguration.Status.DISABLED); 1708 } 1709 localLog("setNetworkSelectionStatus: configKey=" + config.getKey() 1710 + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason=" 1711 + networkStatus.getNetworkSelectionDisableReasonString()); 1712 saveToStore(false); 1713 return true; 1714 } 1715 1716 /** 1717 * Update a network's status (both internal and public) according to the update reason and 1718 * its current state. 1719 * 1720 * @param config network to be updated. 1721 * @param reason reason code for update. 1722 * @return true if the input configuration has been updated, false otherwise. 1723 */ updateNetworkSelectionStatus(WifiConfiguration config, int reason)1724 private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) { 1725 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1726 if (reason != NetworkSelectionStatus.DISABLED_NONE) { 1727 1728 // Do not update SSID blacklist with information if this is the only 1729 // SSID be observed. By ignoring it we will cause additional failures 1730 // which will trigger Watchdog. 1731 if (reason == NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION 1732 || reason == NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE 1733 || reason == NetworkSelectionStatus.DISABLED_DHCP_FAILURE) { 1734 if (mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate()) { 1735 if (mVerboseLoggingEnabled) { 1736 Log.v(TAG, "Ignore update network selection status " 1737 + "since Watchdog trigger is activated"); 1738 } 1739 return false; 1740 } 1741 } 1742 1743 networkStatus.incrementDisableReasonCounter(reason); 1744 // For network disable reasons, we should only update the status if we cross the 1745 // threshold. 1746 int disableReasonCounter = networkStatus.getDisableReasonCounter(reason); 1747 int disableReasonThreshold = getNetworkSelectionDisableThreshold(reason); 1748 if (disableReasonCounter < disableReasonThreshold) { 1749 if (mVerboseLoggingEnabled) { 1750 Log.v(TAG, "Disable counter for network " + config.getPrintableSsid() 1751 + " for reason " 1752 + NetworkSelectionStatus.getNetworkSelectionDisableReasonString(reason) 1753 + " is " + networkStatus.getDisableReasonCounter(reason) 1754 + " and threshold is " + disableReasonThreshold); 1755 } 1756 return true; 1757 } 1758 } 1759 return setNetworkSelectionStatus(config, reason); 1760 } 1761 1762 /** 1763 * Update a network's status (both internal and public) according to the update reason and 1764 * its current state. 1765 * 1766 * Each network has 2 status: 1767 * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used 1768 * for temporarily disabling a network for Network Selector. 1769 * 2. Status: This is the exposed status for a network. This is mostly set by 1770 * the public API's {@link WifiManager#enableNetwork(int, boolean)} & 1771 * {@link WifiManager#disableNetwork(int)}. 1772 * 1773 * @param networkId network ID of the network that needs the update. 1774 * @param reason reason to update the network. 1775 * @return true if the input configuration has been updated, false otherwise. 1776 */ updateNetworkSelectionStatus(int networkId, int reason)1777 public boolean updateNetworkSelectionStatus(int networkId, int reason) { 1778 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1779 if (config == null) { 1780 return false; 1781 } 1782 return updateNetworkSelectionStatus(config, reason); 1783 } 1784 1785 /** 1786 * Attempt to re-enable a network for network selection, if this network was either: 1787 * a) Previously temporarily disabled, but its disable timeout has expired, or 1788 * b) Previously disabled because of a user switch, but is now visible to the current 1789 * user. 1790 * 1791 * @param config configuration for the network to be re-enabled for network selection. The 1792 * network corresponding to the config must be visible to the current user. 1793 * @return true if the network identified by {@param config} was re-enabled for qualified 1794 * network selection, false otherwise. 1795 */ tryEnableNetwork(WifiConfiguration config)1796 private boolean tryEnableNetwork(WifiConfiguration config) { 1797 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1798 if (networkStatus.isNetworkTemporaryDisabled()) { 1799 long timeDifferenceMs = 1800 mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime(); 1801 int disableReason = networkStatus.getNetworkSelectionDisableReason(); 1802 int blockedBssids = Math.min(MAX_BLOCKED_BSSID_PER_NETWORK, 1803 mWifiInjector.getBssidBlocklistMonitor() 1804 .getNumBlockedBssidsForSsid(config.SSID)); 1805 // if no BSSIDs are blocked then we should keep trying to connect to something 1806 long disableTimeoutMs = 0; 1807 if (blockedBssids > 0) { 1808 double multiplier = Math.pow(2.0, blockedBssids - 1.0); 1809 disableTimeoutMs = (long) (getNetworkSelectionDisableTimeoutMillis(disableReason) 1810 * multiplier); 1811 } 1812 if (timeDifferenceMs >= disableTimeoutMs) { 1813 return updateNetworkSelectionStatus( 1814 config, NetworkSelectionStatus.DISABLED_NONE); 1815 } 1816 } 1817 return false; 1818 } 1819 1820 /** 1821 * Attempt to re-enable a network for network selection, if this network was either: 1822 * a) Previously temporarily disabled, but its disable timeout has expired, or 1823 * b) Previously disabled because of a user switch, but is now visible to the current 1824 * user. 1825 * 1826 * @param networkId the id of the network to be checked for possible unblock (due to timeout) 1827 * @return true if the network identified by {@param networkId} was re-enabled for qualified 1828 * network selection, false otherwise. 1829 */ tryEnableNetwork(int networkId)1830 public boolean tryEnableNetwork(int networkId) { 1831 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1832 if (config == null) { 1833 return false; 1834 } 1835 return tryEnableNetwork(config); 1836 } 1837 1838 /** 1839 * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API. 1840 * 1841 * @param networkId network ID of the network that needs the update. 1842 * @param disableOthers Whether to disable all other networks or not. This is used to indicate 1843 * that the app requested connection to a specific network. 1844 * @param uid uid of the app requesting the update. 1845 * @return true if it succeeds, false otherwise 1846 */ enableNetwork(int networkId, boolean disableOthers, int uid, String packageName)1847 public boolean enableNetwork(int networkId, boolean disableOthers, int uid, 1848 String packageName) { 1849 if (mVerboseLoggingEnabled) { 1850 Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")"); 1851 } 1852 if (!doesUidBelongToCurrentUser(uid)) { 1853 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1854 return false; 1855 } 1856 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1857 if (config == null) { 1858 return false; 1859 } 1860 // Set the "last selected" flag even if the app does not have permissions to modify this 1861 // network config. Apps are allowed to connect to networks even if they don't have 1862 // permission to modify it. 1863 if (disableOthers) { 1864 setLastSelectedNetwork(networkId); 1865 } 1866 if (!canModifyNetwork(config, uid, packageName)) { 1867 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1868 + config.getKey()); 1869 return false; 1870 } 1871 if (!updateNetworkSelectionStatus( 1872 networkId, WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)) { 1873 return false; 1874 } 1875 saveToStore(true); 1876 return true; 1877 } 1878 1879 /** 1880 * Disable a network using the public {@link WifiManager#disableNetwork(int)} API. 1881 * 1882 * @param networkId network ID of the network that needs the update. 1883 * @param uid uid of the app requesting the update. 1884 * @return true if it succeeds, false otherwise 1885 */ disableNetwork(int networkId, int uid, String packageName)1886 public boolean disableNetwork(int networkId, int uid, String packageName) { 1887 if (mVerboseLoggingEnabled) { 1888 Log.v(TAG, "Disabling network " + networkId); 1889 } 1890 if (!doesUidBelongToCurrentUser(uid)) { 1891 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1892 return false; 1893 } 1894 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1895 if (config == null) { 1896 return false; 1897 } 1898 // Reset the "last selected" flag even if the app does not have permissions to modify this 1899 // network config. 1900 if (networkId == mLastSelectedNetworkId) { 1901 clearLastSelectedNetwork(); 1902 } 1903 if (!canModifyNetwork(config, uid, packageName)) { 1904 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1905 + config.getKey()); 1906 return false; 1907 } 1908 if (!updateNetworkSelectionStatus( 1909 networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) { 1910 return false; 1911 } 1912 saveToStore(true); 1913 return true; 1914 } 1915 1916 /** 1917 * Changes the user's choice to allow auto-join using the 1918 * {@link WifiManager#allowAutojoin(int, boolean)} API. 1919 * 1920 * @param networkId network ID of the network that needs the update. 1921 * @param choice the choice to allow auto-join or not 1922 * @return true if it succeeds, false otherwise 1923 */ allowAutojoin(int networkId, boolean choice)1924 public boolean allowAutojoin(int networkId, boolean choice) { 1925 if (mVerboseLoggingEnabled) { 1926 Log.v(TAG, "Setting allowAutojoin to " + choice + " for netId " + networkId); 1927 } 1928 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1929 if (config == null) { 1930 Log.e(TAG, "allowAutojoin: Supplied networkId " + networkId 1931 + " has no matching config"); 1932 return false; 1933 } 1934 1935 config.allowAutojoin = choice; 1936 if (!choice) { 1937 removeConnectChoiceFromAllNetworks(config.getKey()); 1938 clearNetworkConnectChoice(config.networkId); 1939 } 1940 sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1941 if (!config.ephemeral) { 1942 saveToStore(true); 1943 } 1944 return true; 1945 } 1946 1947 /** 1948 * Updates the last connected UID for the provided configuration. 1949 * 1950 * @param networkId network ID corresponding to the network. 1951 * @param uid uid of the app requesting the connection. 1952 * @return true if the network was found, false otherwise. 1953 */ updateLastConnectUid(int networkId, int uid)1954 public boolean updateLastConnectUid(int networkId, int uid) { 1955 if (mVerboseLoggingEnabled) { 1956 Log.v(TAG, "Update network last connect UID for " + networkId); 1957 } 1958 if (!doesUidBelongToCurrentUser(uid)) { 1959 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1960 return false; 1961 } 1962 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1963 if (config == null) { 1964 return false; 1965 } 1966 config.lastConnectUid = uid; 1967 return true; 1968 } 1969 1970 /** 1971 * Updates a network configuration after a successful connection to it. 1972 * 1973 * This method updates the following WifiConfiguration elements: 1974 * 1. Set the |lastConnected| timestamp. 1975 * 2. Increment |numAssociation| counter. 1976 * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|. 1977 * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|. 1978 * 5. Sets the status of network as |CURRENT|. 1979 * 1980 * @param networkId network ID corresponding to the network. 1981 * @return true if the network was found, false otherwise. 1982 */ updateNetworkAfterConnect(int networkId)1983 public boolean updateNetworkAfterConnect(int networkId) { 1984 if (mVerboseLoggingEnabled) { 1985 Log.v(TAG, "Update network after connect for " + networkId); 1986 } 1987 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1988 if (config == null) { 1989 return false; 1990 } 1991 1992 // Only record connection order for non-passpoint from user saved or suggestion. 1993 if (!config.isPasspoint() && (config.fromWifiNetworkSuggestion || !config.ephemeral)) { 1994 mLruConnectionTracker.addNetwork(config); 1995 } 1996 config.lastConnected = mClock.getWallClockMillis(); 1997 config.numAssociation++; 1998 config.getNetworkSelectionStatus().clearDisableReasonCounter(); 1999 config.getNetworkSelectionStatus().setHasEverConnected(true); 2000 setNetworkStatus(config, WifiConfiguration.Status.CURRENT); 2001 saveToStore(false); 2002 return true; 2003 } 2004 2005 /** 2006 * Updates a network configuration after disconnection from it. 2007 * 2008 * This method updates the following WifiConfiguration elements: 2009 * 1. Set the |lastDisConnected| timestamp. 2010 * 2. Sets the status of network back to |ENABLED|. 2011 * 2012 * @param networkId network ID corresponding to the network. 2013 * @return true if the network was found, false otherwise. 2014 */ updateNetworkAfterDisconnect(int networkId)2015 public boolean updateNetworkAfterDisconnect(int networkId) { 2016 if (mVerboseLoggingEnabled) { 2017 Log.v(TAG, "Update network after disconnect for " + networkId); 2018 } 2019 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2020 if (config == null) { 2021 return false; 2022 } 2023 config.lastDisconnected = mClock.getWallClockMillis(); 2024 config.randomizedMacExpirationTimeMs = Math.max(config.randomizedMacExpirationTimeMs, 2025 config.lastDisconnected + AGGRESSIVE_MAC_WAIT_AFTER_DISCONNECT_MS); 2026 // If the network hasn't been disabled, mark it back as 2027 // enabled after disconnection. 2028 if (config.status == WifiConfiguration.Status.CURRENT) { 2029 setNetworkStatus(config, WifiConfiguration.Status.ENABLED); 2030 } 2031 saveToStore(false); 2032 return true; 2033 } 2034 2035 /** 2036 * Set default GW MAC address for the provided network. 2037 * 2038 * @param networkId network ID corresponding to the network. 2039 * @param macAddress MAC address of the gateway to be set. 2040 * @return true if the network was found, false otherwise. 2041 */ setNetworkDefaultGwMacAddress(int networkId, String macAddress)2042 public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) { 2043 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2044 if (config == null) { 2045 return false; 2046 } 2047 config.defaultGwMacAddress = macAddress; 2048 return true; 2049 } 2050 2051 /** 2052 * Clear the {@link NetworkSelectionStatus#mCandidate}, 2053 * {@link NetworkSelectionStatus#mCandidateScore} & 2054 * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. 2055 * 2056 * This is invoked by Network Selector at the start of every selection procedure to clear all 2057 * configured networks' scan-result-candidates. 2058 * 2059 * @param networkId network ID corresponding to the network. 2060 * @return true if the network was found, false otherwise. 2061 */ clearNetworkCandidateScanResult(int networkId)2062 public boolean clearNetworkCandidateScanResult(int networkId) { 2063 if (mVerboseLoggingEnabled) { 2064 Log.v(TAG, "Clear network candidate scan result for " + networkId); 2065 } 2066 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2067 if (config == null) { 2068 return false; 2069 } 2070 config.getNetworkSelectionStatus().setCandidate(null); 2071 config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE); 2072 config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false); 2073 return true; 2074 } 2075 2076 /** 2077 * Set the {@link NetworkSelectionStatus#mCandidate}, 2078 * {@link NetworkSelectionStatus#mCandidateScore} & 2079 * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. 2080 * 2081 * This is invoked by Network Selector when it sees a network during network selection procedure 2082 * to set the scan result candidate. 2083 * 2084 * @param networkId network ID corresponding to the network. 2085 * @param scanResult Candidate ScanResult associated with this network. 2086 * @param score Score assigned to the candidate. 2087 * @return true if the network was found, false otherwise. 2088 */ setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score)2089 public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) { 2090 if (mVerboseLoggingEnabled) { 2091 Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId); 2092 } 2093 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2094 if (config == null) { 2095 Log.e(TAG, "Cannot find network for " + networkId); 2096 return false; 2097 } 2098 config.getNetworkSelectionStatus().setCandidate(scanResult); 2099 config.getNetworkSelectionStatus().setCandidateScore(score); 2100 config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true); 2101 return true; 2102 } 2103 2104 /** 2105 * Iterate through all the saved networks and remove the provided configuration from the 2106 * {@link NetworkSelectionStatus#mConnectChoice} from them. 2107 * 2108 * This is invoked when a network is removed from our records. 2109 * 2110 * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed. 2111 */ removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey)2112 private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) { 2113 if (mVerboseLoggingEnabled) { 2114 Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey); 2115 } 2116 if (connectChoiceConfigKey == null) { 2117 return; 2118 } 2119 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2120 WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 2121 String connectChoice = status.getConnectChoice(); 2122 if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) { 2123 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID 2124 + " : " + config.networkId); 2125 clearNetworkConnectChoice(config.networkId); 2126 } 2127 } 2128 } 2129 2130 /** 2131 * Clear the {@link NetworkSelectionStatus#mConnectChoice} for the provided network. 2132 * 2133 * @param networkId network ID corresponding to the network. 2134 * @return true if the network was found, false otherwise. 2135 */ clearNetworkConnectChoice(int networkId)2136 public boolean clearNetworkConnectChoice(int networkId) { 2137 if (mVerboseLoggingEnabled) { 2138 Log.v(TAG, "Clear network connect choice for " + networkId); 2139 } 2140 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2141 if (config == null) { 2142 return false; 2143 } 2144 config.getNetworkSelectionStatus().setConnectChoice(null); 2145 saveToStore(false); 2146 return true; 2147 } 2148 2149 /** 2150 * Set the {@link NetworkSelectionStatus#mConnectChoice} for the provided network. 2151 * 2152 * This is invoked by Network Selector when the user overrides the currently connected network 2153 * choice. 2154 * 2155 * @param networkId network ID corresponding to the network. 2156 * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over 2157 * this network. 2158 * @param timestamp timestamp at which the choice was made. 2159 * @return true if the network was found, false otherwise. 2160 */ setNetworkConnectChoice( int networkId, String connectChoiceConfigKey)2161 public boolean setNetworkConnectChoice( 2162 int networkId, String connectChoiceConfigKey) { 2163 if (mVerboseLoggingEnabled) { 2164 Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId); 2165 } 2166 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2167 if (config == null) { 2168 return false; 2169 } 2170 config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey); 2171 saveToStore(false); 2172 return true; 2173 } 2174 2175 /** 2176 * Increments the number of no internet access reports in the provided network. 2177 * 2178 * @param networkId network ID corresponding to the network. 2179 * @return true if the network was found, false otherwise. 2180 */ incrementNetworkNoInternetAccessReports(int networkId)2181 public boolean incrementNetworkNoInternetAccessReports(int networkId) { 2182 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2183 if (config == null) { 2184 return false; 2185 } 2186 config.numNoInternetAccessReports++; 2187 return true; 2188 } 2189 2190 /** 2191 * Sets the internet access is validated or not in the provided network. 2192 * 2193 * @param networkId network ID corresponding to the network. 2194 * @param validated Whether access is validated or not. 2195 * @return true if the network was found, false otherwise. 2196 */ setNetworkValidatedInternetAccess(int networkId, boolean validated)2197 public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) { 2198 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2199 if (config == null) { 2200 return false; 2201 } 2202 config.validatedInternetAccess = validated; 2203 config.numNoInternetAccessReports = 0; 2204 saveToStore(false); 2205 return true; 2206 } 2207 2208 /** 2209 * Sets whether the internet access is expected or not in the provided network. 2210 * 2211 * @param networkId network ID corresponding to the network. 2212 * @param expected Whether access is expected or not. 2213 * @return true if the network was found, false otherwise. 2214 */ setNetworkNoInternetAccessExpected(int networkId, boolean expected)2215 public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) { 2216 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2217 if (config == null) { 2218 return false; 2219 } 2220 config.noInternetAccessExpected = expected; 2221 return true; 2222 } 2223 2224 /** 2225 * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This 2226 * is done when either the corresponding network is either removed or disabled. 2227 */ clearLastSelectedNetwork()2228 public void clearLastSelectedNetwork() { 2229 if (mVerboseLoggingEnabled) { 2230 Log.v(TAG, "Clearing last selected network"); 2231 } 2232 mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 2233 mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; 2234 } 2235 2236 /** 2237 * Helper method to mark a network as the last selected one by an app/user. This is set 2238 * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set. 2239 * This is used by network selector to assign a special bonus during network selection. 2240 */ setLastSelectedNetwork(int networkId)2241 private void setLastSelectedNetwork(int networkId) { 2242 if (mVerboseLoggingEnabled) { 2243 Log.v(TAG, "Setting last selected network to " + networkId); 2244 } 2245 mLastSelectedNetworkId = networkId; 2246 mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis(); 2247 } 2248 2249 /** 2250 * Retrieve the network Id corresponding to the last network that was explicitly selected by 2251 * an app/user. 2252 * 2253 * @return network Id corresponding to the last selected network. 2254 */ getLastSelectedNetwork()2255 public int getLastSelectedNetwork() { 2256 return mLastSelectedNetworkId; 2257 } 2258 2259 /** 2260 * Retrieve the configKey corresponding to the last network that was explicitly selected by 2261 * an app/user. 2262 * 2263 * @return network Id corresponding to the last selected network. 2264 */ getLastSelectedNetworkConfigKey()2265 public String getLastSelectedNetworkConfigKey() { 2266 if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) { 2267 return ""; 2268 } 2269 WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId); 2270 if (config == null) { 2271 return ""; 2272 } 2273 return config.getKey(); 2274 } 2275 2276 /** 2277 * Retrieve the time stamp at which a network was explicitly selected by an app/user. 2278 * 2279 * @return timestamp in milliseconds from boot when this was set. 2280 */ getLastSelectedTimeStamp()2281 public long getLastSelectedTimeStamp() { 2282 return mLastSelectedTimeStamp; 2283 } 2284 2285 /** 2286 * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided 2287 * network. 2288 * 2289 * @param networkId network ID corresponding to the network. 2290 * @return existing {@link ScanDetailCache} entry if one exists or null. 2291 */ getScanDetailCacheForNetwork(int networkId)2292 public ScanDetailCache getScanDetailCacheForNetwork(int networkId) { 2293 return mScanDetailCaches.get(networkId); 2294 } 2295 2296 /** 2297 * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for 2298 * the provided network. 2299 * 2300 * @param config configuration corresponding to the the network. 2301 * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for 2302 * this network. 2303 */ getOrCreateScanDetailCacheForNetwork(WifiConfiguration config)2304 private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) { 2305 if (config == null) return null; 2306 ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId); 2307 if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { 2308 cache = new ScanDetailCache( 2309 config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE); 2310 mScanDetailCaches.put(config.networkId, cache); 2311 } 2312 return cache; 2313 } 2314 2315 /** 2316 * Saves the provided ScanDetail into the corresponding scan detail cache entry 2317 * {@link #mScanDetailCaches} for the provided network. 2318 * 2319 * @param config configuration corresponding to the the network. 2320 * @param scanDetail new scan detail instance to be saved into the cache. 2321 */ saveToScanDetailCacheForNetwork( WifiConfiguration config, ScanDetail scanDetail)2322 private void saveToScanDetailCacheForNetwork( 2323 WifiConfiguration config, ScanDetail scanDetail) { 2324 ScanResult scanResult = scanDetail.getScanResult(); 2325 2326 WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID); 2327 network.addFrequency(scanResult.frequency); 2328 ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config); 2329 if (scanDetailCache == null) { 2330 Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid()); 2331 return; 2332 } 2333 2334 // Adding a new BSSID 2335 if (config.ephemeral) { 2336 // For an ephemeral Wi-Fi config, the ScanResult should be considered 2337 // untrusted. 2338 scanResult.untrusted = true; 2339 } 2340 2341 // Add the scan detail to this network's scan detail cache. 2342 scanDetailCache.put(scanDetail); 2343 2344 // Since we added a scan result to this configuration, re-attempt linking. 2345 // TODO: Do we really need to do this after every scan result? 2346 attemptNetworkLinking(config); 2347 } 2348 2349 /** 2350 * Retrieves a configured network corresponding to the provided scan detail if one exists. 2351 * 2352 * @param scanDetail ScanDetail instance to use for looking up the network. 2353 * @return WifiConfiguration object representing the network corresponding to the scanDetail, 2354 * null if none exists. 2355 * 2356 * TODO (b/142035508): This should only return saved networks (and rename to 2357 * getSavedNetworkForScanDetail()). 2358 */ getConfiguredNetworkForScanDetail(ScanDetail scanDetail)2359 public WifiConfiguration getConfiguredNetworkForScanDetail(ScanDetail scanDetail) { 2360 ScanResult scanResult = scanDetail.getScanResult(); 2361 if (scanResult == null) { 2362 Log.e(TAG, "No scan result found in scan detail"); 2363 return null; 2364 } 2365 WifiConfiguration config = null; 2366 try { 2367 config = mConfiguredNetworks.getByScanResultForCurrentUser(scanResult); 2368 } catch (IllegalArgumentException e) { 2369 Log.e(TAG, "Failed to lookup network from config map", e); 2370 } 2371 if (config != null) { 2372 if (mVerboseLoggingEnabled) { 2373 Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.getKey() 2374 + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]"); 2375 } 2376 } 2377 return config; 2378 } 2379 2380 /** 2381 * Caches the provided |scanDetail| into the corresponding scan detail cache entry 2382 * {@link #mScanDetailCaches} for the retrieved network. 2383 * 2384 * @param scanDetail input a scanDetail from the scan result 2385 * TODO (b/142035508): This should only return saved networks (and rename to 2386 * updateScanDetailCacheFromScanDetail()). 2387 */ updateScanDetailCacheFromScanDetail(ScanDetail scanDetail)2388 public void updateScanDetailCacheFromScanDetail(ScanDetail scanDetail) { 2389 WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail); 2390 if (network == null) { 2391 return; 2392 } 2393 saveToScanDetailCacheForNetwork(network, scanDetail); 2394 } 2395 /** 2396 * Retrieves a configured network corresponding to the provided scan detail if one exists and 2397 * caches the provided |scanDetail| into the corresponding scan detail cache entry 2398 * {@link #mScanDetailCaches} for the retrieved network. 2399 * 2400 * @param scanDetail input a scanDetail from the scan result 2401 * @return WifiConfiguration object representing the network corresponding to the scanDetail, 2402 * null if none exists. 2403 * TODO (b/142035508): This should only return saved networks (and rename to 2404 * getSavedNetworkForScanDetailAndCache()). 2405 */ getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail)2406 public WifiConfiguration getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail) { 2407 WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail); 2408 if (network == null) { 2409 return null; 2410 } 2411 saveToScanDetailCacheForNetwork(network, scanDetail); 2412 // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM) 2413 // Information Element (IE), into the associated WifiConfigurations. Most of the 2414 // time there is no TIM IE in the scan result (Probe Response instead of Beacon 2415 // Frame), these scanResult DTIM's are negative and ignored. 2416 // Used for metrics collection. 2417 if (scanDetail.getNetworkDetail() != null 2418 && scanDetail.getNetworkDetail().getDtimInterval() > 0) { 2419 network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval(); 2420 } 2421 return createExternalWifiConfiguration(network, true, Process.WIFI_UID); 2422 } 2423 2424 /** 2425 * Update the scan detail cache associated with current connected network with latest 2426 * RSSI value in the provided WifiInfo. 2427 * This is invoked when we get an RSSI poll update after connection. 2428 * 2429 * @param info WifiInfo instance pointing to the current connected network. 2430 */ updateScanDetailCacheFromWifiInfo(WifiInfo info)2431 public void updateScanDetailCacheFromWifiInfo(WifiInfo info) { 2432 WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId()); 2433 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId()); 2434 if (config != null && scanDetailCache != null) { 2435 ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID()); 2436 if (scanDetail != null) { 2437 ScanResult result = scanDetail.getScanResult(); 2438 long previousSeen = result.seen; 2439 int previousRssi = result.level; 2440 // Update the scan result 2441 scanDetail.setSeen(); 2442 result.level = info.getRssi(); 2443 // Average the RSSI value 2444 long maxAge = SCAN_RESULT_MAXIMUM_AGE_MS; 2445 long age = result.seen - previousSeen; 2446 if (previousSeen > 0 && age > 0 && age < maxAge / 2) { 2447 // Average the RSSI with previously seen instances of this scan result 2448 double alpha = 0.5 - (double) age / (double) maxAge; 2449 result.level = (int) ((double) result.level * (1 - alpha) 2450 + (double) previousRssi * alpha); 2451 } 2452 if (mVerboseLoggingEnabled) { 2453 Log.v(TAG, "Updating scan detail cache freq=" + result.frequency 2454 + " BSSID=" + result.BSSID 2455 + " RSSI=" + result.level 2456 + " for " + config.getKey()); 2457 } 2458 } 2459 } 2460 } 2461 2462 /** 2463 * Save the ScanDetail to the ScanDetailCache of the given network. This is used 2464 * by {@link PasspointNetworkNominator} for caching 2465 * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network. 2466 * 2467 * @param networkId The ID of the network to save ScanDetail to 2468 * @param scanDetail The ScanDetail to cache 2469 */ updateScanDetailForNetwork(int networkId, ScanDetail scanDetail)2470 public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) { 2471 WifiConfiguration network = getInternalConfiguredNetwork(networkId); 2472 if (network == null) { 2473 return; 2474 } 2475 saveToScanDetailCacheForNetwork(network, scanDetail); 2476 } 2477 2478 /** 2479 * Helper method to check if the 2 provided networks can be linked or not. 2480 * Networks are considered for linking if: 2481 * 1. Share the same GW MAC address. 2482 * 2. Scan results for the networks have AP's with MAC address which differ only in the last 2483 * nibble. 2484 * 2485 * @param network1 WifiConfiguration corresponding to network 1. 2486 * @param network2 WifiConfiguration corresponding to network 2. 2487 * @param scanDetailCache1 ScanDetailCache entry for network 1. 2488 * @param scanDetailCache1 ScanDetailCache entry for network 2. 2489 * @return true if the networks should be linked, false if the networks should be unlinked. 2490 */ shouldNetworksBeLinked( WifiConfiguration network1, WifiConfiguration network2, ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2)2491 private boolean shouldNetworksBeLinked( 2492 WifiConfiguration network1, WifiConfiguration network2, 2493 ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) { 2494 // TODO (b/30706406): Link networks only with same passwords if the 2495 // |mOnlyLinkSameCredentialConfigurations| flag is set. 2496 if (mContext.getResources().getBoolean( 2497 R.bool.config_wifi_only_link_same_credential_configurations)) { 2498 if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) { 2499 if (mVerboseLoggingEnabled) { 2500 Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch"); 2501 } 2502 return false; 2503 } 2504 } 2505 if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) { 2506 // If both default GW are known, link only if they are equal 2507 if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) { 2508 if (mVerboseLoggingEnabled) { 2509 Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID 2510 + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress); 2511 } 2512 return true; 2513 } 2514 } else { 2515 // We do not know BOTH default gateways hence we will try to link 2516 // hoping that WifiConfigurations are indeed behind the same gateway. 2517 // once both WifiConfiguration have been tried and thus once both default gateways 2518 // are known we will revisit the choice of linking them. 2519 if (scanDetailCache1 != null && scanDetailCache2 != null) { 2520 for (String abssid : scanDetailCache1.keySet()) { 2521 for (String bbssid : scanDetailCache2.keySet()) { 2522 if (abssid.regionMatches( 2523 true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) { 2524 // If first 16 ASCII characters of BSSID matches, 2525 // we assume this is a DBDC. 2526 if (mVerboseLoggingEnabled) { 2527 Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match " 2528 + network2.SSID + " and " + network1.SSID 2529 + " bssida " + abssid + " bssidb " + bbssid); 2530 } 2531 return true; 2532 } 2533 } 2534 } 2535 } 2536 } 2537 return false; 2538 } 2539 2540 /** 2541 * Helper methods to link 2 networks together. 2542 * 2543 * @param network1 WifiConfiguration corresponding to network 1. 2544 * @param network2 WifiConfiguration corresponding to network 2. 2545 */ linkNetworks(WifiConfiguration network1, WifiConfiguration network2)2546 private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) { 2547 if (mVerboseLoggingEnabled) { 2548 Log.v(TAG, "linkNetworks will link " + network2.getKey() 2549 + " and " + network1.getKey()); 2550 } 2551 if (network2.linkedConfigurations == null) { 2552 network2.linkedConfigurations = new HashMap<>(); 2553 } 2554 if (network1.linkedConfigurations == null) { 2555 network1.linkedConfigurations = new HashMap<>(); 2556 } 2557 // TODO (b/30638473): This needs to become a set instead of map, but it will need 2558 // public interface changes and need some migration of existing store data. 2559 network2.linkedConfigurations.put(network1.getKey(), 1); 2560 network1.linkedConfigurations.put(network2.getKey(), 1); 2561 } 2562 2563 /** 2564 * Helper methods to unlink 2 networks from each other. 2565 * 2566 * @param network1 WifiConfiguration corresponding to network 1. 2567 * @param network2 WifiConfiguration corresponding to network 2. 2568 */ unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2)2569 private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) { 2570 if (network2.linkedConfigurations != null 2571 && (network2.linkedConfigurations.get(network1.getKey()) != null)) { 2572 if (mVerboseLoggingEnabled) { 2573 Log.v(TAG, "unlinkNetworks un-link " + network1.getKey() 2574 + " from " + network2.getKey()); 2575 } 2576 network2.linkedConfigurations.remove(network1.getKey()); 2577 } 2578 if (network1.linkedConfigurations != null 2579 && (network1.linkedConfigurations.get(network2.getKey()) != null)) { 2580 if (mVerboseLoggingEnabled) { 2581 Log.v(TAG, "unlinkNetworks un-link " + network2.getKey() 2582 + " from " + network1.getKey()); 2583 } 2584 network1.linkedConfigurations.remove(network2.getKey()); 2585 } 2586 } 2587 2588 /** 2589 * This method runs through all the saved networks and checks if the provided network can be 2590 * linked with any of them. 2591 * 2592 * @param config WifiConfiguration object corresponding to the network that needs to be 2593 * checked for potential links. 2594 */ attemptNetworkLinking(WifiConfiguration config)2595 private void attemptNetworkLinking(WifiConfiguration config) { 2596 // Only link WPA_PSK config. 2597 if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 2598 return; 2599 } 2600 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId); 2601 // Ignore configurations with large number of BSSIDs. 2602 if (scanDetailCache != null 2603 && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) { 2604 return; 2605 } 2606 for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) { 2607 if (linkConfig.getKey().equals(config.getKey())) { 2608 continue; 2609 } 2610 if (linkConfig.ephemeral) { 2611 continue; 2612 } 2613 // Network Selector will be allowed to dynamically jump from a linked configuration 2614 // to another, hence only link configurations that have WPA_PSK security type. 2615 if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 2616 continue; 2617 } 2618 ScanDetailCache linkScanDetailCache = 2619 getScanDetailCacheForNetwork(linkConfig.networkId); 2620 // Ignore configurations with large number of BSSIDs. 2621 if (linkScanDetailCache != null 2622 && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) { 2623 continue; 2624 } 2625 // Check if the networks should be linked/unlinked. 2626 if (shouldNetworksBeLinked( 2627 config, linkConfig, scanDetailCache, linkScanDetailCache)) { 2628 linkNetworks(config, linkConfig); 2629 } else { 2630 unlinkNetworks(config, linkConfig); 2631 } 2632 } 2633 } 2634 2635 /** 2636 * Retrieves a list of all the saved hidden networks for scans 2637 * 2638 * Hidden network list sent to the firmware has limited size. If there are a lot of saved 2639 * networks, this list will be truncated and we might end up not sending the networks 2640 * with the highest chance of connecting to the firmware. 2641 * So, re-sort the network list based on the frequency of connection to those networks 2642 * and whether it was last seen in the scan results. 2643 * 2644 * @return list of networks in the order of priority. 2645 */ retrieveHiddenNetworkList()2646 public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() { 2647 List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>(); 2648 List<WifiConfiguration> networks = getConfiguredNetworks(); 2649 // Remove any non hidden networks. 2650 networks.removeIf(config -> !config.hiddenSSID); 2651 networks.sort(mScanListComparator); 2652 // The most frequently connected network has the highest priority now. 2653 for (WifiConfiguration config : networks) { 2654 hiddenList.add(new WifiScanner.ScanSettings.HiddenNetwork(config.SSID)); 2655 } 2656 return hiddenList; 2657 } 2658 2659 /** 2660 * Check if the provided network was temporarily disabled by the user and still blocked. 2661 * 2662 * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru 2663 * this API matched the WifiConfiguration.SSID rules, and thus be surrounded by 2664 * quotes. 2665 * @return true if network is blocking, otherwise false. 2666 */ isNetworkTemporarilyDisabledByUser(String network)2667 public boolean isNetworkTemporarilyDisabledByUser(String network) { 2668 if (mUserTemporarilyDisabledList.isLocked(network)) { 2669 return true; 2670 } 2671 mUserTemporarilyDisabledList.remove(network); 2672 return false; 2673 } 2674 2675 /** 2676 * User temporarily disable a network and will be block to auto-join when network is still 2677 * nearby. 2678 * 2679 * The network will be re-enabled when: 2680 * a) User select to connect the network. 2681 * b) The network is not in range for {@link #USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS} 2682 * c) Toggle wifi off, reset network settings or device reboot. 2683 * 2684 * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru 2685 * this API matched the WifiConfiguration.SSID rules, and thus be surrounded by 2686 * quotes. 2687 * uid UID of the calling process. 2688 */ userTemporarilyDisabledNetwork(String network, int uid)2689 public void userTemporarilyDisabledNetwork(String network, int uid) { 2690 mUserTemporarilyDisabledList.add(network, USER_DISCONNECT_NETWORK_BLOCK_EXPIRY_MS); 2691 Log.d(TAG, "Temporarily disable network: " + network + " uid=" + uid + " num=" 2692 + mUserTemporarilyDisabledList.size()); 2693 removeUserChoiceFromDisabledNetwork(network, uid); 2694 } 2695 2696 /** 2697 * Update the user temporarily disabled network list with networks in range. 2698 * @param networks networks in range in String format, FQDN or SSID. And caller must ensure 2699 * that the SSID passed thru this API matched the WifiConfiguration.SSID rules, 2700 * and thus be surrounded by quotes. 2701 */ updateUserDisabledList(List<String> networks)2702 public void updateUserDisabledList(List<String> networks) { 2703 mUserTemporarilyDisabledList.update(new HashSet<>(networks)); 2704 } 2705 removeUserChoiceFromDisabledNetwork( @onNull String network, int uid)2706 private void removeUserChoiceFromDisabledNetwork( 2707 @NonNull String network, int uid) { 2708 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2709 if (TextUtils.equals(config.SSID, network) || TextUtils.equals(config.FQDN, network)) { 2710 if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { 2711 mWifiInjector.getWifiMetrics().logUserActionEvent( 2712 UserActionEvent.EVENT_DISCONNECT_WIFI, config.networkId); 2713 } 2714 removeConnectChoiceFromAllNetworks(config.getKey()); 2715 } 2716 } 2717 } 2718 2719 /** 2720 * User enabled network manually, maybe trigger by user select to connect network. 2721 * @param networkId enabled network id. 2722 */ userEnabledNetwork(int networkId)2723 public void userEnabledNetwork(int networkId) { 2724 WifiConfiguration configuration = getInternalConfiguredNetwork(networkId); 2725 if (configuration == null) { 2726 return; 2727 } 2728 String network; 2729 if (configuration.isPasspoint()) { 2730 network = configuration.FQDN; 2731 } else { 2732 network = configuration.SSID; 2733 } 2734 mUserTemporarilyDisabledList.remove(network); 2735 mWifiInjector.getBssidBlocklistMonitor().clearBssidBlocklistForSsid(configuration.SSID); 2736 Log.d(TAG, "Enable disabled network: " + network + " num=" 2737 + mUserTemporarilyDisabledList.size()); 2738 } 2739 2740 /** 2741 * Clear all user temporarily disabled networks. 2742 */ clearUserTemporarilyDisabledList()2743 public void clearUserTemporarilyDisabledList() { 2744 mUserTemporarilyDisabledList.clear(); 2745 } 2746 2747 /** 2748 * Resets all sim networks state. 2749 */ resetSimNetworks()2750 public void resetSimNetworks() { 2751 if (mVerboseLoggingEnabled) localLog("resetSimNetworks"); 2752 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2753 if (config.enterpriseConfig == null 2754 || !config.enterpriseConfig.isAuthenticationSimBased()) { 2755 continue; 2756 } 2757 if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) { 2758 Pair<String, String> currentIdentity = 2759 mWifiCarrierInfoManager.getSimIdentity(config); 2760 if (mVerboseLoggingEnabled) { 2761 Log.d(TAG, "New identity for config " + config + ": " + currentIdentity); 2762 } 2763 // Update the loaded config 2764 if (currentIdentity == null) { 2765 Log.d(TAG, "Identity is null"); 2766 } else { 2767 config.enterpriseConfig.setIdentity(currentIdentity.first); 2768 } 2769 // do not reset anonymous identity since it may be dependent on user-entry 2770 // (i.e. cannot re-request on every reboot/SIM re-entry) 2771 } else { 2772 // reset identity as well: supplicant will ask us for it 2773 config.enterpriseConfig.setIdentity(""); 2774 if (!WifiCarrierInfoManager.isAnonymousAtRealmIdentity( 2775 config.enterpriseConfig.getAnonymousIdentity())) { 2776 config.enterpriseConfig.setAnonymousIdentity(""); 2777 } 2778 } 2779 } 2780 } 2781 2782 /** 2783 * Helper method to perform the following operations during user switch/unlock: 2784 * - Remove private networks of the old user. 2785 * - Load from the new user store file. 2786 * - Save the store files again to migrate any user specific networks from the shared store 2787 * to user store. 2788 * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller 2789 * should ensure that the stores are accessible before invocation. 2790 * 2791 * @param userId The identifier of the new foreground user, after the unlock or switch. 2792 */ handleUserUnlockOrSwitch(int userId)2793 private void handleUserUnlockOrSwitch(int userId) { 2794 if (mVerboseLoggingEnabled) { 2795 Log.v(TAG, "Loading from store after user switch/unlock for " + userId); 2796 } 2797 // Switch out the user store file. 2798 if (loadFromUserStoreAfterUnlockOrSwitch(userId)) { 2799 saveToStore(true); 2800 mPendingUnlockStoreRead = false; 2801 } 2802 } 2803 2804 /** 2805 * Handles the switch to a different foreground user: 2806 * - Flush the current state to the old user's store file. 2807 * - Switch the user specific store file. 2808 * - Reload the networks from the store files (shared & user). 2809 * - Write the store files to move any user specific private networks from shared store to user 2810 * store. 2811 * 2812 * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked. 2813 * 2814 * @param userId The identifier of the new foreground user, after the switch. 2815 * @return List of network ID's of all the private networks of the old user which will be 2816 * removed from memory. 2817 */ handleUserSwitch(int userId)2818 public Set<Integer> handleUserSwitch(int userId) { 2819 if (mVerboseLoggingEnabled) { 2820 Log.v(TAG, "Handling user switch for " + userId); 2821 } 2822 if (userId == mCurrentUserId) { 2823 Log.w(TAG, "User already in foreground " + userId); 2824 return new HashSet<>(); 2825 } 2826 if (mPendingStoreRead) { 2827 Log.w(TAG, "User switch before store is read!"); 2828 mConfiguredNetworks.setNewUser(userId); 2829 mCurrentUserId = userId; 2830 // Reset any state from previous user unlock. 2831 mDeferredUserUnlockRead = false; 2832 // Cannot read data from new user's CE store file before they log-in. 2833 mPendingUnlockStoreRead = true; 2834 return new HashSet<>(); 2835 } 2836 if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) { 2837 saveToStore(true); 2838 } 2839 // Remove any private networks of the old user before switching the userId. 2840 Set<Integer> removedNetworkIds = clearInternalDataForCurrentUser(); 2841 mConfiguredNetworks.setNewUser(userId); 2842 mCurrentUserId = userId; 2843 2844 if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) { 2845 handleUserUnlockOrSwitch(mCurrentUserId); 2846 } else { 2847 // Cannot read data from new user's CE store file before they log-in. 2848 mPendingUnlockStoreRead = true; 2849 Log.i(TAG, "Waiting for user unlock to load from store"); 2850 } 2851 return removedNetworkIds; 2852 } 2853 2854 /** 2855 * Handles the unlock of foreground user. This maybe needed to read the store file if the user's 2856 * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked. 2857 * 2858 * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked. 2859 * 2860 * @param userId The identifier of the user that unlocked. 2861 */ handleUserUnlock(int userId)2862 public void handleUserUnlock(int userId) { 2863 if (mVerboseLoggingEnabled) { 2864 Log.v(TAG, "Handling user unlock for " + userId); 2865 } 2866 if (userId != mCurrentUserId) { 2867 Log.e(TAG, "Ignore user unlock for non current user " + userId); 2868 return; 2869 } 2870 if (mPendingStoreRead) { 2871 Log.w(TAG, "Ignore user unlock until store is read!"); 2872 mDeferredUserUnlockRead = true; 2873 return; 2874 } 2875 if (mPendingUnlockStoreRead) { 2876 handleUserUnlockOrSwitch(mCurrentUserId); 2877 } 2878 } 2879 2880 /** 2881 * Handles the stop of foreground user. This is needed to write the store file to flush 2882 * out any pending data before the user's CE store storage is unavailable. 2883 * 2884 * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked. 2885 * 2886 * @param userId The identifier of the user that stopped. 2887 */ handleUserStop(int userId)2888 public void handleUserStop(int userId) { 2889 if (mVerboseLoggingEnabled) { 2890 Log.v(TAG, "Handling user stop for " + userId); 2891 } 2892 if (userId == mCurrentUserId 2893 && mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) { 2894 saveToStore(true); 2895 clearInternalDataForCurrentUser(); 2896 } 2897 } 2898 2899 /** 2900 * Helper method to clear internal databases. 2901 * This method clears the: 2902 * - List of configured networks. 2903 * - Map of scan detail caches. 2904 * - List of deleted ephemeral networks. 2905 */ clearInternalData()2906 private void clearInternalData() { 2907 localLog("clearInternalData: Clearing all internal data"); 2908 mConfiguredNetworks.clear(); 2909 mUserTemporarilyDisabledList.clear(); 2910 mRandomizedMacAddressMapping.clear(); 2911 mScanDetailCaches.clear(); 2912 clearLastSelectedNetwork(); 2913 } 2914 2915 /** 2916 * Helper method to clear internal databases of the specified user. 2917 * This method clears the: 2918 * - Private configured configured networks of the specified user. 2919 * - Map of scan detail caches. 2920 * - List of deleted ephemeral networks. 2921 * 2922 * @return List of network ID's of all the private networks of the old user which will be 2923 * removed from memory. 2924 */ clearInternalDataForCurrentUser()2925 private Set<Integer> clearInternalDataForCurrentUser() { 2926 localLog("clearInternalUserData: Clearing user internal data for " + mCurrentUserId); 2927 Set<Integer> removedNetworkIds = new HashSet<>(); 2928 // Remove any private networks of the old user before switching the userId. 2929 for (WifiConfiguration config : getConfiguredNetworks()) { 2930 if (!config.shared && doesUidBelongToCurrentUser(config.creatorUid)) { 2931 removedNetworkIds.add(config.networkId); 2932 localLog("clearInternalUserData: removed config." 2933 + " netId=" + config.networkId 2934 + " configKey=" + config.getKey()); 2935 mConfiguredNetworks.remove(config.networkId); 2936 } 2937 } 2938 mUserTemporarilyDisabledList.clear(); 2939 mScanDetailCaches.clear(); 2940 clearLastSelectedNetwork(); 2941 return removedNetworkIds; 2942 } 2943 2944 /** 2945 * Helper function to populate the internal (in-memory) data from the retrieved shared store 2946 * (file) data. 2947 * 2948 * @param configurations list of configurations retrieved from store. 2949 */ loadInternalDataFromSharedStore( List<WifiConfiguration> configurations, Map<String, String> macAddressMapping)2950 private void loadInternalDataFromSharedStore( 2951 List<WifiConfiguration> configurations, 2952 Map<String, String> macAddressMapping) { 2953 for (WifiConfiguration configuration : configurations) { 2954 configuration.networkId = mNextNetworkId++; 2955 if (mVerboseLoggingEnabled) { 2956 Log.v(TAG, "Adding network from shared store " + configuration.getKey()); 2957 } 2958 try { 2959 mConfiguredNetworks.put(configuration); 2960 } catch (IllegalArgumentException e) { 2961 Log.e(TAG, "Failed to add network to config map", e); 2962 } 2963 } 2964 mRandomizedMacAddressMapping.putAll(macAddressMapping); 2965 } 2966 2967 /** 2968 * Helper function to populate the internal (in-memory) data from the retrieved user store 2969 * (file) data. 2970 * 2971 * @param configurations list of configurations retrieved from store. 2972 */ loadInternalDataFromUserStore(List<WifiConfiguration> configurations)2973 private void loadInternalDataFromUserStore(List<WifiConfiguration> configurations) { 2974 for (WifiConfiguration configuration : configurations) { 2975 configuration.networkId = mNextNetworkId++; 2976 if (mVerboseLoggingEnabled) { 2977 Log.v(TAG, "Adding network from user store " + configuration.getKey()); 2978 } 2979 try { 2980 mConfiguredNetworks.put(configuration); 2981 } catch (IllegalArgumentException e) { 2982 Log.e(TAG, "Failed to add network to config map", e); 2983 } 2984 2985 if (configuration.isMostRecentlyConnected) { 2986 mLruConnectionTracker.addNetwork(configuration); 2987 } 2988 } 2989 } 2990 2991 /** 2992 * Initializes the randomized MAC address for an internal WifiConfiguration depending on 2993 * whether it should use aggressive randomization. 2994 * @param config 2995 */ initRandomizedMacForInternalConfig(WifiConfiguration internalConfig)2996 private void initRandomizedMacForInternalConfig(WifiConfiguration internalConfig) { 2997 MacAddress randomizedMac = shouldUseAggressiveRandomization(internalConfig) 2998 ? MacAddressUtils.createRandomUnicastAddress() 2999 : getPersistentMacAddress(internalConfig); 3000 if (randomizedMac != null) { 3001 internalConfig.setRandomizedMacAddress(randomizedMac); 3002 } 3003 } 3004 3005 /** 3006 * Assign randomized MAC addresses for configured networks. 3007 * This is needed to generate persistent randomized MAC address for existing networks when 3008 * a device updates to Q+ for the first time since we are not calling addOrUpdateNetwork when 3009 * we load configuration at boot. 3010 */ generateRandomizedMacAddresses()3011 private void generateRandomizedMacAddresses() { 3012 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 3013 if (DEFAULT_MAC_ADDRESS.equals(config.getRandomizedMacAddress())) { 3014 initRandomizedMacForInternalConfig(config); 3015 } 3016 } 3017 } 3018 3019 /** 3020 * Helper function to populate the internal (in-memory) data from the retrieved stores (file) 3021 * data. 3022 * This method: 3023 * 1. Clears all existing internal data. 3024 * 2. Sends out the networks changed broadcast after loading all the data. 3025 * 3026 * @param sharedConfigurations list of network configurations retrieved from shared store. 3027 * @param userConfigurations list of network configurations retrieved from user store. 3028 * @param macAddressMapping 3029 */ loadInternalData( List<WifiConfiguration> sharedConfigurations, List<WifiConfiguration> userConfigurations, Map<String, String> macAddressMapping)3030 private void loadInternalData( 3031 List<WifiConfiguration> sharedConfigurations, 3032 List<WifiConfiguration> userConfigurations, 3033 Map<String, String> macAddressMapping) { 3034 // Clear out all the existing in-memory lists and load the lists from what was retrieved 3035 // from the config store. 3036 clearInternalData(); 3037 loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping); 3038 loadInternalDataFromUserStore(userConfigurations); 3039 generateRandomizedMacAddresses(); 3040 if (mConfiguredNetworks.sizeForAllUsers() == 0) { 3041 Log.w(TAG, "No stored networks found."); 3042 } 3043 // reset identity & anonymous identity for networks using SIM-based authentication 3044 // on load (i.e. boot) so that if the user changed SIMs while the device was powered off, 3045 // we do not reuse stale credentials that would lead to authentication failure. 3046 resetSimNetworks(); 3047 sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_ADDED); 3048 mPendingStoreRead = false; 3049 } 3050 3051 /** 3052 * Read the config store and load the in-memory lists from the store data retrieved and sends 3053 * out the networks changed broadcast. 3054 * 3055 * This reads all the network configurations from: 3056 * 1. Shared WifiConfigStore.xml 3057 * 2. User WifiConfigStore.xml 3058 * 3059 * @return true on success or not needed (fresh install), false otherwise. 3060 */ loadFromStore()3061 public boolean loadFromStore() { 3062 // If the user unlock comes in before we load from store, which means the user store have 3063 // not been setup yet for the current user. Setup the user store before the read so that 3064 // configurations for the current user will also being loaded. 3065 if (mDeferredUserUnlockRead) { 3066 Log.i(TAG, "Handling user unlock before loading from store."); 3067 List<WifiConfigStore.StoreFile> userStoreFiles = 3068 WifiConfigStore.createUserFiles( 3069 mCurrentUserId, mFrameworkFacade.isNiapModeOn(mContext)); 3070 if (userStoreFiles == null) { 3071 Log.wtf(TAG, "Failed to create user store files"); 3072 return false; 3073 } 3074 mWifiConfigStore.setUserStores(userStoreFiles); 3075 mDeferredUserUnlockRead = false; 3076 } 3077 try { 3078 mWifiConfigStore.read(); 3079 } catch (IOException | IllegalStateException e) { 3080 Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e); 3081 return false; 3082 } catch (XmlPullParserException e) { 3083 Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e); 3084 return false; 3085 } 3086 loadInternalData(mNetworkListSharedStoreData.getConfigurations(), 3087 mNetworkListUserStoreData.getConfigurations(), 3088 mRandomizedMacStoreData.getMacMapping()); 3089 return true; 3090 } 3091 3092 /** 3093 * Read the user config store and load the in-memory lists from the store data retrieved and 3094 * sends out the networks changed broadcast. 3095 * This should be used for all user switches/unlocks to only load networks from the user 3096 * specific store and avoid reloading the shared networks. 3097 * 3098 * This reads all the network configurations from: 3099 * 1. User WifiConfigStore.xml 3100 * 3101 * @param userId The identifier of the foreground user. 3102 * @return true on success, false otherwise. 3103 */ loadFromUserStoreAfterUnlockOrSwitch(int userId)3104 private boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) { 3105 try { 3106 List<WifiConfigStore.StoreFile> userStoreFiles = 3107 WifiConfigStore.createUserFiles( 3108 userId, mFrameworkFacade.isNiapModeOn(mContext)); 3109 if (userStoreFiles == null) { 3110 Log.e(TAG, "Failed to create user store files"); 3111 return false; 3112 } 3113 mWifiConfigStore.switchUserStoresAndRead(userStoreFiles); 3114 } catch (IOException | IllegalStateException e) { 3115 Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e); 3116 return false; 3117 } catch (XmlPullParserException e) { 3118 Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are " 3119 + "lost!", e); 3120 return false; 3121 } 3122 loadInternalDataFromUserStore(mNetworkListUserStoreData.getConfigurations()); 3123 return true; 3124 } 3125 3126 /** 3127 * Save the current snapshot of the in-memory lists to the config store. 3128 * 3129 * @param forceWrite Whether the write needs to be forced or not. 3130 * @return Whether the write was successful or not, this is applicable only for force writes. 3131 */ saveToStore(boolean forceWrite)3132 public boolean saveToStore(boolean forceWrite) { 3133 if (mPendingStoreRead) { 3134 Log.e(TAG, "Cannot save to store before store is read!"); 3135 return false; 3136 } 3137 ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>(); 3138 ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>(); 3139 // List of network IDs for legacy Passpoint configuration to be removed. 3140 List<Integer> legacyPasspointNetId = new ArrayList<>(); 3141 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 3142 // Ignore ephemeral networks and non-legacy Passpoint configurations. 3143 if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) { 3144 continue; 3145 } 3146 3147 // Migrate the legacy Passpoint configurations owned by the current user to 3148 // {@link PasspointManager}. 3149 if (config.isLegacyPasspointConfig && doesUidBelongToCurrentUser(config.creatorUid)) { 3150 legacyPasspointNetId.add(config.networkId); 3151 // Migrate the legacy Passpoint configuration and add it to PasspointManager. 3152 if (!PasspointManager.addLegacyPasspointConfig(config)) { 3153 Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN); 3154 } 3155 // This will prevent adding |config| to the |sharedConfigurations|. 3156 continue; 3157 } 3158 3159 config.isMostRecentlyConnected = 3160 mLruConnectionTracker.isMostRecentlyConnected(config); 3161 3162 // We push all shared networks & private networks not belonging to the current 3163 // user to the shared store. Ideally, private networks for other users should 3164 // not even be in memory, 3165 // But, this logic is in place to deal with store migration from N to O 3166 // because all networks were previously stored in a central file. We cannot 3167 // write these private networks to the user specific store until the corresponding 3168 // user logs in. 3169 if (config.shared || !doesUidBelongToCurrentUser(config.creatorUid)) { 3170 sharedConfigurations.add(config); 3171 } else { 3172 userConfigurations.add(config); 3173 } 3174 } 3175 3176 // Remove the configurations for migrated Passpoint configurations. 3177 for (int networkId : legacyPasspointNetId) { 3178 mConfiguredNetworks.remove(networkId); 3179 } 3180 3181 // Setup store data for write. 3182 mNetworkListSharedStoreData.setConfigurations(sharedConfigurations); 3183 mNetworkListUserStoreData.setConfigurations(userConfigurations); 3184 mRandomizedMacStoreData.setMacMapping(mRandomizedMacAddressMapping); 3185 3186 try { 3187 mWifiConfigStore.write(forceWrite); 3188 } catch (IOException | IllegalStateException e) { 3189 Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e); 3190 return false; 3191 } catch (XmlPullParserException e) { 3192 Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e); 3193 return false; 3194 } 3195 return true; 3196 } 3197 3198 /** 3199 * Helper method for logging into local log buffer. 3200 */ localLog(String s)3201 private void localLog(String s) { 3202 if (mLocalLog != null) { 3203 mLocalLog.log(s); 3204 } 3205 } 3206 3207 /** 3208 * Dump the local log buffer and other internal state of WifiConfigManager. 3209 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)3210 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3211 pw.println("Dump of WifiConfigManager"); 3212 pw.println("WifiConfigManager - Log Begin ----"); 3213 mLocalLog.dump(fd, pw, args); 3214 pw.println("WifiConfigManager - Log End ----"); 3215 pw.println("WifiConfigManager - Configured networks Begin ----"); 3216 for (WifiConfiguration network : getInternalConfiguredNetworks()) { 3217 pw.println(network); 3218 } 3219 pw.println("WifiConfigManager - Configured networks End ----"); 3220 pw.println("WifiConfigManager - ConfigurationMap Begin ----"); 3221 mConfiguredNetworks.dump(fd, pw, args); 3222 pw.println("WifiConfigManager - ConfigurationMap End ----"); 3223 pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId); 3224 pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId); 3225 pw.println("WifiConfigManager - PNO scan frequency culling enabled = " 3226 + mContext.getResources().getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled)); 3227 pw.println("WifiConfigManager - PNO scan recency sorting enabled = " 3228 + mContext.getResources().getBoolean(R.bool.config_wifiPnoRecencySortingEnabled)); 3229 mWifiConfigStore.dump(fd, pw, args); 3230 mWifiCarrierInfoManager.dump(fd, pw, args); 3231 } 3232 3233 /** 3234 * Returns true if the given uid has permission to add, update or remove proxy settings 3235 */ canModifyProxySettings(int uid, String packageName)3236 private boolean canModifyProxySettings(int uid, String packageName) { 3237 final boolean isDeviceOwner = mWifiPermissionsUtil.isDeviceOwner(uid, packageName); 3238 final boolean isProfileOwner = mWifiPermissionsUtil.isProfileOwner(uid, packageName); 3239 final boolean hasNetworkSettingsPermission = 3240 mWifiPermissionsUtil.checkNetworkSettingsPermission(uid); 3241 final boolean hasNetworkSetupWizardPermission = 3242 mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid); 3243 final boolean hasNetworkManagedProvisioningPermission = 3244 mWifiPermissionsUtil.checkNetworkManagedProvisioningPermission(uid); 3245 // If |uid| corresponds to the device owner, allow all modifications. 3246 if (isProfileOwner || isDeviceOwner || hasNetworkSettingsPermission 3247 || hasNetworkSetupWizardPermission || hasNetworkManagedProvisioningPermission) { 3248 return true; 3249 } 3250 if (mVerboseLoggingEnabled) { 3251 Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings." 3252 + " hasNetworkSettings=" + hasNetworkSettingsPermission 3253 + " hasNetworkSetupWizard=" + hasNetworkSetupWizardPermission 3254 + " DeviceOwner=" + isDeviceOwner 3255 + " ProfileOwner=" + isProfileOwner); 3256 } 3257 return false; 3258 } 3259 3260 /** 3261 * Add the network update event listener 3262 */ addOnNetworkUpdateListener(OnNetworkUpdateListener listener)3263 public void addOnNetworkUpdateListener(OnNetworkUpdateListener listener) { 3264 mListeners.add(listener); 3265 } 3266 3267 /** 3268 * Set extra failure reason for given config. Used to surface extra failure details to the UI 3269 * @param netId The network ID of the config to set the extra failure reason for 3270 * @param reason the WifiConfiguration.ExtraFailureReason failure code representing the most 3271 * recent failure reason 3272 */ setRecentFailureAssociationStatus(int netId, int reason)3273 public void setRecentFailureAssociationStatus(int netId, int reason) { 3274 WifiConfiguration config = getInternalConfiguredNetwork(netId); 3275 if (config == null) { 3276 return; 3277 } 3278 config.recentFailure.setAssociationStatus(reason); 3279 } 3280 3281 /** 3282 * @param netId The network ID of the config to clear the extra failure reason from 3283 */ clearRecentFailureReason(int netId)3284 public void clearRecentFailureReason(int netId) { 3285 WifiConfiguration config = getInternalConfiguredNetwork(netId); 3286 if (config == null) { 3287 return; 3288 } 3289 config.recentFailure.clear(); 3290 } 3291 3292 /** 3293 * Find the highest RSSI among all valid scanDetails in current network's scanDetail cache. 3294 * If scanDetail is too old, it is not considered to be valid. 3295 * @param netId The network ID of the config to find scan RSSI 3296 * @params scanRssiValidTimeMs The valid time for scan RSSI 3297 * @return The highest RSSI in dBm found with current network's scanDetail cache. 3298 */ findScanRssi(int netId, int scanRssiValidTimeMs)3299 public int findScanRssi(int netId, int scanRssiValidTimeMs) { 3300 int scanMaxRssi = WifiInfo.INVALID_RSSI; 3301 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(netId); 3302 if (scanDetailCache == null || scanDetailCache.size() == 0) return scanMaxRssi; 3303 long nowInMillis = mClock.getWallClockMillis(); 3304 for (ScanDetail scanDetail : scanDetailCache.values()) { 3305 ScanResult result = scanDetail.getScanResult(); 3306 if (result == null) continue; 3307 boolean valid = (nowInMillis - result.seen) < scanRssiValidTimeMs; 3308 3309 if (valid) { 3310 scanMaxRssi = Math.max(scanMaxRssi, result.level); 3311 } 3312 } 3313 return scanMaxRssi; 3314 } 3315 3316 public Comparator<WifiConfiguration> getScanListComparator() { 3317 return mScanListComparator; 3318 } 3319 } 3320