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