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