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