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