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