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