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