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