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