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