WifiConfigManager.java revision cdd56a55709407c9efe2a24b4ed25666e2e8318e
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 17package com.android.server.wifi; 18 19import android.app.ActivityManager; 20import android.app.admin.DeviceAdminInfo; 21import android.app.admin.DevicePolicyManagerInternal; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.ApplicationInfo; 26import android.content.pm.PackageManager; 27import android.net.IpConfiguration; 28import android.net.ProxyInfo; 29import android.net.StaticIpConfiguration; 30import android.net.wifi.ScanResult; 31import android.net.wifi.WifiConfiguration; 32import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; 33import android.net.wifi.WifiEnterpriseConfig; 34import android.net.wifi.WifiInfo; 35import android.net.wifi.WifiManager; 36import android.net.wifi.WifiScanner; 37import android.os.Process; 38import android.os.UserHandle; 39import android.os.UserManager; 40import android.provider.Settings; 41import android.telephony.TelephonyManager; 42import android.text.TextUtils; 43import android.util.ArraySet; 44import android.util.LocalLog; 45import android.util.Log; 46 47import com.android.internal.R; 48import com.android.internal.annotations.VisibleForTesting; 49import com.android.server.LocalServices; 50import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy; 51import com.android.server.wifi.hotspot2.PasspointManager; 52import com.android.server.wifi.util.ScanResultUtil; 53import com.android.server.wifi.util.TelephonyUtil; 54import com.android.server.wifi.util.WifiPermissionsUtil; 55import com.android.server.wifi.util.WifiPermissionsWrapper; 56 57import org.xmlpull.v1.XmlPullParserException; 58 59import java.io.FileDescriptor; 60import java.io.IOException; 61import java.io.PrintWriter; 62import java.util.ArrayList; 63import java.util.BitSet; 64import java.util.Calendar; 65import java.util.Collection; 66import java.util.Collections; 67import java.util.HashMap; 68import java.util.HashSet; 69import java.util.Iterator; 70import java.util.List; 71import java.util.Map; 72import java.util.Set; 73 74/** 75 * This class provides the APIs to manage configured Wi-Fi networks. 76 * It deals with the following: 77 * - Maintaining a list of configured networks for quick access. 78 * - Persisting the configurations to store when required. 79 * - Supporting WifiManager Public API calls: 80 * > addOrUpdateNetwork() 81 * > removeNetwork() 82 * > enableNetwork() 83 * > disableNetwork() 84 * - Handle user switching on multi-user devices. 85 * 86 * All network configurations retrieved from this class are copies of the original configuration 87 * stored in the internal database. So, any updates to the retrieved configuration object are 88 * meaningless and will not be reflected in the original database. 89 * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored 90 * in the internal database. Any configuration updates should be triggered with appropriate helper 91 * methods of this class using the configuration's unique networkId. 92 * 93 * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread. 94 */ 95public class WifiConfigManager { 96 /** 97 * String used to mask passwords to public interface. 98 */ 99 @VisibleForTesting 100 public static final String PASSWORD_MASK = "*"; 101 /** 102 * Package name for SysUI. This is used to lookup the UID of SysUI which is used to allow 103 * Quick settings to modify network configurations. 104 */ 105 @VisibleForTesting 106 public static final String SYSUI_PACKAGE_NAME = "com.android.systemui"; 107 /** 108 * Network Selection disable reason thresholds. These numbers are used to debounce network 109 * failures before we disable them. 110 * These are indexed using the disable reason constants defined in 111 * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}. 112 */ 113 @VisibleForTesting 114 public static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = { 115 -1, // threshold for NETWORK_SELECTION_ENABLE 116 1, // threshold for DISABLED_BAD_LINK 117 5, // threshold for DISABLED_ASSOCIATION_REJECTION 118 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 119 5, // threshold for DISABLED_DHCP_FAILURE 120 5, // threshold for DISABLED_DNS_FAILURE 121 1, // threshold for DISABLED_WPS_START 122 6, // threshold for DISABLED_TLS_VERSION_MISMATCH 123 1, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 124 1, // threshold for DISABLED_NO_INTERNET 125 1, // threshold for DISABLED_BY_WIFI_MANAGER 126 1 // threshold for DISABLED_BY_USER_SWITCH 127 }; 128 /** 129 * Network Selection disable timeout for each kind of error. After the timeout milliseconds, 130 * enable the network again. 131 * These are indexed using the disable reason constants defined in 132 * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}. 133 */ 134 @VisibleForTesting 135 public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = { 136 Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE 137 15 * 60 * 1000, // threshold for DISABLED_BAD_LINK 138 5 * 60 * 1000, // threshold for DISABLED_ASSOCIATION_REJECTION 139 5 * 60 * 1000, // threshold for DISABLED_AUTHENTICATION_FAILURE 140 5 * 60 * 1000, // threshold for DISABLED_DHCP_FAILURE 141 5 * 60 * 1000, // threshold for DISABLED_DNS_FAILURE 142 0 * 60 * 1000, // threshold for DISABLED_WPS_START 143 Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION 144 Integer.MAX_VALUE, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 145 Integer.MAX_VALUE, // threshold for DISABLED_NO_INTERNET 146 Integer.MAX_VALUE, // threshold for DISABLED_BY_WIFI_MANAGER 147 Integer.MAX_VALUE // threshold for DISABLED_BY_USER_SWITCH 148 }; 149 /** 150 * Max size of scan details to cache in {@link #mScanDetailCaches}. 151 */ 152 @VisibleForTesting 153 public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192; 154 /** 155 * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds 156 * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some 157 * buffer time before the next eviction. 158 */ 159 @VisibleForTesting 160 public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128; 161 /** 162 * Link networks only if they have less than this number of scan cache entries. 163 */ 164 @VisibleForTesting 165 public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6; 166 /** 167 * Link networks only if the bssid in scan results for the networks match in the first 168 * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7" 169 */ 170 @VisibleForTesting 171 public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16; 172 /** 173 * Flags to be passed in for |canModifyNetwork| to decide if the change is minor and can 174 * bypass the lockdown checks. 175 */ 176 private static final boolean ALLOW_LOCKDOWN_CHECK_BYPASS = true; 177 private static final boolean DISALLOW_LOCKDOWN_CHECK_BYPASS = false; 178 /** 179 * Log tag for this class. 180 */ 181 private static final String TAG = "WifiConfigManager"; 182 /** 183 * Maximum age of scan results that can be used for averaging out RSSI value. 184 */ 185 private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000; 186 /** 187 * General sorting algorithm of all networks for scanning purposes: 188 * Place the configurations in descending order of their |numAssociation| values. If networks 189 * have the same |numAssociation|, place the configurations with 190 * |lastSeenInQualifiedNetworkSelection| set first. 191 */ 192 private static final WifiConfigurationUtil.WifiConfigurationComparator sScanListComparator = 193 new WifiConfigurationUtil.WifiConfigurationComparator() { 194 @Override 195 public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) { 196 if (a.numAssociation != b.numAssociation) { 197 return Long.compare(b.numAssociation, a.numAssociation); 198 } else { 199 boolean isConfigALastSeen = 200 a.getNetworkSelectionStatus() 201 .getSeenInLastQualifiedNetworkSelection(); 202 boolean isConfigBLastSeen = 203 b.getNetworkSelectionStatus() 204 .getSeenInLastQualifiedNetworkSelection(); 205 return Boolean.compare(isConfigBLastSeen, isConfigALastSeen); 206 } 207 } 208 }; 209 210 /** 211 * List of external dependencies for WifiConfigManager. 212 */ 213 private final Context mContext; 214 private final Clock mClock; 215 private final UserManager mUserManager; 216 private final BackupManagerProxy mBackupManagerProxy; 217 private final TelephonyManager mTelephonyManager; 218 private final WifiKeyStore mWifiKeyStore; 219 private final WifiConfigStore mWifiConfigStore; 220 private final WifiConfigStoreLegacy mWifiConfigStoreLegacy; 221 private final WifiPermissionsUtil mWifiPermissionsUtil; 222 private final WifiPermissionsWrapper mWifiPermissionsWrapper; 223 /** 224 * Local log used for debugging any WifiConfigManager issues. 225 */ 226 private final LocalLog mLocalLog = 227 new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256); 228 /** 229 * Map of configured networks with network id as the key. 230 */ 231 private final ConfigurationMap mConfiguredNetworks; 232 /** 233 * Stores a map of NetworkId to ScanDetailCache. 234 */ 235 private final Map<Integer, ScanDetailCache> mScanDetailCaches; 236 /** 237 * Framework keeps a list of ephemeral SSIDs that where deleted by user, 238 * so as, framework knows not to autoconnect again those SSIDs based on scorer input. 239 * The list is never cleared up. 240 * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field. 241 */ 242 private final Set<String> mDeletedEphemeralSSIDs; 243 /** 244 * Flag to indicate if only networks with the same psk should be linked. 245 * TODO(b/30706406): Remove this flag if unused. 246 */ 247 private final boolean mOnlyLinkSameCredentialConfigurations; 248 /** 249 * Number of channels to scan for during partial scans initiated while connected. 250 */ 251 private final int mMaxNumActiveChannelsForPartialScans; 252 /** 253 * Verbose logging flag. Toggled by developer options. 254 */ 255 private boolean mVerboseLoggingEnabled = false; 256 /** 257 * Current logged in user ID. 258 */ 259 private int mCurrentUserId = UserHandle.USER_SYSTEM; 260 /** 261 * Flag to indicate that the new user's store has not yet been read since user switch. 262 * Initialize this flag to |true| to trigger a read on the first user unlock after 263 * bootup. 264 */ 265 private boolean mPendingUnlockStoreRead = true; 266 /** 267 * Flag to indicate if we have performed a read from store at all. This is used to gate 268 * any user unlock/switch operations until we read the store (Will happen if wifi is disabled 269 * when user updates from N to O). 270 */ 271 private boolean mPendingStoreRead = true; 272 /** 273 * Flag to indicate if the user unlock was deferred until the store load occurs. 274 */ 275 private boolean mDeferredUserUnlockRead = false; 276 /** 277 * This is keeping track of the next network ID to be assigned. Any new networks will be 278 * assigned |mNextNetworkId| as network ID. 279 */ 280 private int mNextNetworkId = 0; 281 /** 282 * UID of system UI. This uid is allowed to modify network configurations regardless of which 283 * user is logged in. 284 */ 285 private int mSystemUiUid = -1; 286 /** 287 * This is used to remember which network was selected successfully last by an app. This is set 288 * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set. 289 * This is the only way for an app to request connection to a specific network using the 290 * {@link WifiManager} API's. 291 */ 292 private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 293 private long mLastSelectedTimeStamp = 294 WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; 295 296 // Store data for network list and deleted ephemeral SSID list. Used for serializing 297 // parsing data to/from the config store. 298 private final NetworkListStoreData mNetworkListStoreData; 299 private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; 300 301 /** 302 * Create new instance of WifiConfigManager. 303 */ 304 WifiConfigManager( 305 Context context, Clock clock, UserManager userManager, 306 TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore, 307 WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy, 308 WifiPermissionsUtil wifiPermissionsUtil, 309 WifiPermissionsWrapper wifiPermissionsWrapper, 310 NetworkListStoreData networkListStoreData, 311 DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData) { 312 mContext = context; 313 mClock = clock; 314 mUserManager = userManager; 315 mBackupManagerProxy = new BackupManagerProxy(); 316 mTelephonyManager = telephonyManager; 317 mWifiKeyStore = wifiKeyStore; 318 mWifiConfigStore = wifiConfigStore; 319 mWifiConfigStoreLegacy = wifiConfigStoreLegacy; 320 mWifiPermissionsUtil = wifiPermissionsUtil; 321 mWifiPermissionsWrapper = wifiPermissionsWrapper; 322 323 mConfiguredNetworks = new ConfigurationMap(userManager); 324 mScanDetailCaches = new HashMap<>(16, 0.75f); 325 mDeletedEphemeralSSIDs = new HashSet<>(); 326 327 // Register store data for network list and deleted ephemeral SSIDs. 328 mNetworkListStoreData = networkListStoreData; 329 mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData; 330 mWifiConfigStore.registerStoreData(mNetworkListStoreData); 331 mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData); 332 333 mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean( 334 R.bool.config_wifi_only_link_same_credential_configurations); 335 mMaxNumActiveChannelsForPartialScans = mContext.getResources().getInteger( 336 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels); 337 338 try { 339 mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME, 340 PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); 341 } catch (PackageManager.NameNotFoundException e) { 342 Log.e(TAG, "Unable to resolve SystemUI's UID."); 343 } 344 } 345 346 /** 347 * Construct the string to be put in the |creationTime| & |updateTime| elements of 348 * WifiConfiguration from the provided wall clock millis. 349 * 350 * @param wallClockMillis Time in milliseconds to be converted to string. 351 */ 352 @VisibleForTesting 353 public static String createDebugTimeStampString(long wallClockMillis) { 354 StringBuilder sb = new StringBuilder(); 355 sb.append("time="); 356 Calendar c = Calendar.getInstance(); 357 c.setTimeInMillis(wallClockMillis); 358 sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); 359 return sb.toString(); 360 } 361 362 /** 363 * Enable/disable verbose logging in WifiConfigManager & its helper classes. 364 */ 365 public void enableVerboseLogging(int verbose) { 366 if (verbose > 0) { 367 mVerboseLoggingEnabled = true; 368 } else { 369 mVerboseLoggingEnabled = false; 370 } 371 mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled); 372 mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled); 373 } 374 375 /** 376 * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This 377 * is needed when the network configurations are being requested via the public WifiManager 378 * API's. 379 * This currently masks the following elements: psk, wepKeys & enterprise config password. 380 */ 381 private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) { 382 if (!TextUtils.isEmpty(configuration.preSharedKey)) { 383 configuration.preSharedKey = PASSWORD_MASK; 384 } 385 if (configuration.wepKeys != null) { 386 for (int i = 0; i < configuration.wepKeys.length; i++) { 387 if (!TextUtils.isEmpty(configuration.wepKeys[i])) { 388 configuration.wepKeys[i] = PASSWORD_MASK; 389 } 390 } 391 } 392 if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) { 393 configuration.enterpriseConfig.setPassword(PASSWORD_MASK); 394 } 395 } 396 397 /** 398 * Helper method to create a copy of the provided internal WifiConfiguration object to be 399 * passed to external modules. 400 * 401 * @param configuration provided WifiConfiguration object. 402 * @param maskPasswords Mask passwords or not. 403 * @return Copy of the WifiConfiguration object. 404 */ 405 private WifiConfiguration createExternalWifiConfiguration( 406 WifiConfiguration configuration, boolean maskPasswords) { 407 WifiConfiguration network = new WifiConfiguration(configuration); 408 if (maskPasswords) { 409 maskPasswordsInWifiConfiguration(network); 410 } 411 return network; 412 } 413 414 /** 415 * Fetch the list of currently configured networks maintained in WifiConfigManager. 416 * 417 * This retrieves a copy of the internal configurations maintained by WifiConfigManager and 418 * should be used for any public interfaces. 419 * 420 * @param savedOnly Retrieve only saved networks. 421 * @param maskPasswords Mask passwords or not. 422 * @return List of WifiConfiguration objects representing the networks. 423 */ 424 private List<WifiConfiguration> getConfiguredNetworks( 425 boolean savedOnly, boolean maskPasswords) { 426 List<WifiConfiguration> networks = new ArrayList<>(); 427 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 428 if (savedOnly && config.ephemeral) { 429 continue; 430 } 431 networks.add(createExternalWifiConfiguration(config, maskPasswords)); 432 } 433 return networks; 434 } 435 436 /** 437 * Retrieves the list of all configured networks with passwords masked. 438 * 439 * @return List of WifiConfiguration objects representing the networks. 440 */ 441 public List<WifiConfiguration> getConfiguredNetworks() { 442 return getConfiguredNetworks(false, true); 443 } 444 445 /** 446 * Retrieves the list of all configured networks with the passwords in plaintext. 447 * 448 * WARNING: Don't use this to pass network configurations to external apps. Should only be 449 * sent to system apps/wifi stack, when there is a need for passwords in plaintext. 450 * TODO: Need to understand the current use case of this API. 451 * 452 * @return List of WifiConfiguration objects representing the networks. 453 */ 454 public List<WifiConfiguration> getConfiguredNetworksWithPasswords() { 455 return getConfiguredNetworks(false, false); 456 } 457 458 /** 459 * Retrieves the list of all configured networks with the passwords masked. 460 * 461 * @return List of WifiConfiguration objects representing the networks. 462 */ 463 public List<WifiConfiguration> getSavedNetworks() { 464 return getConfiguredNetworks(true, true); 465 } 466 467 /** 468 * Retrieves the configured network corresponding to the provided networkId with password 469 * masked. 470 * 471 * @param networkId networkId of the requested network. 472 * @return WifiConfiguration object if found, null otherwise. 473 */ 474 public WifiConfiguration getConfiguredNetwork(int networkId) { 475 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 476 if (config == null) { 477 return null; 478 } 479 // Create a new configuration object with the passwords masked to send out to the external 480 // world. 481 return createExternalWifiConfiguration(config, true); 482 } 483 484 /** 485 * Retrieves the configured network corresponding to the provided config key with password 486 * masked. 487 * 488 * @param configKey configKey of the requested network. 489 * @return WifiConfiguration object if found, null otherwise. 490 */ 491 public WifiConfiguration getConfiguredNetwork(String configKey) { 492 WifiConfiguration config = getInternalConfiguredNetwork(configKey); 493 if (config == null) { 494 return null; 495 } 496 // Create a new configuration object with the passwords masked to send out to the external 497 // world. 498 return createExternalWifiConfiguration(config, true); 499 } 500 501 /** 502 * Retrieves the configured network corresponding to the provided networkId with password 503 * in plaintext. 504 * 505 * WARNING: Don't use this to pass network configurations to external apps. Should only be 506 * sent to system apps/wifi stack, when there is a need for passwords in plaintext. 507 * 508 * @param networkId networkId of the requested network. 509 * @return WifiConfiguration object if found, null otherwise. 510 */ 511 public WifiConfiguration getConfiguredNetworkWithPassword(int networkId) { 512 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 513 if (config == null) { 514 return null; 515 } 516 // Create a new configuration object without the passwords masked to send out to the 517 // external world. 518 return createExternalWifiConfiguration(config, false); 519 } 520 521 /** 522 * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all 523 * the networks in our database. 524 */ 525 private Collection<WifiConfiguration> getInternalConfiguredNetworks() { 526 return mConfiguredNetworks.valuesForCurrentUser(); 527 } 528 529 /** 530 * Helper method to retrieve the internal WifiConfiguration object corresponding to the 531 * provided configuration in our database. 532 * This first attempts to find the network using the provided network ID in configuration, 533 * else it attempts to find a matching configuration using the configKey. 534 */ 535 private WifiConfiguration getInternalConfiguredNetwork(WifiConfiguration config) { 536 WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId); 537 if (internalConfig != null) { 538 return internalConfig; 539 } 540 internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()); 541 if (internalConfig == null) { 542 Log.e(TAG, "Cannot find network with networkId " + config.networkId 543 + " or configKey " + config.configKey()); 544 } 545 return internalConfig; 546 } 547 548 /** 549 * Helper method to retrieve the internal WifiConfiguration object corresponding to the 550 * provided network ID in our database. 551 */ 552 private WifiConfiguration getInternalConfiguredNetwork(int networkId) { 553 if (networkId == WifiConfiguration.INVALID_NETWORK_ID) { 554 Log.w(TAG, "Looking up network with invalid networkId -1"); 555 return null; 556 } 557 WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId); 558 if (internalConfig == null) { 559 Log.e(TAG, "Cannot find network with networkId " + networkId); 560 } 561 return internalConfig; 562 } 563 564 /** 565 * Helper method to retrieve the internal WifiConfiguration object corresponding to the 566 * provided configKey in our database. 567 */ 568 private WifiConfiguration getInternalConfiguredNetwork(String configKey) { 569 WifiConfiguration internalConfig = 570 mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey); 571 if (internalConfig == null) { 572 Log.e(TAG, "Cannot find network with configKey " + configKey); 573 } 574 return internalConfig; 575 } 576 577 /** 578 * Method to send out the configured networks change broadcast when a single network 579 * configuration is changed. 580 * 581 * @param network WifiConfiguration corresponding to the network that was changed. 582 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 583 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 584 */ 585 private void sendConfiguredNetworkChangedBroadcast( 586 WifiConfiguration network, int reason) { 587 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 588 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 589 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 590 // Create a new WifiConfiguration with passwords masked before we send it out. 591 WifiConfiguration broadcastNetwork = new WifiConfiguration(network); 592 maskPasswordsInWifiConfiguration(broadcastNetwork); 593 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, broadcastNetwork); 594 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 595 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 596 } 597 598 /** 599 * Method to send out the configured networks change broadcast when multiple network 600 * configurations are changed. 601 */ 602 private void sendConfiguredNetworksChangedBroadcast() { 603 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 604 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 605 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 606 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 607 } 608 609 /** 610 * Checks if |uid| has permission to modify the provided configuration. 611 * 612 * @param config WifiConfiguration object corresponding to the network to be modified. 613 * @param uid UID of the app requesting the modification. 614 * @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts. 615 */ 616 private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) { 617 // Passpoint configurations are generated and managed by PasspointManager. They can be 618 // added by either PasspointNetworkEvaluator (for auto connection) or Settings app 619 // (for manual connection), and need to be removed once the connection is completed. 620 // Since it is "owned" by us, so always allow us to modify them. 621 if (config.isPasspoint() && uid == Process.WIFI_UID) { 622 return true; 623 } 624 625 final DevicePolicyManagerInternal dpmi = LocalServices.getService( 626 DevicePolicyManagerInternal.class); 627 628 final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 629 DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 630 631 // If |uid| corresponds to the device owner, allow all modifications. 632 if (isUidDeviceOwner) { 633 return true; 634 } 635 636 final boolean isCreator = (config.creatorUid == uid); 637 638 // Check if the |uid| holds the |OVERRIDE_CONFIG_WIFI| permission if the caller asks us to 639 // bypass the lockdown checks. 640 if (ignoreLockdown) { 641 return mWifiPermissionsUtil.checkConfigOverridePermission(uid); 642 } 643 644 // Check if device has DPM capability. If it has and |dpmi| is still null, then we 645 // treat this case with suspicion and bail out. 646 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) 647 && dpmi == null) { 648 Log.w(TAG, "Error retrieving DPMI service."); 649 return false; 650 } 651 652 // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner. 653 final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy( 654 config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 655 if (!isConfigEligibleForLockdown) { 656 return isCreator || mWifiPermissionsUtil.checkConfigOverridePermission(uid); 657 } 658 659 final ContentResolver resolver = mContext.getContentResolver(); 660 final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, 661 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; 662 return !isLockdownFeatureEnabled && mWifiPermissionsUtil.checkConfigOverridePermission(uid); 663 } 664 665 /** 666 * Method to check if the provided UID belongs to the current foreground user or some other 667 * app (only SysUI today) running on behalf of the user. 668 * This is used to prevent any background user apps from modifying network configurations. 669 * 670 * @param uid uid of the app. 671 * @return true if the UID belongs to the current foreground app or SystemUI, false otherwise. 672 */ 673 private boolean doesUidBelongToCurrentUser(int uid) { 674 return (WifiConfigurationUtil.doesUidBelongToAnyProfile( 675 uid, mUserManager.getProfiles(mCurrentUserId)) || (uid == mSystemUiUid)); 676 } 677 678 /** 679 * Copy over public elements from an external WifiConfiguration object to the internal 680 * configuration object if element has been set in the provided external WifiConfiguration. 681 * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over 682 * for every update. 683 * 684 * This method updates all elements that are common to both network addition & update. 685 * The following fields of {@link WifiConfiguration} are not copied from external configs: 686 * > networkId - These are allocated by Wi-Fi stack internally for any new configurations. 687 * > status - The status needs to be explicitly updated using 688 * {@link WifiManager#enableNetwork(int, boolean)} or 689 * {@link WifiManager#disableNetwork(int)}. 690 * 691 * @param internalConfig WifiConfiguration object in our internal map. 692 * @param externalConfig WifiConfiguration object provided from the external API. 693 */ 694 private void mergeWithInternalWifiConfiguration( 695 WifiConfiguration internalConfig, WifiConfiguration externalConfig) { 696 if (externalConfig.SSID != null) { 697 internalConfig.SSID = externalConfig.SSID; 698 } 699 if (externalConfig.BSSID != null) { 700 internalConfig.BSSID = externalConfig.BSSID; 701 } 702 internalConfig.hiddenSSID = externalConfig.hiddenSSID; 703 if (externalConfig.preSharedKey != null 704 && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) { 705 internalConfig.preSharedKey = externalConfig.preSharedKey; 706 } 707 // Modify only wep keys are present in the provided configuration. This is a little tricky 708 // because there is no easy way to tell if the app is actually trying to null out the 709 // existing keys or not. 710 if (externalConfig.wepKeys != null) { 711 boolean hasWepKey = false; 712 for (int i = 0; i < internalConfig.wepKeys.length; i++) { 713 if (externalConfig.wepKeys[i] != null 714 && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) { 715 internalConfig.wepKeys[i] = externalConfig.wepKeys[i]; 716 hasWepKey = true; 717 } 718 } 719 if (hasWepKey) { 720 internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex; 721 } 722 } 723 if (externalConfig.FQDN != null) { 724 internalConfig.FQDN = externalConfig.FQDN; 725 } 726 if (externalConfig.providerFriendlyName != null) { 727 internalConfig.providerFriendlyName = externalConfig.providerFriendlyName; 728 } 729 if (externalConfig.roamingConsortiumIds != null) { 730 internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone(); 731 } 732 733 // Copy over all the auth/protocol/key mgmt parameters if set. 734 if (externalConfig.allowedAuthAlgorithms != null 735 && !externalConfig.allowedAuthAlgorithms.isEmpty()) { 736 internalConfig.allowedAuthAlgorithms = 737 (BitSet) externalConfig.allowedAuthAlgorithms.clone(); 738 } 739 if (externalConfig.allowedProtocols != null 740 && !externalConfig.allowedProtocols.isEmpty()) { 741 internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone(); 742 } 743 if (externalConfig.allowedKeyManagement != null 744 && !externalConfig.allowedKeyManagement.isEmpty()) { 745 internalConfig.allowedKeyManagement = 746 (BitSet) externalConfig.allowedKeyManagement.clone(); 747 } 748 if (externalConfig.allowedPairwiseCiphers != null 749 && !externalConfig.allowedPairwiseCiphers.isEmpty()) { 750 internalConfig.allowedPairwiseCiphers = 751 (BitSet) externalConfig.allowedPairwiseCiphers.clone(); 752 } 753 if (externalConfig.allowedGroupCiphers != null 754 && !externalConfig.allowedGroupCiphers.isEmpty()) { 755 internalConfig.allowedGroupCiphers = 756 (BitSet) externalConfig.allowedGroupCiphers.clone(); 757 } 758 759 // Copy over the |IpConfiguration| parameters if set. 760 if (externalConfig.getIpConfiguration() != null) { 761 IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment(); 762 if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) { 763 internalConfig.setIpAssignment(ipAssignment); 764 if (ipAssignment == IpConfiguration.IpAssignment.STATIC) { 765 internalConfig.setStaticIpConfiguration( 766 new StaticIpConfiguration(externalConfig.getStaticIpConfiguration())); 767 } 768 } 769 IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings(); 770 if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) { 771 internalConfig.setProxySettings(proxySettings); 772 if (proxySettings == IpConfiguration.ProxySettings.PAC 773 || proxySettings == IpConfiguration.ProxySettings.STATIC) { 774 internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy())); 775 } 776 } 777 } 778 779 // Copy over the |WifiEnterpriseConfig| parameters if set. 780 if (externalConfig.enterpriseConfig != null) { 781 internalConfig.enterpriseConfig = 782 new WifiEnterpriseConfig(externalConfig.enterpriseConfig); 783 } 784 } 785 786 /** 787 * Set all the exposed defaults in the newly created WifiConfiguration object. 788 * These fields have a default value advertised in our public documentation. The only exception 789 * is the hidden |IpConfiguration| parameters, these have a default value even though they're 790 * hidden. 791 * 792 * @param configuration provided WifiConfiguration object. 793 */ 794 private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) { 795 configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); 796 797 configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); 798 configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA); 799 800 configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); 801 configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 802 803 configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); 804 configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); 805 806 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); 807 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); 808 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); 809 configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); 810 811 configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP); 812 configuration.setProxySettings(IpConfiguration.ProxySettings.NONE); 813 814 configuration.status = WifiConfiguration.Status.DISABLED; 815 configuration.getNetworkSelectionStatus().setNetworkSelectionStatus( 816 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); 817 } 818 819 /** 820 * Create a new internal WifiConfiguration object by copying over parameters from the provided 821 * external configuration and set defaults for the appropriate parameters. 822 * 823 * @param externalConfig WifiConfiguration object provided from the external API. 824 * @return New WifiConfiguration object with parameters merged from the provided external 825 * configuration. 826 */ 827 private WifiConfiguration createNewInternalWifiConfigurationFromExternal( 828 WifiConfiguration externalConfig, int uid) { 829 WifiConfiguration newInternalConfig = new WifiConfiguration(); 830 831 // First allocate a new network ID for the configuration. 832 newInternalConfig.networkId = mNextNetworkId++; 833 834 // First set defaults in the new configuration created. 835 setDefaultsInWifiConfiguration(newInternalConfig); 836 837 // Copy over all the public elements from the provided configuration. 838 mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig); 839 840 // Copy over the hidden configuration parameters. These are the only parameters used by 841 // system apps to indicate some property about the network being added. 842 // These are only copied over for network additions and ignored for network updates. 843 newInternalConfig.requirePMF = externalConfig.requirePMF; 844 newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected; 845 newInternalConfig.ephemeral = externalConfig.ephemeral; 846 newInternalConfig.meteredHint = externalConfig.meteredHint; 847 newInternalConfig.useExternalScores = externalConfig.useExternalScores; 848 newInternalConfig.shared = externalConfig.shared; 849 850 // Add debug information for network addition. 851 newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid; 852 newInternalConfig.creatorName = newInternalConfig.lastUpdateName = 853 mContext.getPackageManager().getNameForUid(uid); 854 newInternalConfig.creationTime = newInternalConfig.updateTime = 855 createDebugTimeStampString(mClock.getWallClockMillis()); 856 857 return newInternalConfig; 858 } 859 860 /** 861 * Create a new internal WifiConfiguration object by copying over parameters from the provided 862 * external configuration to a copy of the existing internal WifiConfiguration object. 863 * 864 * @param internalConfig WifiConfiguration object in our internal map. 865 * @param externalConfig WifiConfiguration object provided from the external API. 866 * @return Copy of existing WifiConfiguration object with parameters merged from the provided 867 * configuration. 868 */ 869 private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal( 870 WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid) { 871 WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig); 872 873 // Copy over all the public elements from the provided configuration. 874 mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig); 875 876 // Add debug information for network update. 877 newInternalConfig.lastUpdateUid = uid; 878 newInternalConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid); 879 newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis()); 880 881 return newInternalConfig; 882 } 883 884 /** 885 * Add a network or update a network configuration to our database. 886 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 887 * network configuration. Otherwise, the networkId should refer to an existing configuration. 888 * 889 * @param config provided WifiConfiguration object. 890 * @param uid UID of the app requesting the network addition/deletion. 891 * @return NetworkUpdateResult object representing status of the update. 892 */ 893 private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) { 894 if (mVerboseLoggingEnabled) { 895 Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid()); 896 } 897 WifiConfiguration newInternalConfig = null; 898 899 // First check if we already have a network with the provided network id or configKey. 900 WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config); 901 // No existing network found. So, potentially a network add. 902 if (existingInternalConfig == null) { 903 newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid); 904 // Since the original config provided may have had an empty 905 // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a 906 // network with the the same configkey. 907 existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey()); 908 } 909 // Existing network found. So, a network update. 910 if (existingInternalConfig != null) { 911 // Check for the app's permission before we let it update this network. 912 if (!canModifyNetwork(existingInternalConfig, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) { 913 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 914 + config.configKey()); 915 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 916 } 917 newInternalConfig = 918 updateExistingInternalWifiConfigurationFromExternal( 919 existingInternalConfig, config, uid); 920 } 921 922 // Only add networks with proxy settings if the user has permission to 923 if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig) 924 && !canModifyProxySettings(uid)) { 925 Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings " 926 + config.configKey() + ". Must have OVERRIDE_WIFI_CONFIG," 927 + " or be device or profile owner."); 928 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 929 } 930 931 // Update the keys for non-Passpoint enterprise networks. For Passpoint, the certificates 932 // and keys are installed at the time the provider is installed. 933 if (config.enterpriseConfig != null 934 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE 935 && !config.isPasspoint()) { 936 if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) { 937 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 938 } 939 } 940 941 boolean newNetwork = (existingInternalConfig == null); 942 // This is needed to inform IpManager about any IP configuration changes. 943 boolean hasIpChanged = 944 newNetwork || WifiConfigurationUtil.hasIpChanged( 945 existingInternalConfig, newInternalConfig); 946 boolean hasProxyChanged = 947 newNetwork || WifiConfigurationUtil.hasProxyChanged( 948 existingInternalConfig, newInternalConfig); 949 // Reset the |hasEverConnected| flag if the credential parameters changed in this update. 950 boolean hasCredentialChanged = 951 newNetwork || WifiConfigurationUtil.hasCredentialChanged( 952 existingInternalConfig, newInternalConfig); 953 if (hasCredentialChanged) { 954 newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false); 955 } 956 957 // Add it to our internal map. This will replace any existing network configuration for 958 // updates. 959 mConfiguredNetworks.put(newInternalConfig); 960 961 if (mDeletedEphemeralSSIDs.remove(config.SSID)) { 962 if (mVerboseLoggingEnabled) { 963 Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID); 964 } 965 } 966 967 // Stage the backup of the SettingsProvider package which backs this up. 968 mBackupManagerProxy.notifyDataChanged(); 969 970 NetworkUpdateResult result = 971 new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged); 972 result.setIsNewNetwork(newNetwork); 973 result.setNetworkId(newInternalConfig.networkId); 974 975 localLog("addOrUpdateNetworkInternal: added/updated config." 976 + " netId=" + newInternalConfig.networkId 977 + " configKey=" + newInternalConfig.configKey() 978 + " uid=" + Integer.toString(newInternalConfig.creatorUid) 979 + " name=" + newInternalConfig.creatorName); 980 return result; 981 } 982 983 /** 984 * Add a network or update a network configuration to our database. 985 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 986 * network configuration. Otherwise, the networkId should refer to an existing configuration. 987 * 988 * @param config provided WifiConfiguration object. 989 * @param uid UID of the app requesting the network addition/modification. 990 * @return NetworkUpdateResult object representing status of the update. 991 */ 992 public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) { 993 if (!doesUidBelongToCurrentUser(uid)) { 994 Log.e(TAG, "UID " + uid + " not visible to the current user"); 995 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 996 } 997 if (config == null) { 998 Log.e(TAG, "Cannot add/update network with null config"); 999 return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); 1000 } 1001 NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid); 1002 if (!result.isSuccess()) { 1003 Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid()); 1004 return result; 1005 } 1006 WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId()); 1007 sendConfiguredNetworkChangedBroadcast( 1008 newConfig, 1009 result.isNewNetwork() 1010 ? WifiManager.CHANGE_REASON_ADDED 1011 : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1012 // Unless the added network is ephemeral or Passpoint, persist the network update/addition. 1013 if (!config.ephemeral && !config.isPasspoint()) { 1014 saveToStore(true); 1015 } 1016 return result; 1017 } 1018 1019 /** 1020 * Removes the specified network configuration from our database. 1021 * 1022 * @param config provided WifiConfiguration object. 1023 * @return true if successful, false otherwise. 1024 */ 1025 private boolean removeNetworkInternal(WifiConfiguration config) { 1026 if (mVerboseLoggingEnabled) { 1027 Log.v(TAG, "Removing network " + config.getPrintableSsid()); 1028 } 1029 // Remove any associated enterprise keys for non-Passpoint networks. 1030 if (!config.isPasspoint() && config.enterpriseConfig != null 1031 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 1032 mWifiKeyStore.removeKeys(config.enterpriseConfig); 1033 } 1034 1035 removeConnectChoiceFromAllNetworks(config.configKey()); 1036 mConfiguredNetworks.remove(config.networkId); 1037 mScanDetailCaches.remove(config.networkId); 1038 // Stage the backup of the SettingsProvider package which backs this up. 1039 mBackupManagerProxy.notifyDataChanged(); 1040 1041 localLog("removeNetworkInternal: removed config." 1042 + " netId=" + config.networkId 1043 + " configKey=" + config.configKey()); 1044 return true; 1045 } 1046 1047 /** 1048 * Removes the specified network configuration from our database. 1049 * 1050 * @param networkId network ID of the provided network. 1051 * @param uid UID of the app requesting the network deletion. 1052 * @return true if successful, false otherwise. 1053 */ 1054 public boolean removeNetwork(int networkId, int uid) { 1055 if (!doesUidBelongToCurrentUser(uid)) { 1056 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1057 return false; 1058 } 1059 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1060 if (config == null) { 1061 return false; 1062 } 1063 if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) { 1064 Log.e(TAG, "UID " + uid + " does not have permission to delete configuration " 1065 + config.configKey()); 1066 return false; 1067 } 1068 if (!removeNetworkInternal(config)) { 1069 Log.e(TAG, "Failed to remove network " + config.getPrintableSsid()); 1070 return false; 1071 } 1072 if (networkId == mLastSelectedNetworkId) { 1073 clearLastSelectedNetwork(); 1074 } 1075 sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 1076 // Unless the removed network is ephemeral or Passpoint, persist the network removal. 1077 if (!config.ephemeral && !config.isPasspoint()) { 1078 saveToStore(true); 1079 } 1080 return true; 1081 } 1082 1083 /** 1084 * Remove all networks associated with an application. 1085 * 1086 * @param app Application info of the package of networks to remove. 1087 * @return the {@link Set} of networks that were removed by this call. Networks which matched 1088 * but failed to remove are omitted from this set. 1089 */ 1090 public Set<Integer> removeNetworksForApp(ApplicationInfo app) { 1091 if (app == null || app.packageName == null) { 1092 return Collections.<Integer>emptySet(); 1093 } 1094 Log.d(TAG, "Remove all networks for app " + app); 1095 Set<Integer> removedNetworks = new ArraySet<>(); 1096 WifiConfiguration[] copiedConfigs = 1097 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1098 for (WifiConfiguration config : copiedConfigs) { 1099 if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) { 1100 continue; 1101 } 1102 localLog("Removing network " + config.SSID 1103 + ", application \"" + app.packageName + "\" uninstalled" 1104 + " from user " + UserHandle.getUserId(app.uid)); 1105 if (removeNetwork(config.networkId, mSystemUiUid)) { 1106 removedNetworks.add(config.networkId); 1107 } 1108 } 1109 return removedNetworks; 1110 } 1111 1112 /** 1113 * Remove all networks associated with a user. 1114 * 1115 * @param userId The identifier of the user which is being removed. 1116 * @return the {@link Set} of networks that were removed by this call. Networks which matched 1117 * but failed to remove are omitted from this set. 1118 */ 1119 Set<Integer> removeNetworksForUser(int userId) { 1120 Log.d(TAG, "Remove all networks for user " + userId); 1121 Set<Integer> removedNetworks = new ArraySet<>(); 1122 WifiConfiguration[] copiedConfigs = 1123 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1124 for (WifiConfiguration config : copiedConfigs) { 1125 if (userId != UserHandle.getUserId(config.creatorUid)) { 1126 continue; 1127 } 1128 localLog("Removing network " + config.SSID + ", user " + userId + " removed"); 1129 if (removeNetwork(config.networkId, mSystemUiUid)) { 1130 removedNetworks.add(config.networkId); 1131 } 1132 } 1133 return removedNetworks; 1134 } 1135 1136 /** 1137 * Helper method to mark a network enabled for network selection. 1138 */ 1139 private void setNetworkSelectionEnabled(NetworkSelectionStatus status) { 1140 status.setNetworkSelectionStatus( 1141 NetworkSelectionStatus.NETWORK_SELECTION_ENABLED); 1142 status.setDisableTime( 1143 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1144 status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 1145 1146 // Clear out all the disable reason counters. 1147 status.clearDisableReasonCounter(); 1148 } 1149 1150 /** 1151 * Helper method to mark a network temporarily disabled for network selection. 1152 */ 1153 private void setNetworkSelectionTemporarilyDisabled( 1154 NetworkSelectionStatus status, int disableReason) { 1155 status.setNetworkSelectionStatus( 1156 NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); 1157 // Only need a valid time filled in for temporarily disabled networks. 1158 status.setDisableTime(mClock.getElapsedSinceBootMillis()); 1159 status.setNetworkSelectionDisableReason(disableReason); 1160 } 1161 1162 /** 1163 * Helper method to mark a network permanently disabled for network selection. 1164 */ 1165 private void setNetworkSelectionPermanentlyDisabled( 1166 NetworkSelectionStatus status, int disableReason) { 1167 status.setNetworkSelectionStatus( 1168 NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); 1169 status.setDisableTime( 1170 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1171 status.setNetworkSelectionDisableReason(disableReason); 1172 } 1173 1174 /** 1175 * Helper method to set the publicly exposed status for the network and send out the network 1176 * status change broadcast. 1177 */ 1178 private void setNetworkStatus(WifiConfiguration config, int status) { 1179 config.status = status; 1180 sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1181 } 1182 1183 /** 1184 * Sets a network's status (both internal and public) according to the update reason and 1185 * its current state. 1186 * 1187 * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the 1188 * public {@link WifiConfiguration#status} field if the network is either enabled or 1189 * permanently disabled. 1190 * 1191 * @param config network to be updated. 1192 * @param reason reason code for update. 1193 * @return true if the input configuration has been updated, false otherwise. 1194 */ 1195 private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) { 1196 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1197 if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) { 1198 Log.e(TAG, "Invalid Network disable reason " + reason); 1199 return false; 1200 } 1201 if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { 1202 setNetworkSelectionEnabled(networkStatus); 1203 setNetworkStatus(config, WifiConfiguration.Status.ENABLED); 1204 } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) { 1205 setNetworkSelectionTemporarilyDisabled(networkStatus, reason); 1206 } else { 1207 setNetworkSelectionPermanentlyDisabled(networkStatus, reason); 1208 setNetworkStatus(config, WifiConfiguration.Status.DISABLED); 1209 } 1210 localLog("setNetworkSelectionStatus: configKey=" + config.configKey() 1211 + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason=" 1212 + networkStatus.getNetworkDisableReasonString() + " at=" 1213 + createDebugTimeStampString(mClock.getWallClockMillis())); 1214 saveToStore(false); 1215 return true; 1216 } 1217 1218 /** 1219 * Update a network's status (both internal and public) according to the update reason and 1220 * its current state. 1221 * 1222 * @param config network to be updated. 1223 * @param reason reason code for update. 1224 * @return true if the input configuration has been updated, false otherwise. 1225 */ 1226 private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) { 1227 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1228 if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { 1229 networkStatus.incrementDisableReasonCounter(reason); 1230 // For network disable reasons, we should only update the status if we cross the 1231 // threshold. 1232 int disableReasonCounter = networkStatus.getDisableReasonCounter(reason); 1233 int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason]; 1234 if (disableReasonCounter < disableReasonThreshold) { 1235 if (mVerboseLoggingEnabled) { 1236 Log.v(TAG, "Disable counter for network " + config.getPrintableSsid() 1237 + " for reason " 1238 + NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is " 1239 + networkStatus.getDisableReasonCounter(reason) + " and threshold is " 1240 + disableReasonThreshold); 1241 } 1242 return true; 1243 } 1244 } 1245 return setNetworkSelectionStatus(config, reason); 1246 } 1247 1248 /** 1249 * Update a network's status (both internal and public) according to the update reason and 1250 * its current state. 1251 * 1252 * Each network has 2 status: 1253 * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used 1254 * for temporarily disabling a network for Network Selector. 1255 * 2. Status: This is the exposed status for a network. This is mostly set by 1256 * the public API's {@link WifiManager#enableNetwork(int, boolean)} & 1257 * {@link WifiManager#disableNetwork(int)}. 1258 * 1259 * @param networkId network ID of the network that needs the update. 1260 * @param reason reason to update the network. 1261 * @return true if the input configuration has been updated, false otherwise. 1262 */ 1263 public boolean updateNetworkSelectionStatus(int networkId, int reason) { 1264 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1265 if (config == null) { 1266 return false; 1267 } 1268 return updateNetworkSelectionStatus(config, reason); 1269 } 1270 1271 /** 1272 * Update whether a network is currently not recommended by {@link RecommendedNetworkEvaluator}. 1273 * 1274 * @param networkId network ID of the network to be updated 1275 * @param notRecommended whether this network is not recommended 1276 * @return true if the network is updated, false otherwise 1277 */ 1278 public boolean updateNetworkNotRecommended(int networkId, boolean notRecommended) { 1279 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1280 if (config == null) { 1281 return false; 1282 } 1283 1284 config.getNetworkSelectionStatus().setNotRecommended(notRecommended); 1285 localLog("updateNetworkRecommendation: configKey=" + config.configKey() 1286 + " recommended=" + notRecommended); 1287 saveToStore(false); 1288 return true; 1289 } 1290 1291 /** 1292 * Attempt to re-enable a network for network selection, if this network was either: 1293 * a) Previously temporarily disabled, but its disable timeout has expired, or 1294 * b) Previously disabled because of a user switch, but is now visible to the current 1295 * user. 1296 * 1297 * @param config configuration for the network to be re-enabled for network selection. The 1298 * network corresponding to the config must be visible to the current user. 1299 * @return true if the network identified by {@param config} was re-enabled for qualified 1300 * network selection, false otherwise. 1301 */ 1302 private boolean tryEnableNetwork(WifiConfiguration config) { 1303 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1304 if (networkStatus.isNetworkTemporaryDisabled()) { 1305 long timeDifferenceMs = 1306 mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime(); 1307 int disableReason = networkStatus.getNetworkSelectionDisableReason(); 1308 long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason]; 1309 if (timeDifferenceMs >= disableTimeoutMs) { 1310 return updateNetworkSelectionStatus( 1311 config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 1312 } 1313 } else if (networkStatus.isDisabledByReason( 1314 NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) { 1315 return updateNetworkSelectionStatus( 1316 config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 1317 } 1318 return false; 1319 } 1320 1321 /** 1322 * Attempt to re-enable a network for network selection, if this network was either: 1323 * a) Previously temporarily disabled, but its disable timeout has expired, or 1324 * b) Previously disabled because of a user switch, but is now visible to the current 1325 * user. 1326 * 1327 * @param networkId the id of the network to be checked for possible unblock (due to timeout) 1328 * @return true if the network identified by {@param networkId} was re-enabled for qualified 1329 * network selection, false otherwise. 1330 */ 1331 public boolean tryEnableNetwork(int networkId) { 1332 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1333 if (config == null) { 1334 return false; 1335 } 1336 return tryEnableNetwork(config); 1337 } 1338 1339 /** 1340 * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API. 1341 * 1342 * @param networkId network ID of the network that needs the update. 1343 * @param disableOthers Whether to disable all other networks or not. This is used to indicate 1344 * that the app requested connection to a specific network. 1345 * @param uid uid of the app requesting the update. 1346 * @return true if it succeeds, false otherwise 1347 */ 1348 public boolean enableNetwork(int networkId, boolean disableOthers, int uid) { 1349 if (mVerboseLoggingEnabled) { 1350 Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")"); 1351 } 1352 if (!doesUidBelongToCurrentUser(uid)) { 1353 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1354 return false; 1355 } 1356 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1357 if (config == null) { 1358 return false; 1359 } 1360 if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) { 1361 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1362 + config.configKey()); 1363 return false; 1364 } 1365 if (!updateNetworkSelectionStatus( 1366 networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) { 1367 return false; 1368 } 1369 if (disableOthers) { 1370 setLastSelectedNetwork(networkId); 1371 } 1372 return true; 1373 } 1374 1375 /** 1376 * Disable a network using the public {@link WifiManager#disableNetwork(int)} API. 1377 * 1378 * @param networkId network ID of the network that needs the update. 1379 * @param uid uid of the app requesting the update. 1380 * @return true if it succeeds, false otherwise 1381 */ 1382 public boolean disableNetwork(int networkId, int uid) { 1383 if (mVerboseLoggingEnabled) { 1384 Log.v(TAG, "Disabling network " + networkId); 1385 } 1386 if (!doesUidBelongToCurrentUser(uid)) { 1387 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1388 return false; 1389 } 1390 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1391 if (config == null) { 1392 return false; 1393 } 1394 if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) { 1395 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1396 + config.configKey()); 1397 return false; 1398 } 1399 if (!updateNetworkSelectionStatus( 1400 networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) { 1401 return false; 1402 } 1403 if (networkId == mLastSelectedNetworkId) { 1404 clearLastSelectedNetwork(); 1405 } 1406 return true; 1407 } 1408 1409 /** 1410 * Checks if the |uid| has the necessary permission to force a connection to a network 1411 * and updates the last connected UID for the provided configuration. 1412 * 1413 * @param networkId network ID corresponding to the network. 1414 * @param uid uid of the app requesting the connection. 1415 * @return true if |uid| has the necessary permission to trigger explicit connection to the 1416 * network, false otherwise. 1417 * Note: This returns true only for the system settings/sysui app which holds the 1418 * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let 1419 * any other app force connection to a network. 1420 */ 1421 public boolean checkAndUpdateLastConnectUid(int networkId, int uid) { 1422 if (mVerboseLoggingEnabled) { 1423 Log.v(TAG, "Update network last connect UID for " + networkId); 1424 } 1425 if (!doesUidBelongToCurrentUser(uid)) { 1426 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1427 return false; 1428 } 1429 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1430 if (config == null) { 1431 return false; 1432 } 1433 if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) { 1434 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1435 + config.configKey()); 1436 return false; 1437 } 1438 config.lastConnectUid = uid; 1439 return true; 1440 } 1441 1442 /** 1443 * Updates a network configuration after a successful connection to it. 1444 * 1445 * This method updates the following WifiConfiguration elements: 1446 * 1. Set the |lastConnected| timestamp. 1447 * 2. Increment |numAssociation| counter. 1448 * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|. 1449 * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|. 1450 * 5. Sets the status of network as |CURRENT|. 1451 * 1452 * @param networkId network ID corresponding to the network. 1453 * @return true if the network was found, false otherwise. 1454 */ 1455 public boolean updateNetworkAfterConnect(int networkId) { 1456 if (mVerboseLoggingEnabled) { 1457 Log.v(TAG, "Update network after connect for " + networkId); 1458 } 1459 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1460 if (config == null) { 1461 return false; 1462 } 1463 config.lastConnected = mClock.getWallClockMillis(); 1464 config.numAssociation++; 1465 config.getNetworkSelectionStatus().clearDisableReasonCounter(); 1466 config.getNetworkSelectionStatus().setHasEverConnected(true); 1467 setNetworkStatus(config, WifiConfiguration.Status.CURRENT); 1468 saveToStore(false); 1469 return true; 1470 } 1471 1472 /** 1473 * Updates a network configuration after disconnection from it. 1474 * 1475 * This method updates the following WifiConfiguration elements: 1476 * 1. Set the |lastDisConnected| timestamp. 1477 * 2. Sets the status of network back to |ENABLED|. 1478 * 1479 * @param networkId network ID corresponding to the network. 1480 * @return true if the network was found, false otherwise. 1481 */ 1482 public boolean updateNetworkAfterDisconnect(int networkId) { 1483 if (mVerboseLoggingEnabled) { 1484 Log.v(TAG, "Update network after disconnect for " + networkId); 1485 } 1486 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1487 if (config == null) { 1488 return false; 1489 } 1490 config.lastDisconnected = mClock.getWallClockMillis(); 1491 // If the network hasn't been disabled, mark it back as 1492 // enabled after disconnection. 1493 if (config.status == WifiConfiguration.Status.CURRENT) { 1494 setNetworkStatus(config, WifiConfiguration.Status.ENABLED); 1495 } 1496 saveToStore(false); 1497 return true; 1498 } 1499 1500 /** 1501 * Set default GW MAC address for the provided network. 1502 * 1503 * @param networkId network ID corresponding to the network. 1504 * @param macAddress MAC address of the gateway to be set. 1505 * @return true if the network was found, false otherwise. 1506 */ 1507 public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) { 1508 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1509 if (config == null) { 1510 return false; 1511 } 1512 config.defaultGwMacAddress = macAddress; 1513 return true; 1514 } 1515 1516 /** 1517 * Clear the {@link NetworkSelectionStatus#mCandidate}, 1518 * {@link NetworkSelectionStatus#mCandidateScore} & 1519 * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. 1520 * 1521 * This is invoked by Network Selector at the start of every selection procedure to clear all 1522 * configured networks' scan-result-candidates. 1523 * 1524 * @param networkId network ID corresponding to the network. 1525 * @return true if the network was found, false otherwise. 1526 */ 1527 public boolean clearNetworkCandidateScanResult(int networkId) { 1528 if (mVerboseLoggingEnabled) { 1529 Log.v(TAG, "Clear network candidate scan result for " + networkId); 1530 } 1531 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1532 if (config == null) { 1533 return false; 1534 } 1535 config.getNetworkSelectionStatus().setCandidate(null); 1536 config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE); 1537 config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false); 1538 return true; 1539 } 1540 1541 /** 1542 * Set the {@link NetworkSelectionStatus#mCandidate}, 1543 * {@link NetworkSelectionStatus#mCandidateScore} & 1544 * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. 1545 * 1546 * This is invoked by Network Selector when it sees a network during network selection procedure 1547 * to set the scan result candidate. 1548 * 1549 * @param networkId network ID corresponding to the network. 1550 * @param scanResult Candidate ScanResult associated with this network. 1551 * @param score Score assigned to the candidate. 1552 * @return true if the network was found, false otherwise. 1553 */ 1554 public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) { 1555 if (mVerboseLoggingEnabled) { 1556 Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId); 1557 } 1558 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1559 if (config == null) { 1560 return false; 1561 } 1562 config.getNetworkSelectionStatus().setCandidate(scanResult); 1563 config.getNetworkSelectionStatus().setCandidateScore(score); 1564 config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true); 1565 return true; 1566 } 1567 1568 /** 1569 * Iterate through all the saved networks and remove the provided configuration from the 1570 * {@link NetworkSelectionStatus#mConnectChoice} from them. 1571 * 1572 * This is invoked when a network is removed from our records. 1573 * 1574 * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed. 1575 */ 1576 private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) { 1577 if (mVerboseLoggingEnabled) { 1578 Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey); 1579 } 1580 if (connectChoiceConfigKey == null) { 1581 return; 1582 } 1583 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 1584 WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1585 String connectChoice = status.getConnectChoice(); 1586 if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) { 1587 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID 1588 + " : " + config.networkId); 1589 clearNetworkConnectChoice(config.networkId); 1590 } 1591 } 1592 } 1593 1594 /** 1595 * Clear the {@link NetworkSelectionStatus#mConnectChoice} & 1596 * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network. 1597 * 1598 * @param networkId network ID corresponding to the network. 1599 * @return true if the network was found, false otherwise. 1600 */ 1601 public boolean clearNetworkConnectChoice(int networkId) { 1602 if (mVerboseLoggingEnabled) { 1603 Log.v(TAG, "Clear network connect choice for " + networkId); 1604 } 1605 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1606 if (config == null) { 1607 return false; 1608 } 1609 config.getNetworkSelectionStatus().setConnectChoice(null); 1610 config.getNetworkSelectionStatus().setConnectChoiceTimestamp( 1611 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1612 saveToStore(false); 1613 return true; 1614 } 1615 1616 /** 1617 * Set the {@link NetworkSelectionStatus#mConnectChoice} & 1618 * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network. 1619 * 1620 * This is invoked by Network Selector when the user overrides the currently connected network 1621 * choice. 1622 * 1623 * @param networkId network ID corresponding to the network. 1624 * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over 1625 * this network. 1626 * @param timestamp timestamp at which the choice was made. 1627 * @return true if the network was found, false otherwise. 1628 */ 1629 public boolean setNetworkConnectChoice( 1630 int networkId, String connectChoiceConfigKey, long timestamp) { 1631 if (mVerboseLoggingEnabled) { 1632 Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId); 1633 } 1634 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1635 if (config == null) { 1636 return false; 1637 } 1638 config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey); 1639 config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp); 1640 saveToStore(false); 1641 return true; 1642 } 1643 1644 /** 1645 * Increments the number of no internet access reports in the provided network. 1646 * 1647 * @param networkId network ID corresponding to the network. 1648 * @return true if the network was found, false otherwise. 1649 */ 1650 public boolean incrementNetworkNoInternetAccessReports(int networkId) { 1651 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1652 if (config == null) { 1653 return false; 1654 } 1655 config.numNoInternetAccessReports++; 1656 return true; 1657 } 1658 1659 /** 1660 * Sets the internet access is validated or not in the provided network. 1661 * 1662 * @param networkId network ID corresponding to the network. 1663 * @param validated Whether access is validated or not. 1664 * @return true if the network was found, false otherwise. 1665 */ 1666 public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) { 1667 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1668 if (config == null) { 1669 return false; 1670 } 1671 config.validatedInternetAccess = validated; 1672 config.numNoInternetAccessReports = 0; 1673 saveToStore(false); 1674 return true; 1675 } 1676 1677 /** 1678 * Sets whether the internet access is expected or not in the provided network. 1679 * 1680 * @param networkId network ID corresponding to the network. 1681 * @param expected Whether access is expected or not. 1682 * @return true if the network was found, false otherwise. 1683 */ 1684 public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) { 1685 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1686 if (config == null) { 1687 return false; 1688 } 1689 config.noInternetAccessExpected = expected; 1690 return true; 1691 } 1692 1693 /** 1694 * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This 1695 * is done when either the corresponding network is either removed or disabled. 1696 */ 1697 private void clearLastSelectedNetwork() { 1698 if (mVerboseLoggingEnabled) { 1699 Log.v(TAG, "Clearing last selected network"); 1700 } 1701 mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 1702 mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; 1703 } 1704 1705 /** 1706 * Helper method to mark a network as the last selected one by an app/user. This is set 1707 * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set. 1708 * This is used by network selector to assign a special bonus during network selection. 1709 */ 1710 private void setLastSelectedNetwork(int networkId) { 1711 if (mVerboseLoggingEnabled) { 1712 Log.v(TAG, "Setting last selected network to " + networkId); 1713 } 1714 mLastSelectedNetworkId = networkId; 1715 mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis(); 1716 } 1717 1718 /** 1719 * Retrieve the network Id corresponding to the last network that was explicitly selected by 1720 * an app/user. 1721 * 1722 * @return network Id corresponding to the last selected network. 1723 */ 1724 public int getLastSelectedNetwork() { 1725 return mLastSelectedNetworkId; 1726 } 1727 1728 /** 1729 * Retrieve the configKey corresponding to the last network that was explicitly selected by 1730 * an app/user. 1731 * 1732 * @return network Id corresponding to the last selected network. 1733 */ 1734 public String getLastSelectedNetworkConfigKey() { 1735 if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) { 1736 return ""; 1737 } 1738 WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId); 1739 if (config == null) { 1740 return ""; 1741 } 1742 return config.configKey(); 1743 } 1744 1745 /** 1746 * Retrieve the time stamp at which a network was explicitly selected by an app/user. 1747 * 1748 * @return timestamp in milliseconds from boot when this was set. 1749 */ 1750 public long getLastSelectedTimeStamp() { 1751 return mLastSelectedTimeStamp; 1752 } 1753 1754 /** 1755 * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided 1756 * network. 1757 * 1758 * @param networkId network ID corresponding to the network. 1759 * @return existing {@link ScanDetailCache} entry if one exists or null. 1760 */ 1761 public ScanDetailCache getScanDetailCacheForNetwork(int networkId) { 1762 return mScanDetailCaches.get(networkId); 1763 } 1764 1765 /** 1766 * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for 1767 * the provided network. 1768 * 1769 * @param config configuration corresponding to the the network. 1770 * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for 1771 * this network. 1772 */ 1773 private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) { 1774 if (config == null) return null; 1775 ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId); 1776 if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { 1777 cache = new ScanDetailCache( 1778 config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE); 1779 mScanDetailCaches.put(config.networkId, cache); 1780 } 1781 return cache; 1782 } 1783 1784 /** 1785 * Saves the provided ScanDetail into the corresponding scan detail cache entry 1786 * {@link #mScanDetailCaches} for the provided network. 1787 * 1788 * @param config configuration corresponding to the the network. 1789 * @param scanDetail new scan detail instance to be saved into the cache. 1790 */ 1791 private void saveToScanDetailCacheForNetwork( 1792 WifiConfiguration config, ScanDetail scanDetail) { 1793 ScanResult scanResult = scanDetail.getScanResult(); 1794 1795 ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config); 1796 if (scanDetailCache == null) { 1797 Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid()); 1798 return; 1799 } 1800 1801 // Adding a new BSSID 1802 ScanResult result = scanDetailCache.get(scanResult.BSSID); 1803 if (result != null) { 1804 // transfer the black list status 1805 scanResult.blackListTimestamp = result.blackListTimestamp; 1806 scanResult.numIpConfigFailures = result.numIpConfigFailures; 1807 scanResult.numConnection = result.numConnection; 1808 } 1809 if (config.ephemeral) { 1810 // For an ephemeral Wi-Fi config, the ScanResult should be considered 1811 // untrusted. 1812 scanResult.untrusted = true; 1813 } 1814 1815 // Add the scan detail to this network's scan detail cache. 1816 scanDetailCache.put(scanDetail); 1817 1818 // Since we added a scan result to this configuration, re-attempt linking. 1819 // TODO: Do we really need to do this after every scan result? 1820 attemptNetworkLinking(config); 1821 } 1822 1823 /** 1824 * Retrieves a saved network corresponding to the provided scan detail if one exists. 1825 * 1826 * @param scanDetail ScanDetail instance to use for looking up the network. 1827 * @return WifiConfiguration object representing the network corresponding to the scanDetail, 1828 * null if none exists. 1829 */ 1830 private WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) { 1831 ScanResult scanResult = scanDetail.getScanResult(); 1832 if (scanResult == null) { 1833 Log.e(TAG, "No scan result found in scan detail"); 1834 return null; 1835 } 1836 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 1837 if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) { 1838 if (mVerboseLoggingEnabled) { 1839 Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey() 1840 + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]"); 1841 } 1842 return config; 1843 } 1844 } 1845 return null; 1846 } 1847 1848 /** 1849 * Retrieves a saved network corresponding to the provided scan detail if one exists and caches 1850 * the provided |scanDetail| into the corresponding scan detail cache entry 1851 * {@link #mScanDetailCaches} for the retrieved network. 1852 * 1853 * @param scanDetail input a scanDetail from the scan result 1854 * @return WifiConfiguration object representing the network corresponding to the scanDetail, 1855 * null if none exists. 1856 */ 1857 public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) { 1858 WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail); 1859 if (network == null) { 1860 return null; 1861 } 1862 saveToScanDetailCacheForNetwork(network, scanDetail); 1863 // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM) 1864 // Information Element (IE), into the associated WifiConfigurations. Most of the 1865 // time there is no TIM IE in the scan result (Probe Response instead of Beacon 1866 // Frame), these scanResult DTIM's are negative and ignored. 1867 // Used for metrics collection. 1868 if (scanDetail.getNetworkDetail() != null 1869 && scanDetail.getNetworkDetail().getDtimInterval() > 0) { 1870 network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval(); 1871 } 1872 return createExternalWifiConfiguration(network, true); 1873 } 1874 1875 /** 1876 * Update the scan detail cache associated with current connected network with latest 1877 * RSSI value in the provided WifiInfo. 1878 * This is invoked when we get an RSSI poll update after connection. 1879 * 1880 * @param info WifiInfo instance pointing to the current connected network. 1881 */ 1882 public void updateScanDetailCacheFromWifiInfo(WifiInfo info) { 1883 WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId()); 1884 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId()); 1885 if (config != null && scanDetailCache != null) { 1886 ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID()); 1887 if (scanDetail != null) { 1888 ScanResult result = scanDetail.getScanResult(); 1889 long previousSeen = result.seen; 1890 int previousRssi = result.level; 1891 // Update the scan result 1892 scanDetail.setSeen(); 1893 result.level = info.getRssi(); 1894 // Average the RSSI value 1895 result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS); 1896 if (mVerboseLoggingEnabled) { 1897 Log.v(TAG, "Updating scan detail cache freq=" + result.frequency 1898 + " BSSID=" + result.BSSID 1899 + " RSSI=" + result.level 1900 + " for " + config.configKey()); 1901 } 1902 } 1903 } 1904 } 1905 1906 /** 1907 * Save the ScanDetail to the ScanDetailCache of the given network. This is used 1908 * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching 1909 * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network. 1910 * 1911 * @param networkId The ID of the network to save ScanDetail to 1912 * @param scanDetail The ScanDetail to cache 1913 */ 1914 public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) { 1915 WifiConfiguration network = getInternalConfiguredNetwork(networkId); 1916 if (network == null) { 1917 return; 1918 } 1919 saveToScanDetailCacheForNetwork(network, scanDetail); 1920 } 1921 1922 /** 1923 * Helper method to check if the 2 provided networks can be linked or not. 1924 * Networks are considered for linking if: 1925 * 1. Share the same GW MAC address. 1926 * 2. Scan results for the networks have AP's with MAC address which differ only in the last 1927 * nibble. 1928 * 1929 * @param network1 WifiConfiguration corresponding to network 1. 1930 * @param network2 WifiConfiguration corresponding to network 2. 1931 * @param scanDetailCache1 ScanDetailCache entry for network 1. 1932 * @param scanDetailCache1 ScanDetailCache entry for network 2. 1933 * @return true if the networks should be linked, false if the networks should be unlinked. 1934 */ 1935 private boolean shouldNetworksBeLinked( 1936 WifiConfiguration network1, WifiConfiguration network2, 1937 ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) { 1938 // TODO (b/30706406): Link networks only with same passwords if the 1939 // |mOnlyLinkSameCredentialConfigurations| flag is set. 1940 if (mOnlyLinkSameCredentialConfigurations) { 1941 if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) { 1942 if (mVerboseLoggingEnabled) { 1943 Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch"); 1944 } 1945 return false; 1946 } 1947 } 1948 if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) { 1949 // If both default GW are known, link only if they are equal 1950 if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) { 1951 if (mVerboseLoggingEnabled) { 1952 Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID 1953 + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress); 1954 } 1955 return true; 1956 } 1957 } else { 1958 // We do not know BOTH default gateways hence we will try to link 1959 // hoping that WifiConfigurations are indeed behind the same gateway. 1960 // once both WifiConfiguration have been tried and thus once both default gateways 1961 // are known we will revisit the choice of linking them. 1962 if (scanDetailCache1 != null && scanDetailCache2 != null) { 1963 for (String abssid : scanDetailCache1.keySet()) { 1964 for (String bbssid : scanDetailCache2.keySet()) { 1965 if (abssid.regionMatches( 1966 true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) { 1967 // If first 16 ASCII characters of BSSID matches, 1968 // we assume this is a DBDC. 1969 if (mVerboseLoggingEnabled) { 1970 Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match " 1971 + network2.SSID + " and " + network1.SSID 1972 + " bssida " + abssid + " bssidb " + bbssid); 1973 } 1974 return true; 1975 } 1976 } 1977 } 1978 } 1979 } 1980 return false; 1981 } 1982 1983 /** 1984 * Helper methods to link 2 networks together. 1985 * 1986 * @param network1 WifiConfiguration corresponding to network 1. 1987 * @param network2 WifiConfiguration corresponding to network 2. 1988 */ 1989 private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) { 1990 if (mVerboseLoggingEnabled) { 1991 Log.v(TAG, "linkNetworks will link " + network2.configKey() 1992 + " and " + network1.configKey()); 1993 } 1994 if (network2.linkedConfigurations == null) { 1995 network2.linkedConfigurations = new HashMap<>(); 1996 } 1997 if (network1.linkedConfigurations == null) { 1998 network1.linkedConfigurations = new HashMap<>(); 1999 } 2000 // TODO (b/30638473): This needs to become a set instead of map, but it will need 2001 // public interface changes and need some migration of existing store data. 2002 network2.linkedConfigurations.put(network1.configKey(), 1); 2003 network1.linkedConfigurations.put(network2.configKey(), 1); 2004 } 2005 2006 /** 2007 * Helper methods to unlink 2 networks from each other. 2008 * 2009 * @param network1 WifiConfiguration corresponding to network 1. 2010 * @param network2 WifiConfiguration corresponding to network 2. 2011 */ 2012 private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) { 2013 if (network2.linkedConfigurations != null 2014 && (network2.linkedConfigurations.get(network1.configKey()) != null)) { 2015 if (mVerboseLoggingEnabled) { 2016 Log.v(TAG, "unlinkNetworks un-link " + network1.configKey() 2017 + " from " + network2.configKey()); 2018 } 2019 network2.linkedConfigurations.remove(network1.configKey()); 2020 } 2021 if (network1.linkedConfigurations != null 2022 && (network1.linkedConfigurations.get(network2.configKey()) != null)) { 2023 if (mVerboseLoggingEnabled) { 2024 Log.v(TAG, "unlinkNetworks un-link " + network2.configKey() 2025 + " from " + network1.configKey()); 2026 } 2027 network1.linkedConfigurations.remove(network2.configKey()); 2028 } 2029 } 2030 2031 /** 2032 * This method runs through all the saved networks and checks if the provided network can be 2033 * linked with any of them. 2034 * 2035 * @param config WifiConfiguration object corresponding to the network that needs to be 2036 * checked for potential links. 2037 */ 2038 private void attemptNetworkLinking(WifiConfiguration config) { 2039 // Only link WPA_PSK config. 2040 if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 2041 return; 2042 } 2043 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId); 2044 // Ignore configurations with large number of BSSIDs. 2045 if (scanDetailCache != null 2046 && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) { 2047 return; 2048 } 2049 for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) { 2050 if (linkConfig.configKey().equals(config.configKey())) { 2051 continue; 2052 } 2053 if (linkConfig.ephemeral) { 2054 continue; 2055 } 2056 // Network Selector will be allowed to dynamically jump from a linked configuration 2057 // to another, hence only link configurations that have WPA_PSK security type. 2058 if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 2059 continue; 2060 } 2061 ScanDetailCache linkScanDetailCache = 2062 getScanDetailCacheForNetwork(linkConfig.networkId); 2063 // Ignore configurations with large number of BSSIDs. 2064 if (linkScanDetailCache != null 2065 && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) { 2066 continue; 2067 } 2068 // Check if the networks should be linked/unlinked. 2069 if (shouldNetworksBeLinked( 2070 config, linkConfig, scanDetailCache, linkScanDetailCache)) { 2071 linkNetworks(config, linkConfig); 2072 } else { 2073 unlinkNetworks(config, linkConfig); 2074 } 2075 } 2076 } 2077 2078 /** 2079 * Helper method to fetch list of channels for a network from the associated ScanResult's cache 2080 * and add it to the provided channel as long as the size of the set is less than 2081 * |maxChannelSetSize|. 2082 * 2083 * @param channelSet Channel set holding all the channels for the network. 2084 * @param scanDetailCache ScanDetailCache entry associated with the network. 2085 * @param nowInMillis current timestamp to be used for age comparison. 2086 * @param ageInMillis only consider scan details whose timestamps are earlier than this 2087 * value. 2088 * @param maxChannelSetSize Maximum number of channels to be added to the set. 2089 * @return false if the list is full, true otherwise. 2090 */ 2091 private boolean addToChannelSetForNetworkFromScanDetailCache( 2092 Set<Integer> channelSet, ScanDetailCache scanDetailCache, 2093 long nowInMillis, long ageInMillis, int maxChannelSetSize) { 2094 if (scanDetailCache != null && scanDetailCache.size() > 0) { 2095 for (ScanDetail scanDetail : scanDetailCache.values()) { 2096 ScanResult result = scanDetail.getScanResult(); 2097 boolean valid = (nowInMillis - result.seen) < ageInMillis; 2098 if (mVerboseLoggingEnabled) { 2099 Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq " 2100 + result.frequency + " age " + (nowInMillis - result.seen) 2101 + " ?=" + valid); 2102 } 2103 if (valid) { 2104 channelSet.add(result.frequency); 2105 } 2106 if (channelSet.size() >= maxChannelSetSize) { 2107 return false; 2108 } 2109 } 2110 } 2111 return true; 2112 } 2113 2114 /** 2115 * Retrieve a set of channels on which AP's for the provided network was seen using the 2116 * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial 2117 * scans for the currently connected network. 2118 * 2119 * @param networkId network ID corresponding to the network. 2120 * @param ageInMillis only consider scan details whose timestamps are earlier than this value. 2121 * @param homeChannelFreq frequency of the currently connected network. 2122 * @return Set containing the frequencies on which this network was found, null if the network 2123 * was not found or there are no associated scan details in the cache. 2124 */ 2125 public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis, 2126 int homeChannelFreq) { 2127 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2128 if (config == null) { 2129 return null; 2130 } 2131 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId); 2132 if (scanDetailCache == null && config.linkedConfigurations == null) { 2133 Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId); 2134 return null; 2135 } 2136 if (mVerboseLoggingEnabled) { 2137 StringBuilder dbg = new StringBuilder(); 2138 dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ") 2139 .append(ageInMillis) 2140 .append(" for ") 2141 .append(config.configKey()) 2142 .append(" max ") 2143 .append(mMaxNumActiveChannelsForPartialScans); 2144 if (scanDetailCache != null) { 2145 dbg.append(" bssids " + scanDetailCache.size()); 2146 } 2147 if (config.linkedConfigurations != null) { 2148 dbg.append(" linked " + config.linkedConfigurations.size()); 2149 } 2150 Log.v(TAG, dbg.toString()); 2151 } 2152 Set<Integer> channelSet = new HashSet<>(); 2153 2154 // First add the currently connected network channel. 2155 if (homeChannelFreq > 0) { 2156 channelSet.add(homeChannelFreq); 2157 if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) { 2158 return channelSet; 2159 } 2160 } 2161 2162 long nowInMillis = mClock.getWallClockMillis(); 2163 2164 // Then get channels for the network. 2165 if (!addToChannelSetForNetworkFromScanDetailCache( 2166 channelSet, scanDetailCache, nowInMillis, ageInMillis, 2167 mMaxNumActiveChannelsForPartialScans)) { 2168 return channelSet; 2169 } 2170 2171 // Lastly get channels for linked networks. 2172 if (config.linkedConfigurations != null) { 2173 for (String configKey : config.linkedConfigurations.keySet()) { 2174 WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey); 2175 if (linkedConfig == null) { 2176 continue; 2177 } 2178 ScanDetailCache linkedScanDetailCache = 2179 getScanDetailCacheForNetwork(linkedConfig.networkId); 2180 if (!addToChannelSetForNetworkFromScanDetailCache( 2181 channelSet, linkedScanDetailCache, nowInMillis, ageInMillis, 2182 mMaxNumActiveChannelsForPartialScans)) { 2183 break; 2184 } 2185 } 2186 } 2187 return channelSet; 2188 } 2189 2190 /** 2191 * Retrieves a list of all the saved networks before enabling disconnected/connected PNO. 2192 * 2193 * PNO network list sent to the firmware has limited size. If there are a lot of saved 2194 * networks, this list will be truncated and we might end up not sending the networks 2195 * with the highest chance of connecting to the firmware. 2196 * So, re-sort the network list based on the frequency of connection to those networks 2197 * and whether it was last seen in the scan results. 2198 * 2199 * TODO (b/30399964): Recalculate the list whenever network status changes. 2200 * @return list of networks with updated priorities. 2201 */ 2202 public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() { 2203 List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); 2204 List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks()); 2205 // Remove any permanently disabled networks. 2206 Iterator<WifiConfiguration> iter = networks.iterator(); 2207 while (iter.hasNext()) { 2208 WifiConfiguration config = iter.next(); 2209 if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) { 2210 iter.remove(); 2211 } 2212 } 2213 Collections.sort(networks, sScanListComparator); 2214 // Let's use the network list size - 1 as the highest priority and then go down from there. 2215 // So, the most frequently connected network has the highest priority now. 2216 int priority = networks.size() - 1; 2217 for (WifiConfiguration config : networks) { 2218 pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority)); 2219 priority--; 2220 } 2221 return pnoList; 2222 } 2223 2224 /** 2225 * Retrieves a list of all the saved hidden networks for scans. 2226 * 2227 * Hidden network list sent to the firmware has limited size. If there are a lot of saved 2228 * networks, this list will be truncated and we might end up not sending the networks 2229 * with the highest chance of connecting to the firmware. 2230 * So, re-sort the network list based on the frequency of connection to those networks 2231 * and whether it was last seen in the scan results. 2232 * 2233 * @return list of networks with updated priorities. 2234 */ 2235 public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() { 2236 List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>(); 2237 List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks()); 2238 // Remove any permanently disabled networks or non hidden networks. 2239 Iterator<WifiConfiguration> iter = networks.iterator(); 2240 while (iter.hasNext()) { 2241 WifiConfiguration config = iter.next(); 2242 if (!config.hiddenSSID || 2243 config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) { 2244 iter.remove(); 2245 } 2246 } 2247 Collections.sort(networks, sScanListComparator); 2248 // Let's use the network list size - 1 as the highest priority and then go down from there. 2249 // So, the most frequently connected network has the highest priority now. 2250 int priority = networks.size() - 1; 2251 for (WifiConfiguration config : networks) { 2252 hiddenList.add( 2253 new WifiScanner.ScanSettings.HiddenNetwork(config.SSID)); 2254 priority--; 2255 } 2256 return hiddenList; 2257 } 2258 2259 /** 2260 * Check if the provided ephemeral network was deleted by the user or not. 2261 * 2262 * @param ssid caller must ensure that the SSID passed thru this API match 2263 * the WifiConfiguration.SSID rules, and thus be surrounded by quotes. 2264 * @return true if network was deleted, false otherwise. 2265 */ 2266 public boolean wasEphemeralNetworkDeleted(String ssid) { 2267 return mDeletedEphemeralSSIDs.contains(ssid); 2268 } 2269 2270 /** 2271 * Disable an ephemeral SSID for the purpose of network selection. 2272 * 2273 * The only way to "un-disable it" is if the user create a network for that SSID and then 2274 * forget it. 2275 * 2276 * @param ssid caller must ensure that the SSID passed thru this API match 2277 * the WifiConfiguration.SSID rules, and thus be surrounded by quotes. 2278 * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can 2279 * disconnect if this is the current network. 2280 */ 2281 public WifiConfiguration disableEphemeralNetwork(String ssid) { 2282 if (ssid == null) { 2283 return null; 2284 } 2285 WifiConfiguration foundConfig = null; 2286 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2287 if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) { 2288 foundConfig = config; 2289 break; 2290 } 2291 } 2292 mDeletedEphemeralSSIDs.add(ssid); 2293 Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size()); 2294 if (foundConfig != null) { 2295 Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: " 2296 + foundConfig.networkId); 2297 } 2298 return foundConfig; 2299 } 2300 2301 /** 2302 * Resets all sim networks state. 2303 */ 2304 public void resetSimNetworks() { 2305 if (mVerboseLoggingEnabled) localLog("resetSimNetworks"); 2306 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2307 if (TelephonyUtil.isSimConfig(config)) { 2308 String currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config); 2309 // Update the loaded config 2310 config.enterpriseConfig.setIdentity(currentIdentity); 2311 config.enterpriseConfig.setAnonymousIdentity(""); 2312 } 2313 } 2314 } 2315 2316 /** 2317 * Any network using certificates to authenticate access requires unlocked key store; unless 2318 * the certificates can be stored with hardware encryption 2319 * 2320 * @return true if we need an unlocked keystore, false otherwise. 2321 */ 2322 public boolean needsUnlockedKeyStore() { 2323 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2324 if (WifiConfigurationUtil.isConfigForEapNetwork(config)) { 2325 if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 2326 return true; 2327 } 2328 } 2329 } 2330 return false; 2331 } 2332 2333 /** 2334 * Helper method to perform the following operations during user switch/unlock: 2335 * - Remove private networks of the old user. 2336 * - Load from the new user store file. 2337 * - Save the store files again to migrate any user specific networks from the shared store 2338 * to user store. 2339 * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller 2340 * should ensure that the stores are accessible before invocation. 2341 * 2342 * @param userId The identifier of the new foreground user, after the unlock or switch. 2343 */ 2344 private void handleUserUnlockOrSwitch(int userId) { 2345 if (mVerboseLoggingEnabled) { 2346 Log.v(TAG, "Loading from store after user switch/unlock for " + userId); 2347 } 2348 // Switch out the user store file. 2349 if (loadFromUserStoreAfterUnlockOrSwitch(userId)) { 2350 saveToStore(true); 2351 mPendingUnlockStoreRead = false; 2352 } 2353 } 2354 2355 /** 2356 * Handles the switch to a different foreground user: 2357 * - Flush the current state to the old user's store file. 2358 * - Switch the user specific store file. 2359 * - Reload the networks from the store files (shared & user). 2360 * - Write the store files to move any user specific private networks from shared store to user 2361 * store. 2362 * 2363 * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked. 2364 * 2365 * @param userId The identifier of the new foreground user, after the switch. 2366 * @return List of network ID's of all the private networks of the old user which will be 2367 * removed from memory. 2368 */ 2369 public Set<Integer> handleUserSwitch(int userId) { 2370 if (mVerboseLoggingEnabled) { 2371 Log.v(TAG, "Handling user switch for " + userId); 2372 } 2373 if (userId == mCurrentUserId) { 2374 Log.w(TAG, "User already in foreground " + userId); 2375 return new HashSet<>(); 2376 } 2377 if (mPendingStoreRead) { 2378 Log.wtf(TAG, "Unexpected user switch before store is read!"); 2379 return new HashSet<>(); 2380 } 2381 if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { 2382 saveToStore(true); 2383 } 2384 // Remove any private networks of the old user before switching the userId. 2385 Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId); 2386 mConfiguredNetworks.setNewUser(userId); 2387 mCurrentUserId = userId; 2388 2389 if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { 2390 handleUserUnlockOrSwitch(mCurrentUserId); 2391 } else { 2392 // Cannot read data from new user's CE store file before they log-in. 2393 mPendingUnlockStoreRead = true; 2394 Log.i(TAG, "Waiting for user unlock to load from store"); 2395 } 2396 return removedNetworkIds; 2397 } 2398 2399 /** 2400 * Handles the unlock of foreground user. This maybe needed to read the store file if the user's 2401 * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked. 2402 * 2403 * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked. 2404 * 2405 * @param userId The identifier of the user that unlocked. 2406 */ 2407 public void handleUserUnlock(int userId) { 2408 if (mVerboseLoggingEnabled) { 2409 Log.v(TAG, "Handling user unlock for " + userId); 2410 } 2411 if (mPendingStoreRead) { 2412 Log.w(TAG, "Ignore user unlock until store is read!"); 2413 mDeferredUserUnlockRead = true; 2414 return; 2415 } 2416 if (userId == mCurrentUserId && mPendingUnlockStoreRead) { 2417 handleUserUnlockOrSwitch(mCurrentUserId); 2418 } 2419 } 2420 2421 /** 2422 * Handles the stop of foreground user. This is needed to write the store file to flush 2423 * out any pending data before the user's CE store storage is unavailable. 2424 * 2425 * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked. 2426 * 2427 * @param userId The identifier of the user that stopped. 2428 */ 2429 public void handleUserStop(int userId) { 2430 if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { 2431 saveToStore(true); 2432 clearInternalData(); 2433 mCurrentUserId = UserHandle.USER_SYSTEM; 2434 } 2435 } 2436 2437 /** 2438 * Helper method to clear internal databases. 2439 * This method clears the: 2440 * - List of configured networks. 2441 * - Map of scan detail caches. 2442 * - List of deleted ephemeral networks. 2443 */ 2444 private void clearInternalData() { 2445 mConfiguredNetworks.clear(); 2446 mDeletedEphemeralSSIDs.clear(); 2447 mScanDetailCaches.clear(); 2448 clearLastSelectedNetwork(); 2449 } 2450 2451 /** 2452 * Helper method to clear internal databases of the specified user. 2453 * This method clears the: 2454 * - Private configured configured networks of the specified user. 2455 * - Map of scan detail caches. 2456 * - List of deleted ephemeral networks. 2457 * 2458 * @param userId The identifier of the current foreground user, before the switch. 2459 * @return List of network ID's of all the private networks of the old user which will be 2460 * removed from memory. 2461 */ 2462 private Set<Integer> clearInternalUserData(int userId) { 2463 Set<Integer> removedNetworkIds = new HashSet<>(); 2464 // Remove any private networks of the old user before switching the userId. 2465 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2466 if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile( 2467 config.creatorUid, mUserManager.getProfiles(userId))) { 2468 removedNetworkIds.add(config.networkId); 2469 mConfiguredNetworks.remove(config.networkId); 2470 } 2471 } 2472 mDeletedEphemeralSSIDs.clear(); 2473 mScanDetailCaches.clear(); 2474 clearLastSelectedNetwork(); 2475 return removedNetworkIds; 2476 } 2477 2478 /** 2479 * Helper function to populate the internal (in-memory) data from the retrieved shared store 2480 * (file) data. 2481 * 2482 * @param configurations list of configurations retrieved from store. 2483 */ 2484 private void loadInternalDataFromSharedStore( 2485 List<WifiConfiguration> configurations) { 2486 for (WifiConfiguration configuration : configurations) { 2487 configuration.networkId = mNextNetworkId++; 2488 if (mVerboseLoggingEnabled) { 2489 Log.v(TAG, "Adding network from shared store " + configuration.configKey()); 2490 } 2491 mConfiguredNetworks.put(configuration); 2492 } 2493 } 2494 2495 /** 2496 * Helper function to populate the internal (in-memory) data from the retrieved user store 2497 * (file) data. 2498 * 2499 * @param configurations list of configurations retrieved from store. 2500 * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by 2501 * the user. 2502 */ 2503 private void loadInternalDataFromUserStore( 2504 List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) { 2505 for (WifiConfiguration configuration : configurations) { 2506 configuration.networkId = mNextNetworkId++; 2507 if (mVerboseLoggingEnabled) { 2508 Log.v(TAG, "Adding network from user store " + configuration.configKey()); 2509 } 2510 mConfiguredNetworks.put(configuration); 2511 } 2512 for (String ssid : deletedEphemeralSSIDs) { 2513 mDeletedEphemeralSSIDs.add(ssid); 2514 } 2515 } 2516 2517 /** 2518 * Helper function to populate the internal (in-memory) data from the retrieved stores (file) 2519 * data. 2520 * This method: 2521 * 1. Clears all existing internal data. 2522 * 2. Sends out the networks changed broadcast after loading all the data. 2523 * 2524 * @param sharedConfigurations list of network configurations retrieved from shared store. 2525 * @param userConfigurations list of network configurations retrieved from user store. 2526 * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by 2527 * the user. 2528 */ 2529 private void loadInternalData( 2530 List<WifiConfiguration> sharedConfigurations, 2531 List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) { 2532 // Clear out all the existing in-memory lists and load the lists from what was retrieved 2533 // from the config store. 2534 clearInternalData(); 2535 loadInternalDataFromSharedStore(sharedConfigurations); 2536 loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs); 2537 if (mConfiguredNetworks.sizeForAllUsers() == 0) { 2538 Log.w(TAG, "No stored networks found."); 2539 } 2540 sendConfiguredNetworksChangedBroadcast(); 2541 mPendingStoreRead = false; 2542 } 2543 2544 /** 2545 * Migrate data from legacy store files. The function performs the following operations: 2546 * 1. Check if the legacy store files are present. 2547 * 2. If yes, read all the data from the store files. 2548 * 3. Save it to the new store files. 2549 * 4. Delete the legacy store file. 2550 * 2551 * @return true if migration was successful or not needed (fresh install), false if it failed. 2552 */ 2553 public boolean migrateFromLegacyStore() { 2554 if (!mWifiConfigStoreLegacy.areStoresPresent()) { 2555 Log.d(TAG, "Legacy store files not found. No migration needed!"); 2556 return true; 2557 } 2558 WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read(); 2559 Log.d(TAG, "Reading from legacy store completed"); 2560 loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(), 2561 storeData.getDeletedEphemeralSSIDs()); 2562 2563 // Setup user store for the current user in case it have not setup yet, so that data 2564 // owned by the current user will be backed to the user store. 2565 if (mDeferredUserUnlockRead) { 2566 mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId)); 2567 mDeferredUserUnlockRead = false; 2568 } 2569 2570 if (!saveToStore(true)) { 2571 return false; 2572 } 2573 mWifiConfigStoreLegacy.removeStores(); 2574 Log.d(TAG, "Migration from legacy store completed"); 2575 return true; 2576 } 2577 2578 /** 2579 * Read the config store and load the in-memory lists from the store data retrieved and sends 2580 * out the networks changed broadcast. 2581 * 2582 * This reads all the network configurations from: 2583 * 1. Shared WifiConfigStore.xml 2584 * 2. User WifiConfigStore.xml 2585 * 2586 * @return true on success or not needed (fresh install/pending legacy store migration), 2587 * false otherwise. 2588 */ 2589 public boolean loadFromStore() { 2590 if (!mWifiConfigStore.areStoresPresent()) { 2591 Log.d(TAG, "New store files not found. No saved networks loaded!"); 2592 if (!mWifiConfigStoreLegacy.areStoresPresent()) { 2593 // No legacy store files either, so reset the pending store read flag. 2594 mPendingStoreRead = false; 2595 } 2596 return true; 2597 } 2598 // If the user unlock comes in before we load from store, which means the user store have 2599 // not been setup yet for the current user. Setup the user store before the read so that 2600 // configurations for the current user will also being loaded. 2601 if (mDeferredUserUnlockRead) { 2602 Log.i(TAG, "Handling user unlock before loading from store."); 2603 mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId)); 2604 mDeferredUserUnlockRead = false; 2605 } 2606 try { 2607 mWifiConfigStore.read(); 2608 } catch (IOException e) { 2609 Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e); 2610 return false; 2611 } catch (XmlPullParserException e) { 2612 Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e); 2613 return false; 2614 } 2615 loadInternalData(mNetworkListStoreData.getSharedConfigurations(), 2616 mNetworkListStoreData.getUserConfigurations(), 2617 mDeletedEphemeralSsidsStoreData.getSsidList()); 2618 return true; 2619 } 2620 2621 /** 2622 * Read the user config store and load the in-memory lists from the store data retrieved and 2623 * sends out the networks changed broadcast. 2624 * This should be used for all user switches/unlocks to only load networks from the user 2625 * specific store and avoid reloading the shared networks. 2626 * 2627 * This reads all the network configurations from: 2628 * 1. User WifiConfigStore.xml 2629 * 2630 * @param userId The identifier of the foreground user. 2631 * @return true on success, false otherwise. 2632 */ 2633 public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) { 2634 try { 2635 mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId)); 2636 } catch (IOException e) { 2637 Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e); 2638 return false; 2639 } catch (XmlPullParserException e) { 2640 Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" + 2641 "lost!", e); 2642 return false; 2643 } 2644 loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(), 2645 mDeletedEphemeralSsidsStoreData.getSsidList()); 2646 return true; 2647 } 2648 2649 /** 2650 * Save the current snapshot of the in-memory lists to the config store. 2651 * 2652 * @param forceWrite Whether the write needs to be forced or not. 2653 * @return Whether the write was successful or not, this is applicable only for force writes. 2654 */ 2655 public boolean saveToStore(boolean forceWrite) { 2656 ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>(); 2657 ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>(); 2658 // List of network IDs for legacy Passpoint configuration to be removed. 2659 List<Integer> legacyPasspointNetId = new ArrayList<>(); 2660 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 2661 // Ignore ephemeral networks and non-legacy Passpoint configurations. 2662 if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) { 2663 continue; 2664 } 2665 2666 // Migrate the legacy Passpoint configurations owned by the current user to 2667 // {@link PasspointManager}. 2668 if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile( 2669 config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) { 2670 legacyPasspointNetId.add(config.networkId); 2671 // Migrate the legacy Passpoint configuration and add it to PasspointManager. 2672 if (!PasspointManager.addLegacyPasspointConfig(config)) { 2673 Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN); 2674 } 2675 // This will prevent adding |config| to the |sharedConfigurations|. 2676 continue; 2677 } 2678 2679 // We push all shared networks & private networks not belonging to the current 2680 // user to the shared store. Ideally, private networks for other users should 2681 // not even be in memory, 2682 // But, this logic is in place to deal with store migration from N to O 2683 // because all networks were previously stored in a central file. We cannot 2684 // write these private networks to the user specific store until the corresponding 2685 // user logs in. 2686 if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile( 2687 config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) { 2688 sharedConfigurations.add(config); 2689 } else { 2690 userConfigurations.add(config); 2691 } 2692 } 2693 2694 // Remove the configurations for migrated Passpoint configurations. 2695 for (int networkId : legacyPasspointNetId) { 2696 mConfiguredNetworks.remove(networkId); 2697 } 2698 2699 // Setup store data for write. 2700 mNetworkListStoreData.setSharedConfigurations(sharedConfigurations); 2701 mNetworkListStoreData.setUserConfigurations(userConfigurations); 2702 mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs); 2703 2704 try { 2705 mWifiConfigStore.write(forceWrite); 2706 } catch (IOException e) { 2707 Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e); 2708 return false; 2709 } catch (XmlPullParserException e) { 2710 Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e); 2711 return false; 2712 } 2713 return true; 2714 } 2715 2716 /** 2717 * Helper method for logging into local log buffer. 2718 */ 2719 private void localLog(String s) { 2720 if (mLocalLog != null) { 2721 mLocalLog.log(s); 2722 } 2723 } 2724 2725 /** 2726 * Dump the local log buffer and other internal state of WifiConfigManager. 2727 */ 2728 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2729 pw.println("Dump of WifiConfigManager"); 2730 pw.println("WifiConfigManager - Log Begin ----"); 2731 mLocalLog.dump(fd, pw, args); 2732 pw.println("WifiConfigManager - Log End ----"); 2733 pw.println("WifiConfigManager - Configured networks Begin ----"); 2734 for (WifiConfiguration network : getInternalConfiguredNetworks()) { 2735 pw.println(network); 2736 } 2737 pw.println("WifiConfigManager - Configured networks End ----"); 2738 pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId); 2739 pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId); 2740 } 2741 2742 /** 2743 * Returns true if the given uid has permission to add, update or remove proxy settings 2744 */ 2745 private boolean canModifyProxySettings(int uid) { 2746 final DevicePolicyManagerInternal dpmi = 2747 mWifiPermissionsWrapper.getDevicePolicyManagerInternal(); 2748 final boolean isUidProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 2749 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); 2750 final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 2751 DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 2752 final boolean hasConfigOverridePermission = 2753 mWifiPermissionsUtil.checkConfigOverridePermission(uid); 2754 // If |uid| corresponds to the device owner, allow all modifications. 2755 if (isUidDeviceOwner || isUidProfileOwner || hasConfigOverridePermission) { 2756 return true; 2757 } 2758 if (mVerboseLoggingEnabled) { 2759 Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings." 2760 + " ConfigOverride=" + hasConfigOverridePermission 2761 + " DeviceOwner=" + isUidDeviceOwner 2762 + " ProfileOwner=" + isUidProfileOwner); 2763 } 2764 return false; 2765 } 2766} 2767