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