WifiConfigManager.java revision 6f1055408a43428ababffc1d1fa928c825924680
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 saveToStore(true); 1373 return true; 1374 } 1375 1376 /** 1377 * Disable a network using the public {@link WifiManager#disableNetwork(int)} API. 1378 * 1379 * @param networkId network ID of the network that needs the update. 1380 * @param uid uid of the app requesting the update. 1381 * @return true if it succeeds, false otherwise 1382 */ 1383 public boolean disableNetwork(int networkId, int uid) { 1384 if (mVerboseLoggingEnabled) { 1385 Log.v(TAG, "Disabling network " + networkId); 1386 } 1387 if (!doesUidBelongToCurrentUser(uid)) { 1388 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1389 return false; 1390 } 1391 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1392 if (config == null) { 1393 return false; 1394 } 1395 if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) { 1396 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1397 + config.configKey()); 1398 return false; 1399 } 1400 if (!updateNetworkSelectionStatus( 1401 networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) { 1402 return false; 1403 } 1404 if (networkId == mLastSelectedNetworkId) { 1405 clearLastSelectedNetwork(); 1406 } 1407 saveToStore(true); 1408 return true; 1409 } 1410 1411 /** 1412 * Checks if the |uid| has the necessary permission to force a connection to a network 1413 * and updates the last connected UID for the provided configuration. 1414 * 1415 * @param networkId network ID corresponding to the network. 1416 * @param uid uid of the app requesting the connection. 1417 * @return true if |uid| has the necessary permission to trigger explicit connection to the 1418 * network, false otherwise. 1419 * Note: This returns true only for the system settings/sysui app which holds the 1420 * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let 1421 * any other app force connection to a network. 1422 */ 1423 public boolean checkAndUpdateLastConnectUid(int networkId, int uid) { 1424 if (mVerboseLoggingEnabled) { 1425 Log.v(TAG, "Update network last connect UID for " + networkId); 1426 } 1427 if (!doesUidBelongToCurrentUser(uid)) { 1428 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1429 return false; 1430 } 1431 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1432 if (config == null) { 1433 return false; 1434 } 1435 if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) { 1436 Log.e(TAG, "UID " + uid + " does not have permission to update configuration " 1437 + config.configKey()); 1438 return false; 1439 } 1440 config.lastConnectUid = uid; 1441 return true; 1442 } 1443 1444 /** 1445 * Updates a network configuration after a successful connection to it. 1446 * 1447 * This method updates the following WifiConfiguration elements: 1448 * 1. Set the |lastConnected| timestamp. 1449 * 2. Increment |numAssociation| counter. 1450 * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|. 1451 * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|. 1452 * 5. Sets the status of network as |CURRENT|. 1453 * 1454 * @param networkId network ID corresponding to the network. 1455 * @return true if the network was found, false otherwise. 1456 */ 1457 public boolean updateNetworkAfterConnect(int networkId) { 1458 if (mVerboseLoggingEnabled) { 1459 Log.v(TAG, "Update network after connect for " + networkId); 1460 } 1461 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1462 if (config == null) { 1463 return false; 1464 } 1465 config.lastConnected = mClock.getWallClockMillis(); 1466 config.numAssociation++; 1467 config.getNetworkSelectionStatus().clearDisableReasonCounter(); 1468 config.getNetworkSelectionStatus().setHasEverConnected(true); 1469 setNetworkStatus(config, WifiConfiguration.Status.CURRENT); 1470 saveToStore(false); 1471 return true; 1472 } 1473 1474 /** 1475 * Updates a network configuration after disconnection from it. 1476 * 1477 * This method updates the following WifiConfiguration elements: 1478 * 1. Set the |lastDisConnected| timestamp. 1479 * 2. Sets the status of network back to |ENABLED|. 1480 * 1481 * @param networkId network ID corresponding to the network. 1482 * @return true if the network was found, false otherwise. 1483 */ 1484 public boolean updateNetworkAfterDisconnect(int networkId) { 1485 if (mVerboseLoggingEnabled) { 1486 Log.v(TAG, "Update network after disconnect for " + networkId); 1487 } 1488 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1489 if (config == null) { 1490 return false; 1491 } 1492 config.lastDisconnected = mClock.getWallClockMillis(); 1493 // If the network hasn't been disabled, mark it back as 1494 // enabled after disconnection. 1495 if (config.status == WifiConfiguration.Status.CURRENT) { 1496 setNetworkStatus(config, WifiConfiguration.Status.ENABLED); 1497 } 1498 saveToStore(false); 1499 return true; 1500 } 1501 1502 /** 1503 * Set default GW MAC address for the provided network. 1504 * 1505 * @param networkId network ID corresponding to the network. 1506 * @param macAddress MAC address of the gateway to be set. 1507 * @return true if the network was found, false otherwise. 1508 */ 1509 public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) { 1510 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1511 if (config == null) { 1512 return false; 1513 } 1514 config.defaultGwMacAddress = macAddress; 1515 return true; 1516 } 1517 1518 /** 1519 * Clear the {@link NetworkSelectionStatus#mCandidate}, 1520 * {@link NetworkSelectionStatus#mCandidateScore} & 1521 * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. 1522 * 1523 * This is invoked by Network Selector at the start of every selection procedure to clear all 1524 * configured networks' scan-result-candidates. 1525 * 1526 * @param networkId network ID corresponding to the network. 1527 * @return true if the network was found, false otherwise. 1528 */ 1529 public boolean clearNetworkCandidateScanResult(int networkId) { 1530 if (mVerboseLoggingEnabled) { 1531 Log.v(TAG, "Clear network candidate scan result for " + networkId); 1532 } 1533 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1534 if (config == null) { 1535 return false; 1536 } 1537 config.getNetworkSelectionStatus().setCandidate(null); 1538 config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE); 1539 config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false); 1540 return true; 1541 } 1542 1543 /** 1544 * Set the {@link NetworkSelectionStatus#mCandidate}, 1545 * {@link NetworkSelectionStatus#mCandidateScore} & 1546 * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network. 1547 * 1548 * This is invoked by Network Selector when it sees a network during network selection procedure 1549 * to set the scan result candidate. 1550 * 1551 * @param networkId network ID corresponding to the network. 1552 * @param scanResult Candidate ScanResult associated with this network. 1553 * @param score Score assigned to the candidate. 1554 * @return true if the network was found, false otherwise. 1555 */ 1556 public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) { 1557 if (mVerboseLoggingEnabled) { 1558 Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId); 1559 } 1560 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1561 if (config == null) { 1562 return false; 1563 } 1564 config.getNetworkSelectionStatus().setCandidate(scanResult); 1565 config.getNetworkSelectionStatus().setCandidateScore(score); 1566 config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true); 1567 return true; 1568 } 1569 1570 /** 1571 * Iterate through all the saved networks and remove the provided configuration from the 1572 * {@link NetworkSelectionStatus#mConnectChoice} from them. 1573 * 1574 * This is invoked when a network is removed from our records. 1575 * 1576 * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed. 1577 */ 1578 private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) { 1579 if (mVerboseLoggingEnabled) { 1580 Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey); 1581 } 1582 if (connectChoiceConfigKey == null) { 1583 return; 1584 } 1585 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 1586 WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1587 String connectChoice = status.getConnectChoice(); 1588 if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) { 1589 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID 1590 + " : " + config.networkId); 1591 clearNetworkConnectChoice(config.networkId); 1592 } 1593 } 1594 } 1595 1596 /** 1597 * Clear the {@link NetworkSelectionStatus#mConnectChoice} & 1598 * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network. 1599 * 1600 * @param networkId network ID corresponding to the network. 1601 * @return true if the network was found, false otherwise. 1602 */ 1603 public boolean clearNetworkConnectChoice(int networkId) { 1604 if (mVerboseLoggingEnabled) { 1605 Log.v(TAG, "Clear network connect choice for " + networkId); 1606 } 1607 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1608 if (config == null) { 1609 return false; 1610 } 1611 config.getNetworkSelectionStatus().setConnectChoice(null); 1612 config.getNetworkSelectionStatus().setConnectChoiceTimestamp( 1613 NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1614 saveToStore(false); 1615 return true; 1616 } 1617 1618 /** 1619 * Set the {@link NetworkSelectionStatus#mConnectChoice} & 1620 * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network. 1621 * 1622 * This is invoked by Network Selector when the user overrides the currently connected network 1623 * choice. 1624 * 1625 * @param networkId network ID corresponding to the network. 1626 * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over 1627 * this network. 1628 * @param timestamp timestamp at which the choice was made. 1629 * @return true if the network was found, false otherwise. 1630 */ 1631 public boolean setNetworkConnectChoice( 1632 int networkId, String connectChoiceConfigKey, long timestamp) { 1633 if (mVerboseLoggingEnabled) { 1634 Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId); 1635 } 1636 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1637 if (config == null) { 1638 return false; 1639 } 1640 config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey); 1641 config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp); 1642 saveToStore(false); 1643 return true; 1644 } 1645 1646 /** 1647 * Increments the number of no internet access reports in the provided network. 1648 * 1649 * @param networkId network ID corresponding to the network. 1650 * @return true if the network was found, false otherwise. 1651 */ 1652 public boolean incrementNetworkNoInternetAccessReports(int networkId) { 1653 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1654 if (config == null) { 1655 return false; 1656 } 1657 config.numNoInternetAccessReports++; 1658 return true; 1659 } 1660 1661 /** 1662 * Sets the internet access is validated or not in the provided network. 1663 * 1664 * @param networkId network ID corresponding to the network. 1665 * @param validated Whether access is validated or not. 1666 * @return true if the network was found, false otherwise. 1667 */ 1668 public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) { 1669 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1670 if (config == null) { 1671 return false; 1672 } 1673 config.validatedInternetAccess = validated; 1674 config.numNoInternetAccessReports = 0; 1675 saveToStore(false); 1676 return true; 1677 } 1678 1679 /** 1680 * Sets whether the internet access is expected or not in the provided network. 1681 * 1682 * @param networkId network ID corresponding to the network. 1683 * @param expected Whether access is expected or not. 1684 * @return true if the network was found, false otherwise. 1685 */ 1686 public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) { 1687 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 1688 if (config == null) { 1689 return false; 1690 } 1691 config.noInternetAccessExpected = expected; 1692 return true; 1693 } 1694 1695 /** 1696 * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This 1697 * is done when either the corresponding network is either removed or disabled. 1698 */ 1699 private void clearLastSelectedNetwork() { 1700 if (mVerboseLoggingEnabled) { 1701 Log.v(TAG, "Clearing last selected network"); 1702 } 1703 mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 1704 mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; 1705 } 1706 1707 /** 1708 * Helper method to mark a network as the last selected one by an app/user. This is set 1709 * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set. 1710 * This is used by network selector to assign a special bonus during network selection. 1711 */ 1712 private void setLastSelectedNetwork(int networkId) { 1713 if (mVerboseLoggingEnabled) { 1714 Log.v(TAG, "Setting last selected network to " + networkId); 1715 } 1716 mLastSelectedNetworkId = networkId; 1717 mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis(); 1718 } 1719 1720 /** 1721 * Retrieve the network Id corresponding to the last network that was explicitly selected by 1722 * an app/user. 1723 * 1724 * @return network Id corresponding to the last selected network. 1725 */ 1726 public int getLastSelectedNetwork() { 1727 return mLastSelectedNetworkId; 1728 } 1729 1730 /** 1731 * Retrieve the configKey corresponding to the last network that was explicitly selected by 1732 * an app/user. 1733 * 1734 * @return network Id corresponding to the last selected network. 1735 */ 1736 public String getLastSelectedNetworkConfigKey() { 1737 if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) { 1738 return ""; 1739 } 1740 WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId); 1741 if (config == null) { 1742 return ""; 1743 } 1744 return config.configKey(); 1745 } 1746 1747 /** 1748 * Retrieve the time stamp at which a network was explicitly selected by an app/user. 1749 * 1750 * @return timestamp in milliseconds from boot when this was set. 1751 */ 1752 public long getLastSelectedTimeStamp() { 1753 return mLastSelectedTimeStamp; 1754 } 1755 1756 /** 1757 * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided 1758 * network. 1759 * 1760 * @param networkId network ID corresponding to the network. 1761 * @return existing {@link ScanDetailCache} entry if one exists or null. 1762 */ 1763 public ScanDetailCache getScanDetailCacheForNetwork(int networkId) { 1764 return mScanDetailCaches.get(networkId); 1765 } 1766 1767 /** 1768 * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for 1769 * the provided network. 1770 * 1771 * @param config configuration corresponding to the the network. 1772 * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for 1773 * this network. 1774 */ 1775 private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) { 1776 if (config == null) return null; 1777 ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId); 1778 if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { 1779 cache = new ScanDetailCache( 1780 config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE); 1781 mScanDetailCaches.put(config.networkId, cache); 1782 } 1783 return cache; 1784 } 1785 1786 /** 1787 * Saves the provided ScanDetail into the corresponding scan detail cache entry 1788 * {@link #mScanDetailCaches} for the provided network. 1789 * 1790 * @param config configuration corresponding to the the network. 1791 * @param scanDetail new scan detail instance to be saved into the cache. 1792 */ 1793 private void saveToScanDetailCacheForNetwork( 1794 WifiConfiguration config, ScanDetail scanDetail) { 1795 ScanResult scanResult = scanDetail.getScanResult(); 1796 1797 ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config); 1798 if (scanDetailCache == null) { 1799 Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid()); 1800 return; 1801 } 1802 1803 // Adding a new BSSID 1804 ScanResult result = scanDetailCache.get(scanResult.BSSID); 1805 if (result != null) { 1806 // transfer the black list status 1807 scanResult.blackListTimestamp = result.blackListTimestamp; 1808 scanResult.numIpConfigFailures = result.numIpConfigFailures; 1809 scanResult.numConnection = result.numConnection; 1810 } 1811 if (config.ephemeral) { 1812 // For an ephemeral Wi-Fi config, the ScanResult should be considered 1813 // untrusted. 1814 scanResult.untrusted = true; 1815 } 1816 1817 // Add the scan detail to this network's scan detail cache. 1818 scanDetailCache.put(scanDetail); 1819 1820 // Since we added a scan result to this configuration, re-attempt linking. 1821 // TODO: Do we really need to do this after every scan result? 1822 attemptNetworkLinking(config); 1823 } 1824 1825 /** 1826 * Retrieves a saved network corresponding to the provided scan detail if one exists. 1827 * 1828 * @param scanDetail ScanDetail instance to use for looking up the network. 1829 * @return WifiConfiguration object representing the network corresponding to the scanDetail, 1830 * null if none exists. 1831 */ 1832 private WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) { 1833 ScanResult scanResult = scanDetail.getScanResult(); 1834 if (scanResult == null) { 1835 Log.e(TAG, "No scan result found in scan detail"); 1836 return null; 1837 } 1838 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 1839 if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) { 1840 if (mVerboseLoggingEnabled) { 1841 Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey() 1842 + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]"); 1843 } 1844 return config; 1845 } 1846 } 1847 return null; 1848 } 1849 1850 /** 1851 * Retrieves a saved network corresponding to the provided scan detail if one exists and caches 1852 * the provided |scanDetail| into the corresponding scan detail cache entry 1853 * {@link #mScanDetailCaches} for the retrieved network. 1854 * 1855 * @param scanDetail input a scanDetail from the scan result 1856 * @return WifiConfiguration object representing the network corresponding to the scanDetail, 1857 * null if none exists. 1858 */ 1859 public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) { 1860 WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail); 1861 if (network == null) { 1862 return null; 1863 } 1864 saveToScanDetailCacheForNetwork(network, scanDetail); 1865 // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM) 1866 // Information Element (IE), into the associated WifiConfigurations. Most of the 1867 // time there is no TIM IE in the scan result (Probe Response instead of Beacon 1868 // Frame), these scanResult DTIM's are negative and ignored. 1869 // Used for metrics collection. 1870 if (scanDetail.getNetworkDetail() != null 1871 && scanDetail.getNetworkDetail().getDtimInterval() > 0) { 1872 network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval(); 1873 } 1874 return createExternalWifiConfiguration(network, true); 1875 } 1876 1877 /** 1878 * Update the scan detail cache associated with current connected network with latest 1879 * RSSI value in the provided WifiInfo. 1880 * This is invoked when we get an RSSI poll update after connection. 1881 * 1882 * @param info WifiInfo instance pointing to the current connected network. 1883 */ 1884 public void updateScanDetailCacheFromWifiInfo(WifiInfo info) { 1885 WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId()); 1886 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId()); 1887 if (config != null && scanDetailCache != null) { 1888 ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID()); 1889 if (scanDetail != null) { 1890 ScanResult result = scanDetail.getScanResult(); 1891 long previousSeen = result.seen; 1892 int previousRssi = result.level; 1893 // Update the scan result 1894 scanDetail.setSeen(); 1895 result.level = info.getRssi(); 1896 // Average the RSSI value 1897 result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS); 1898 if (mVerboseLoggingEnabled) { 1899 Log.v(TAG, "Updating scan detail cache freq=" + result.frequency 1900 + " BSSID=" + result.BSSID 1901 + " RSSI=" + result.level 1902 + " for " + config.configKey()); 1903 } 1904 } 1905 } 1906 } 1907 1908 /** 1909 * Save the ScanDetail to the ScanDetailCache of the given network. This is used 1910 * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching 1911 * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network. 1912 * 1913 * @param networkId The ID of the network to save ScanDetail to 1914 * @param scanDetail The ScanDetail to cache 1915 */ 1916 public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) { 1917 WifiConfiguration network = getInternalConfiguredNetwork(networkId); 1918 if (network == null) { 1919 return; 1920 } 1921 saveToScanDetailCacheForNetwork(network, scanDetail); 1922 } 1923 1924 /** 1925 * Helper method to check if the 2 provided networks can be linked or not. 1926 * Networks are considered for linking if: 1927 * 1. Share the same GW MAC address. 1928 * 2. Scan results for the networks have AP's with MAC address which differ only in the last 1929 * nibble. 1930 * 1931 * @param network1 WifiConfiguration corresponding to network 1. 1932 * @param network2 WifiConfiguration corresponding to network 2. 1933 * @param scanDetailCache1 ScanDetailCache entry for network 1. 1934 * @param scanDetailCache1 ScanDetailCache entry for network 2. 1935 * @return true if the networks should be linked, false if the networks should be unlinked. 1936 */ 1937 private boolean shouldNetworksBeLinked( 1938 WifiConfiguration network1, WifiConfiguration network2, 1939 ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) { 1940 // TODO (b/30706406): Link networks only with same passwords if the 1941 // |mOnlyLinkSameCredentialConfigurations| flag is set. 1942 if (mOnlyLinkSameCredentialConfigurations) { 1943 if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) { 1944 if (mVerboseLoggingEnabled) { 1945 Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch"); 1946 } 1947 return false; 1948 } 1949 } 1950 if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) { 1951 // If both default GW are known, link only if they are equal 1952 if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) { 1953 if (mVerboseLoggingEnabled) { 1954 Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID 1955 + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress); 1956 } 1957 return true; 1958 } 1959 } else { 1960 // We do not know BOTH default gateways hence we will try to link 1961 // hoping that WifiConfigurations are indeed behind the same gateway. 1962 // once both WifiConfiguration have been tried and thus once both default gateways 1963 // are known we will revisit the choice of linking them. 1964 if (scanDetailCache1 != null && scanDetailCache2 != null) { 1965 for (String abssid : scanDetailCache1.keySet()) { 1966 for (String bbssid : scanDetailCache2.keySet()) { 1967 if (abssid.regionMatches( 1968 true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) { 1969 // If first 16 ASCII characters of BSSID matches, 1970 // we assume this is a DBDC. 1971 if (mVerboseLoggingEnabled) { 1972 Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match " 1973 + network2.SSID + " and " + network1.SSID 1974 + " bssida " + abssid + " bssidb " + bbssid); 1975 } 1976 return true; 1977 } 1978 } 1979 } 1980 } 1981 } 1982 return false; 1983 } 1984 1985 /** 1986 * Helper methods to link 2 networks together. 1987 * 1988 * @param network1 WifiConfiguration corresponding to network 1. 1989 * @param network2 WifiConfiguration corresponding to network 2. 1990 */ 1991 private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) { 1992 if (mVerboseLoggingEnabled) { 1993 Log.v(TAG, "linkNetworks will link " + network2.configKey() 1994 + " and " + network1.configKey()); 1995 } 1996 if (network2.linkedConfigurations == null) { 1997 network2.linkedConfigurations = new HashMap<>(); 1998 } 1999 if (network1.linkedConfigurations == null) { 2000 network1.linkedConfigurations = new HashMap<>(); 2001 } 2002 // TODO (b/30638473): This needs to become a set instead of map, but it will need 2003 // public interface changes and need some migration of existing store data. 2004 network2.linkedConfigurations.put(network1.configKey(), 1); 2005 network1.linkedConfigurations.put(network2.configKey(), 1); 2006 } 2007 2008 /** 2009 * Helper methods to unlink 2 networks from each other. 2010 * 2011 * @param network1 WifiConfiguration corresponding to network 1. 2012 * @param network2 WifiConfiguration corresponding to network 2. 2013 */ 2014 private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) { 2015 if (network2.linkedConfigurations != null 2016 && (network2.linkedConfigurations.get(network1.configKey()) != null)) { 2017 if (mVerboseLoggingEnabled) { 2018 Log.v(TAG, "unlinkNetworks un-link " + network1.configKey() 2019 + " from " + network2.configKey()); 2020 } 2021 network2.linkedConfigurations.remove(network1.configKey()); 2022 } 2023 if (network1.linkedConfigurations != null 2024 && (network1.linkedConfigurations.get(network2.configKey()) != null)) { 2025 if (mVerboseLoggingEnabled) { 2026 Log.v(TAG, "unlinkNetworks un-link " + network2.configKey() 2027 + " from " + network1.configKey()); 2028 } 2029 network1.linkedConfigurations.remove(network2.configKey()); 2030 } 2031 } 2032 2033 /** 2034 * This method runs through all the saved networks and checks if the provided network can be 2035 * linked with any of them. 2036 * 2037 * @param config WifiConfiguration object corresponding to the network that needs to be 2038 * checked for potential links. 2039 */ 2040 private void attemptNetworkLinking(WifiConfiguration config) { 2041 // Only link WPA_PSK config. 2042 if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 2043 return; 2044 } 2045 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId); 2046 // Ignore configurations with large number of BSSIDs. 2047 if (scanDetailCache != null 2048 && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) { 2049 return; 2050 } 2051 for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) { 2052 if (linkConfig.configKey().equals(config.configKey())) { 2053 continue; 2054 } 2055 if (linkConfig.ephemeral) { 2056 continue; 2057 } 2058 // Network Selector will be allowed to dynamically jump from a linked configuration 2059 // to another, hence only link configurations that have WPA_PSK security type. 2060 if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 2061 continue; 2062 } 2063 ScanDetailCache linkScanDetailCache = 2064 getScanDetailCacheForNetwork(linkConfig.networkId); 2065 // Ignore configurations with large number of BSSIDs. 2066 if (linkScanDetailCache != null 2067 && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) { 2068 continue; 2069 } 2070 // Check if the networks should be linked/unlinked. 2071 if (shouldNetworksBeLinked( 2072 config, linkConfig, scanDetailCache, linkScanDetailCache)) { 2073 linkNetworks(config, linkConfig); 2074 } else { 2075 unlinkNetworks(config, linkConfig); 2076 } 2077 } 2078 } 2079 2080 /** 2081 * Helper method to fetch list of channels for a network from the associated ScanResult's cache 2082 * and add it to the provided channel as long as the size of the set is less than 2083 * |maxChannelSetSize|. 2084 * 2085 * @param channelSet Channel set holding all the channels for the network. 2086 * @param scanDetailCache ScanDetailCache entry associated with the network. 2087 * @param nowInMillis current timestamp to be used for age comparison. 2088 * @param ageInMillis only consider scan details whose timestamps are earlier than this 2089 * value. 2090 * @param maxChannelSetSize Maximum number of channels to be added to the set. 2091 * @return false if the list is full, true otherwise. 2092 */ 2093 private boolean addToChannelSetForNetworkFromScanDetailCache( 2094 Set<Integer> channelSet, ScanDetailCache scanDetailCache, 2095 long nowInMillis, long ageInMillis, int maxChannelSetSize) { 2096 if (scanDetailCache != null && scanDetailCache.size() > 0) { 2097 for (ScanDetail scanDetail : scanDetailCache.values()) { 2098 ScanResult result = scanDetail.getScanResult(); 2099 boolean valid = (nowInMillis - result.seen) < ageInMillis; 2100 if (mVerboseLoggingEnabled) { 2101 Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq " 2102 + result.frequency + " age " + (nowInMillis - result.seen) 2103 + " ?=" + valid); 2104 } 2105 if (valid) { 2106 channelSet.add(result.frequency); 2107 } 2108 if (channelSet.size() >= maxChannelSetSize) { 2109 return false; 2110 } 2111 } 2112 } 2113 return true; 2114 } 2115 2116 /** 2117 * Retrieve a set of channels on which AP's for the provided network was seen using the 2118 * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial 2119 * scans for the currently connected network. 2120 * 2121 * @param networkId network ID corresponding to the network. 2122 * @param ageInMillis only consider scan details whose timestamps are earlier than this value. 2123 * @param homeChannelFreq frequency of the currently connected network. 2124 * @return Set containing the frequencies on which this network was found, null if the network 2125 * was not found or there are no associated scan details in the cache. 2126 */ 2127 public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis, 2128 int homeChannelFreq) { 2129 WifiConfiguration config = getInternalConfiguredNetwork(networkId); 2130 if (config == null) { 2131 return null; 2132 } 2133 ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId); 2134 if (scanDetailCache == null && config.linkedConfigurations == null) { 2135 Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId); 2136 return null; 2137 } 2138 if (mVerboseLoggingEnabled) { 2139 StringBuilder dbg = new StringBuilder(); 2140 dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ") 2141 .append(ageInMillis) 2142 .append(" for ") 2143 .append(config.configKey()) 2144 .append(" max ") 2145 .append(mMaxNumActiveChannelsForPartialScans); 2146 if (scanDetailCache != null) { 2147 dbg.append(" bssids " + scanDetailCache.size()); 2148 } 2149 if (config.linkedConfigurations != null) { 2150 dbg.append(" linked " + config.linkedConfigurations.size()); 2151 } 2152 Log.v(TAG, dbg.toString()); 2153 } 2154 Set<Integer> channelSet = new HashSet<>(); 2155 2156 // First add the currently connected network channel. 2157 if (homeChannelFreq > 0) { 2158 channelSet.add(homeChannelFreq); 2159 if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) { 2160 return channelSet; 2161 } 2162 } 2163 2164 long nowInMillis = mClock.getWallClockMillis(); 2165 2166 // Then get channels for the network. 2167 if (!addToChannelSetForNetworkFromScanDetailCache( 2168 channelSet, scanDetailCache, nowInMillis, ageInMillis, 2169 mMaxNumActiveChannelsForPartialScans)) { 2170 return channelSet; 2171 } 2172 2173 // Lastly get channels for linked networks. 2174 if (config.linkedConfigurations != null) { 2175 for (String configKey : config.linkedConfigurations.keySet()) { 2176 WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey); 2177 if (linkedConfig == null) { 2178 continue; 2179 } 2180 ScanDetailCache linkedScanDetailCache = 2181 getScanDetailCacheForNetwork(linkedConfig.networkId); 2182 if (!addToChannelSetForNetworkFromScanDetailCache( 2183 channelSet, linkedScanDetailCache, nowInMillis, ageInMillis, 2184 mMaxNumActiveChannelsForPartialScans)) { 2185 break; 2186 } 2187 } 2188 } 2189 return channelSet; 2190 } 2191 2192 /** 2193 * Retrieves a list of all the saved networks before enabling disconnected/connected PNO. 2194 * 2195 * PNO network list sent to the firmware has limited size. If there are a lot of saved 2196 * networks, this list will be truncated and we might end up not sending the networks 2197 * with the highest chance of connecting to the firmware. 2198 * So, re-sort the network list based on the frequency of connection to those networks 2199 * and whether it was last seen in the scan results. 2200 * 2201 * TODO (b/30399964): Recalculate the list whenever network status changes. 2202 * @return list of networks with updated priorities. 2203 */ 2204 public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() { 2205 List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); 2206 List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks()); 2207 // Remove any permanently disabled networks. 2208 Iterator<WifiConfiguration> iter = networks.iterator(); 2209 while (iter.hasNext()) { 2210 WifiConfiguration config = iter.next(); 2211 if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) { 2212 iter.remove(); 2213 } 2214 } 2215 Collections.sort(networks, sScanListComparator); 2216 // Let's use the network list size - 1 as the highest priority and then go down from there. 2217 // So, the most frequently connected network has the highest priority now. 2218 int priority = networks.size() - 1; 2219 for (WifiConfiguration config : networks) { 2220 pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority)); 2221 priority--; 2222 } 2223 return pnoList; 2224 } 2225 2226 /** 2227 * Retrieves a list of all the saved hidden networks for scans. 2228 * 2229 * Hidden network list sent to the firmware has limited size. If there are a lot of saved 2230 * networks, this list will be truncated and we might end up not sending the networks 2231 * with the highest chance of connecting to the firmware. 2232 * So, re-sort the network list based on the frequency of connection to those networks 2233 * and whether it was last seen in the scan results. 2234 * 2235 * @return list of networks with updated priorities. 2236 */ 2237 public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() { 2238 List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>(); 2239 List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks()); 2240 // Remove any permanently disabled networks or non hidden networks. 2241 Iterator<WifiConfiguration> iter = networks.iterator(); 2242 while (iter.hasNext()) { 2243 WifiConfiguration config = iter.next(); 2244 if (!config.hiddenSSID || 2245 config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) { 2246 iter.remove(); 2247 } 2248 } 2249 Collections.sort(networks, sScanListComparator); 2250 // Let's use the network list size - 1 as the highest priority and then go down from there. 2251 // So, the most frequently connected network has the highest priority now. 2252 int priority = networks.size() - 1; 2253 for (WifiConfiguration config : networks) { 2254 hiddenList.add( 2255 new WifiScanner.ScanSettings.HiddenNetwork(config.SSID)); 2256 priority--; 2257 } 2258 return hiddenList; 2259 } 2260 2261 /** 2262 * Check if the provided ephemeral network was deleted by the user or not. 2263 * 2264 * @param ssid caller must ensure that the SSID passed thru this API match 2265 * the WifiConfiguration.SSID rules, and thus be surrounded by quotes. 2266 * @return true if network was deleted, false otherwise. 2267 */ 2268 public boolean wasEphemeralNetworkDeleted(String ssid) { 2269 return mDeletedEphemeralSSIDs.contains(ssid); 2270 } 2271 2272 /** 2273 * Disable an ephemeral SSID for the purpose of network selection. 2274 * 2275 * The only way to "un-disable it" is if the user create a network for that SSID and then 2276 * forget it. 2277 * 2278 * @param ssid caller must ensure that the SSID passed thru this API match 2279 * the WifiConfiguration.SSID rules, and thus be surrounded by quotes. 2280 * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can 2281 * disconnect if this is the current network. 2282 */ 2283 public WifiConfiguration disableEphemeralNetwork(String ssid) { 2284 if (ssid == null) { 2285 return null; 2286 } 2287 WifiConfiguration foundConfig = null; 2288 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2289 if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) { 2290 foundConfig = config; 2291 break; 2292 } 2293 } 2294 mDeletedEphemeralSSIDs.add(ssid); 2295 Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size()); 2296 if (foundConfig != null) { 2297 Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: " 2298 + foundConfig.networkId); 2299 } 2300 return foundConfig; 2301 } 2302 2303 /** 2304 * Resets all sim networks state. 2305 */ 2306 public void resetSimNetworks() { 2307 if (mVerboseLoggingEnabled) localLog("resetSimNetworks"); 2308 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2309 if (TelephonyUtil.isSimConfig(config)) { 2310 String currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config); 2311 // Update the loaded config 2312 config.enterpriseConfig.setIdentity(currentIdentity); 2313 config.enterpriseConfig.setAnonymousIdentity(""); 2314 } 2315 } 2316 } 2317 2318 /** 2319 * Any network using certificates to authenticate access requires unlocked key store; unless 2320 * the certificates can be stored with hardware encryption 2321 * 2322 * @return true if we need an unlocked keystore, false otherwise. 2323 */ 2324 public boolean needsUnlockedKeyStore() { 2325 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2326 if (WifiConfigurationUtil.isConfigForEapNetwork(config)) { 2327 if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 2328 return true; 2329 } 2330 } 2331 } 2332 return false; 2333 } 2334 2335 /** 2336 * Helper method to perform the following operations during user switch/unlock: 2337 * - Remove private networks of the old user. 2338 * - Load from the new user store file. 2339 * - Save the store files again to migrate any user specific networks from the shared store 2340 * to user store. 2341 * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller 2342 * should ensure that the stores are accessible before invocation. 2343 * 2344 * @param userId The identifier of the new foreground user, after the unlock or switch. 2345 */ 2346 private void handleUserUnlockOrSwitch(int userId) { 2347 if (mVerboseLoggingEnabled) { 2348 Log.v(TAG, "Loading from store after user switch/unlock for " + userId); 2349 } 2350 // Switch out the user store file. 2351 if (loadFromUserStoreAfterUnlockOrSwitch(userId)) { 2352 saveToStore(true); 2353 mPendingUnlockStoreRead = false; 2354 } 2355 } 2356 2357 /** 2358 * Handles the switch to a different foreground user: 2359 * - Flush the current state to the old user's store file. 2360 * - Switch the user specific store file. 2361 * - Reload the networks from the store files (shared & user). 2362 * - Write the store files to move any user specific private networks from shared store to user 2363 * store. 2364 * 2365 * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked. 2366 * 2367 * @param userId The identifier of the new foreground user, after the switch. 2368 * @return List of network ID's of all the private networks of the old user which will be 2369 * removed from memory. 2370 */ 2371 public Set<Integer> handleUserSwitch(int userId) { 2372 if (mVerboseLoggingEnabled) { 2373 Log.v(TAG, "Handling user switch for " + userId); 2374 } 2375 if (userId == mCurrentUserId) { 2376 Log.w(TAG, "User already in foreground " + userId); 2377 return new HashSet<>(); 2378 } 2379 if (mPendingStoreRead) { 2380 Log.wtf(TAG, "Unexpected user switch before store is read!"); 2381 return new HashSet<>(); 2382 } 2383 if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { 2384 saveToStore(true); 2385 } 2386 // Remove any private networks of the old user before switching the userId. 2387 Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId); 2388 mConfiguredNetworks.setNewUser(userId); 2389 mCurrentUserId = userId; 2390 2391 if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { 2392 handleUserUnlockOrSwitch(mCurrentUserId); 2393 } else { 2394 // Cannot read data from new user's CE store file before they log-in. 2395 mPendingUnlockStoreRead = true; 2396 Log.i(TAG, "Waiting for user unlock to load from store"); 2397 } 2398 return removedNetworkIds; 2399 } 2400 2401 /** 2402 * Handles the unlock of foreground user. This maybe needed to read the store file if the user's 2403 * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked. 2404 * 2405 * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked. 2406 * 2407 * @param userId The identifier of the user that unlocked. 2408 */ 2409 public void handleUserUnlock(int userId) { 2410 if (mVerboseLoggingEnabled) { 2411 Log.v(TAG, "Handling user unlock for " + userId); 2412 } 2413 if (mPendingStoreRead) { 2414 Log.w(TAG, "Ignore user unlock until store is read!"); 2415 mDeferredUserUnlockRead = true; 2416 return; 2417 } 2418 if (userId == mCurrentUserId && mPendingUnlockStoreRead) { 2419 handleUserUnlockOrSwitch(mCurrentUserId); 2420 } 2421 } 2422 2423 /** 2424 * Handles the stop of foreground user. This is needed to write the store file to flush 2425 * out any pending data before the user's CE store storage is unavailable. 2426 * 2427 * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked. 2428 * 2429 * @param userId The identifier of the user that stopped. 2430 */ 2431 public void handleUserStop(int userId) { 2432 if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { 2433 saveToStore(true); 2434 clearInternalData(); 2435 mCurrentUserId = UserHandle.USER_SYSTEM; 2436 } 2437 } 2438 2439 /** 2440 * Helper method to clear internal databases. 2441 * This method clears the: 2442 * - List of configured networks. 2443 * - Map of scan detail caches. 2444 * - List of deleted ephemeral networks. 2445 */ 2446 private void clearInternalData() { 2447 mConfiguredNetworks.clear(); 2448 mDeletedEphemeralSSIDs.clear(); 2449 mScanDetailCaches.clear(); 2450 clearLastSelectedNetwork(); 2451 } 2452 2453 /** 2454 * Helper method to clear internal databases of the specified user. 2455 * This method clears the: 2456 * - Private configured configured networks of the specified user. 2457 * - Map of scan detail caches. 2458 * - List of deleted ephemeral networks. 2459 * 2460 * @param userId The identifier of the current foreground user, before the switch. 2461 * @return List of network ID's of all the private networks of the old user which will be 2462 * removed from memory. 2463 */ 2464 private Set<Integer> clearInternalUserData(int userId) { 2465 Set<Integer> removedNetworkIds = new HashSet<>(); 2466 // Remove any private networks of the old user before switching the userId. 2467 for (WifiConfiguration config : getInternalConfiguredNetworks()) { 2468 if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile( 2469 config.creatorUid, mUserManager.getProfiles(userId))) { 2470 removedNetworkIds.add(config.networkId); 2471 mConfiguredNetworks.remove(config.networkId); 2472 } 2473 } 2474 mDeletedEphemeralSSIDs.clear(); 2475 mScanDetailCaches.clear(); 2476 clearLastSelectedNetwork(); 2477 return removedNetworkIds; 2478 } 2479 2480 /** 2481 * Helper function to populate the internal (in-memory) data from the retrieved shared store 2482 * (file) data. 2483 * 2484 * @param configurations list of configurations retrieved from store. 2485 */ 2486 private void loadInternalDataFromSharedStore( 2487 List<WifiConfiguration> configurations) { 2488 for (WifiConfiguration configuration : configurations) { 2489 configuration.networkId = mNextNetworkId++; 2490 if (mVerboseLoggingEnabled) { 2491 Log.v(TAG, "Adding network from shared store " + configuration.configKey()); 2492 } 2493 mConfiguredNetworks.put(configuration); 2494 } 2495 } 2496 2497 /** 2498 * Helper function to populate the internal (in-memory) data from the retrieved user store 2499 * (file) data. 2500 * 2501 * @param configurations list of configurations retrieved from store. 2502 * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by 2503 * the user. 2504 */ 2505 private void loadInternalDataFromUserStore( 2506 List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) { 2507 for (WifiConfiguration configuration : configurations) { 2508 configuration.networkId = mNextNetworkId++; 2509 if (mVerboseLoggingEnabled) { 2510 Log.v(TAG, "Adding network from user store " + configuration.configKey()); 2511 } 2512 mConfiguredNetworks.put(configuration); 2513 } 2514 for (String ssid : deletedEphemeralSSIDs) { 2515 mDeletedEphemeralSSIDs.add(ssid); 2516 } 2517 } 2518 2519 /** 2520 * Helper function to populate the internal (in-memory) data from the retrieved stores (file) 2521 * data. 2522 * This method: 2523 * 1. Clears all existing internal data. 2524 * 2. Sends out the networks changed broadcast after loading all the data. 2525 * 2526 * @param sharedConfigurations list of network configurations retrieved from shared store. 2527 * @param userConfigurations list of network configurations retrieved from user store. 2528 * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by 2529 * the user. 2530 */ 2531 private void loadInternalData( 2532 List<WifiConfiguration> sharedConfigurations, 2533 List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) { 2534 // Clear out all the existing in-memory lists and load the lists from what was retrieved 2535 // from the config store. 2536 clearInternalData(); 2537 loadInternalDataFromSharedStore(sharedConfigurations); 2538 loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs); 2539 if (mConfiguredNetworks.sizeForAllUsers() == 0) { 2540 Log.w(TAG, "No stored networks found."); 2541 } 2542 sendConfiguredNetworksChangedBroadcast(); 2543 mPendingStoreRead = false; 2544 } 2545 2546 /** 2547 * Migrate data from legacy store files. The function performs the following operations: 2548 * 1. Check if the legacy store files are present. 2549 * 2. If yes, read all the data from the store files. 2550 * 3. Save it to the new store files. 2551 * 4. Delete the legacy store file. 2552 * 2553 * @return true if migration was successful or not needed (fresh install), false if it failed. 2554 */ 2555 public boolean migrateFromLegacyStore() { 2556 if (!mWifiConfigStoreLegacy.areStoresPresent()) { 2557 Log.d(TAG, "Legacy store files not found. No migration needed!"); 2558 return true; 2559 } 2560 WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read(); 2561 Log.d(TAG, "Reading from legacy store completed"); 2562 loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(), 2563 storeData.getDeletedEphemeralSSIDs()); 2564 2565 // Setup user store for the current user in case it have not setup yet, so that data 2566 // owned by the current user will be backed to the user store. 2567 if (mDeferredUserUnlockRead) { 2568 mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId)); 2569 mDeferredUserUnlockRead = false; 2570 } 2571 2572 if (!saveToStore(true)) { 2573 return false; 2574 } 2575 mWifiConfigStoreLegacy.removeStores(); 2576 Log.d(TAG, "Migration from legacy store completed"); 2577 return true; 2578 } 2579 2580 /** 2581 * Read the config store and load the in-memory lists from the store data retrieved and sends 2582 * out the networks changed broadcast. 2583 * 2584 * This reads all the network configurations from: 2585 * 1. Shared WifiConfigStore.xml 2586 * 2. User WifiConfigStore.xml 2587 * 2588 * @return true on success or not needed (fresh install/pending legacy store migration), 2589 * false otherwise. 2590 */ 2591 public boolean loadFromStore() { 2592 if (!mWifiConfigStore.areStoresPresent()) { 2593 Log.d(TAG, "New store files not found. No saved networks loaded!"); 2594 if (!mWifiConfigStoreLegacy.areStoresPresent()) { 2595 // No legacy store files either, so reset the pending store read flag. 2596 mPendingStoreRead = false; 2597 } 2598 return true; 2599 } 2600 // If the user unlock comes in before we load from store, which means the user store have 2601 // not been setup yet for the current user. Setup the user store before the read so that 2602 // configurations for the current user will also being loaded. 2603 if (mDeferredUserUnlockRead) { 2604 Log.i(TAG, "Handling user unlock before loading from store."); 2605 mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId)); 2606 mDeferredUserUnlockRead = false; 2607 } 2608 try { 2609 mWifiConfigStore.read(); 2610 } catch (IOException e) { 2611 Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e); 2612 return false; 2613 } catch (XmlPullParserException e) { 2614 Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e); 2615 return false; 2616 } 2617 loadInternalData(mNetworkListStoreData.getSharedConfigurations(), 2618 mNetworkListStoreData.getUserConfigurations(), 2619 mDeletedEphemeralSsidsStoreData.getSsidList()); 2620 return true; 2621 } 2622 2623 /** 2624 * Read the user config store and load the in-memory lists from the store data retrieved and 2625 * sends out the networks changed broadcast. 2626 * This should be used for all user switches/unlocks to only load networks from the user 2627 * specific store and avoid reloading the shared networks. 2628 * 2629 * This reads all the network configurations from: 2630 * 1. User WifiConfigStore.xml 2631 * 2632 * @param userId The identifier of the foreground user. 2633 * @return true on success, false otherwise. 2634 */ 2635 public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) { 2636 try { 2637 mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId)); 2638 } catch (IOException e) { 2639 Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e); 2640 return false; 2641 } catch (XmlPullParserException e) { 2642 Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" + 2643 "lost!", e); 2644 return false; 2645 } 2646 loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(), 2647 mDeletedEphemeralSsidsStoreData.getSsidList()); 2648 return true; 2649 } 2650 2651 /** 2652 * Save the current snapshot of the in-memory lists to the config store. 2653 * 2654 * @param forceWrite Whether the write needs to be forced or not. 2655 * @return Whether the write was successful or not, this is applicable only for force writes. 2656 */ 2657 public boolean saveToStore(boolean forceWrite) { 2658 ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>(); 2659 ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>(); 2660 // List of network IDs for legacy Passpoint configuration to be removed. 2661 List<Integer> legacyPasspointNetId = new ArrayList<>(); 2662 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 2663 // Ignore ephemeral networks and non-legacy Passpoint configurations. 2664 if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) { 2665 continue; 2666 } 2667 2668 // Migrate the legacy Passpoint configurations owned by the current user to 2669 // {@link PasspointManager}. 2670 if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile( 2671 config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) { 2672 legacyPasspointNetId.add(config.networkId); 2673 // Migrate the legacy Passpoint configuration and add it to PasspointManager. 2674 if (!PasspointManager.addLegacyPasspointConfig(config)) { 2675 Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN); 2676 } 2677 // This will prevent adding |config| to the |sharedConfigurations|. 2678 continue; 2679 } 2680 2681 // We push all shared networks & private networks not belonging to the current 2682 // user to the shared store. Ideally, private networks for other users should 2683 // not even be in memory, 2684 // But, this logic is in place to deal with store migration from N to O 2685 // because all networks were previously stored in a central file. We cannot 2686 // write these private networks to the user specific store until the corresponding 2687 // user logs in. 2688 if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile( 2689 config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) { 2690 sharedConfigurations.add(config); 2691 } else { 2692 userConfigurations.add(config); 2693 } 2694 } 2695 2696 // Remove the configurations for migrated Passpoint configurations. 2697 for (int networkId : legacyPasspointNetId) { 2698 mConfiguredNetworks.remove(networkId); 2699 } 2700 2701 // Setup store data for write. 2702 mNetworkListStoreData.setSharedConfigurations(sharedConfigurations); 2703 mNetworkListStoreData.setUserConfigurations(userConfigurations); 2704 mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs); 2705 2706 try { 2707 mWifiConfigStore.write(forceWrite); 2708 } catch (IOException e) { 2709 Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e); 2710 return false; 2711 } catch (XmlPullParserException e) { 2712 Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e); 2713 return false; 2714 } 2715 return true; 2716 } 2717 2718 /** 2719 * Helper method for logging into local log buffer. 2720 */ 2721 private void localLog(String s) { 2722 if (mLocalLog != null) { 2723 mLocalLog.log(s); 2724 } 2725 } 2726 2727 /** 2728 * Dump the local log buffer and other internal state of WifiConfigManager. 2729 */ 2730 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2731 pw.println("Dump of WifiConfigManager"); 2732 pw.println("WifiConfigManager - Log Begin ----"); 2733 mLocalLog.dump(fd, pw, args); 2734 pw.println("WifiConfigManager - Log End ----"); 2735 pw.println("WifiConfigManager - Configured networks Begin ----"); 2736 for (WifiConfiguration network : getInternalConfiguredNetworks()) { 2737 pw.println(network); 2738 } 2739 pw.println("WifiConfigManager - Configured networks End ----"); 2740 pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId); 2741 pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId); 2742 } 2743 2744 /** 2745 * Returns true if the given uid has permission to add, update or remove proxy settings 2746 */ 2747 private boolean canModifyProxySettings(int uid) { 2748 final DevicePolicyManagerInternal dpmi = 2749 mWifiPermissionsWrapper.getDevicePolicyManagerInternal(); 2750 final boolean isUidProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 2751 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); 2752 final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 2753 DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 2754 final boolean hasConfigOverridePermission = 2755 mWifiPermissionsUtil.checkConfigOverridePermission(uid); 2756 // If |uid| corresponds to the device owner, allow all modifications. 2757 if (isUidDeviceOwner || isUidProfileOwner || hasConfigOverridePermission) { 2758 return true; 2759 } 2760 if (mVerboseLoggingEnabled) { 2761 Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings." 2762 + " ConfigOverride=" + hasConfigOverridePermission 2763 + " DeviceOwner=" + isUidDeviceOwner 2764 + " ProfileOwner=" + isUidProfileOwner); 2765 } 2766 return false; 2767 } 2768} 2769