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