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