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