WifiConfigManager.java revision fdbf9d334419ff5aff35bf56f7cf46d01998861b
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi; 18 19import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 20 21import android.app.admin.DeviceAdminInfo; 22import android.app.admin.DevicePolicyManagerInternal; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.pm.ApplicationInfo; 27import android.content.pm.PackageManager; 28import android.content.pm.UserInfo; 29import android.net.IpConfiguration; 30import android.net.IpConfiguration.IpAssignment; 31import android.net.IpConfiguration.ProxySettings; 32import android.net.NetworkInfo.DetailedState; 33import android.net.ProxyInfo; 34import android.net.StaticIpConfiguration; 35import android.net.wifi.PasspointManagementObjectDefinition; 36import android.net.wifi.ScanResult; 37import android.net.wifi.WifiConfiguration; 38import android.net.wifi.WifiConfiguration.KeyMgmt; 39import android.net.wifi.WifiConfiguration.Status; 40import android.net.wifi.WifiEnterpriseConfig; 41import android.net.wifi.WifiInfo; 42import android.net.wifi.WifiManager; 43import android.net.wifi.WifiScanner; 44import android.net.wifi.WpsInfo; 45import android.net.wifi.WpsResult; 46import android.os.Environment; 47import android.os.RemoteException; 48import android.os.SystemClock; 49import android.os.UserHandle; 50import android.os.UserManager; 51import android.provider.Settings; 52import android.security.KeyStore; 53import android.text.TextUtils; 54import android.util.LocalLog; 55import android.util.Log; 56import android.util.SparseArray; 57 58import com.android.internal.R; 59import com.android.server.LocalServices; 60import com.android.server.net.DelayedDiskWrite; 61import com.android.server.net.IpConfigStore; 62import com.android.server.wifi.anqp.ANQPElement; 63import com.android.server.wifi.anqp.ANQPFactory; 64import com.android.server.wifi.anqp.Constants; 65import com.android.server.wifi.hotspot2.ANQPData; 66import com.android.server.wifi.hotspot2.AnqpCache; 67import com.android.server.wifi.hotspot2.IconEvent; 68import com.android.server.wifi.hotspot2.NetworkDetail; 69import com.android.server.wifi.hotspot2.PasspointMatch; 70import com.android.server.wifi.hotspot2.SupplicantBridge; 71import com.android.server.wifi.hotspot2.Utils; 72import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager; 73import com.android.server.wifi.hotspot2.pps.Credential; 74import com.android.server.wifi.hotspot2.pps.HomeSP; 75 76import org.xml.sax.SAXException; 77 78import java.io.BufferedReader; 79import java.io.DataOutputStream; 80import java.io.File; 81import java.io.FileDescriptor; 82import java.io.FileNotFoundException; 83import java.io.FileReader; 84import java.io.IOException; 85import java.io.PrintWriter; 86import java.text.DateFormat; 87import java.util.ArrayList; 88import java.util.Calendar; 89import java.util.Collection; 90import java.util.Collections; 91import java.util.Comparator; 92import java.util.Date; 93import java.util.HashMap; 94import java.util.HashSet; 95import java.util.List; 96import java.util.Map; 97import java.util.Objects; 98import java.util.Set; 99import java.util.concurrent.ConcurrentHashMap; 100import java.util.concurrent.atomic.AtomicBoolean; 101import java.util.concurrent.atomic.AtomicInteger; 102import java.util.zip.CRC32; 103import java.util.zip.Checksum; 104 105 106/** 107 * This class provides the API to manage configured 108 * wifi networks. The API is not thread safe is being 109 * used only from WifiStateMachine. 110 * 111 * It deals with the following 112 * - Add/update/remove a WifiConfiguration 113 * The configuration contains two types of information. 114 * = IP and proxy configuration that is handled by WifiConfigManager and 115 * is saved to disk on any change. 116 * 117 * The format of configuration file is as follows: 118 * <version> 119 * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS> 120 * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS> 121 * .. 122 * 123 * (key, value) pairs for a given network are grouped together and can 124 * be in any order. A EOS at the end of a set of (key, value) pairs 125 * indicates that the next set of (key, value) pairs are for a new 126 * network. A network is identified by a unique ID_KEY. If there is no 127 * ID_KEY in the (key, value) pairs, the data is discarded. 128 * 129 * An invalid version on read would result in discarding the contents of 130 * the file. On the next write, the latest version is written to file. 131 * 132 * Any failures during read or write to the configuration file are ignored 133 * without reporting to the user since the likelihood of these errors are 134 * low and the impact on connectivity is low. 135 * 136 * = SSID & security details that is pushed to the supplicant. 137 * supplicant saves these details to the disk on calling 138 * saveConfigCommand(). 139 * 140 * We have two kinds of APIs exposed: 141 * > public API calls that provide fine grained control 142 * - enableNetwork, disableNetwork, addOrUpdateNetwork(), 143 * removeNetwork(). For these calls, the config is not persisted 144 * to the disk. (TODO: deprecate these calls in WifiManager) 145 * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork(). 146 * These calls persist the supplicant config to disk. 147 * 148 * - Maintain a list of configured networks for quick access 149 * 150 */ 151public class WifiConfigManager { 152 153 private Context mContext; 154 public static final String TAG = "WifiConfigManager"; 155 public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8; 156 public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16; 157 public static final int MAX_TX_PACKET_FOR_PARTIAL_SCANS = 40; 158 public static final int MAX_RX_PACKET_FOR_PARTIAL_SCANS = 80; 159 public static final boolean ROAM_ON_ANY = false; 160 public static final int MAX_NUM_SCAN_CACHE_ENTRIES = 128; 161 162 /* Enterprise configuration keys */ 163 /** 164 * In old configurations, the "private_key" field was used. However, newer 165 * configurations use the key_id field with the engine_id set to "keystore". 166 * If this field is found in the configuration, the migration code is 167 * triggered. 168 */ 169 public static final String OLD_PRIVATE_KEY_NAME = "private_key"; 170 171 private static boolean sVDBG = false; 172 private static boolean sVVDBG = false; 173 174 private static final boolean DBG = true; 175 private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf"; 176 private static final String IP_CONFIG_FILE = 177 Environment.getDataDirectory() + "/misc/wifi/ipconfig.txt"; 178 179 // The Wifi verbose log is provided as a way to persist the verbose logging settings 180 // for testing purpose. 181 // It is not intended for normal use. 182 private static final String WIFI_VERBOSE_LOGS_KEY = "WIFI_VERBOSE_LOGS"; 183 184 // As we keep deleted PSK WifiConfiguration for a while, the PSK of 185 // those deleted WifiConfiguration is set to this random unused PSK 186 private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted"; 187 188 /** 189 * The maximum number of times we will retry a connection to an access point 190 * for which we have failed in acquiring an IP address from DHCP. A value of 191 * N means that we will make N+1 connection attempts in all. 192 * <p> 193 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default 194 * value if a Settings value is not present. 195 */ 196 private static final int DEFAULT_MAX_DHCP_RETRIES = 9; 197 198 /** 199 * The threshold for each kind of error. If a network continuously encounter the same error more 200 * than the threshold times, this network will be disabled. -1 means unavailable. 201 */ 202 private static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = { 203 -1, // threshold for NETWORK_SELECTION_ENABLE 204 1, // threshold for DISABLED_BAD_LINK 205 5, // threshold for DISABLED_ASSOCIATION_REJECTION 206 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 207 5, // threshold for DISABLED_DHCP_FAILURE 208 5, // threshold for DISABLED_DNS_FAILURE 209 6, // threshold for DISABLED_TLS_VERSION_MISMATCH 210 1, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 211 1, // threshold for DISABLED_NO_INTERNET 212 1 // threshold for DISABLED_BY_WIFI_MANAGER 213 }; 214 215 /** 216 * Timeout for each kind of error. After the timeout minutes, unblock the network again. 217 */ 218 private static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT = { 219 Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE 220 15, // threshold for DISABLED_BAD_LINK 221 5, // threshold for DISABLED_ASSOCIATION_REJECTION 222 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 223 5, // threshold for DISABLED_DHCP_FAILURE 224 5, // threshold for DISABLED_DNS_FAILURE 225 Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION 226 Integer.MAX_VALUE, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 227 Integer.MAX_VALUE, // threshold for DISABLED_NO_INTERNET 228 Integer.MAX_VALUE // threshold for DISABLED_BY_WIFI_MANAGER 229 }; 230 231 public final AtomicBoolean mEnableAutoJoinWhenAssociated = new AtomicBoolean(); 232 public final AtomicBoolean mEnableFullBandScanWhenAssociated = new AtomicBoolean(true); 233 public final AtomicBoolean mEnableChipWakeUpWhenAssociated = new AtomicBoolean(true); 234 public final AtomicBoolean mEnableRssiPollWhenAssociated = new AtomicBoolean(true); 235 public final AtomicInteger mThresholdSaturatedRssi5 = new AtomicInteger(); 236 public final AtomicInteger mThresholdQualifiedRssi24 = new AtomicInteger(); 237 public final AtomicInteger mEnableVerboseLogging = new AtomicInteger(0); 238 public final AtomicInteger mAssociatedFullScanBackoff = 239 new AtomicInteger(); // Will be divided by 8 by WifiStateMachine 240 public final AtomicInteger mAlwaysEnableScansWhileAssociated = new AtomicInteger(0); 241 public final AtomicInteger mMaxNumActiveChannelsForPartialScans = new AtomicInteger(); 242 public final AtomicInteger mWifiDisconnectedShortScanIntervalMs = new AtomicInteger(); 243 public final AtomicInteger mWifiAssociatedShortScanIntervalMs = new AtomicInteger(); 244 245 public boolean mEnableLinkDebouncing; 246 public boolean mEnableWifiCellularHandoverUserTriggeredAdjustment; 247 public int mAssociatedFullScanMaxIntervalMs; 248 public int mNetworkSwitchingBlackListPeriodMs; 249 public int mBadLinkSpeed24; 250 public int mBadLinkSpeed5; 251 public int mGoodLinkSpeed24; 252 public int mGoodLinkSpeed5; 253 254 // These fields are non-final for testing. 255 public AtomicInteger mThresholdQualifiedRssi5 = new AtomicInteger(); 256 public AtomicInteger mThresholdMinimumRssi5 = new AtomicInteger(); 257 public AtomicInteger mThresholdSaturatedRssi24 = new AtomicInteger(); 258 public AtomicInteger mThresholdMinimumRssi24 = new AtomicInteger(); 259 public AtomicInteger mCurrentNetworkBoost = new AtomicInteger(); 260 public AtomicInteger mBandAward5Ghz = new AtomicInteger(); 261 262 /** 263 * If Connectivity Service has triggered an unwanted network disconnect 264 */ 265 public long mLastUnwantedNetworkDisconnectTimestamp = 0; 266 267 /** 268 * Framework keeps a list of ephemeral SSIDs that where deleted by user, 269 * so as, framework knows not to autojoin again those SSIDs based on scorer input. 270 * The list is never cleared up. 271 * 272 * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field. 273 */ 274 public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>(); 275 276 /* configured networks with network id as the key */ 277 private final ConfigurationMap mConfiguredNetworks; 278 279 private final LocalLog mLocalLog; 280 private final KeyStore mKeyStore; 281 private final WifiNetworkHistory mWifiNetworkHistory; 282 private final WifiConfigStore mWifiConfigStore; 283 private final AnqpCache mAnqpCache; 284 private final SupplicantBridge mSupplicantBridge; 285 private final SupplicantBridgeCallbacks mSupplicantBridgeCallbacks; 286 private final PasspointManagementObjectManager mMOManager; 287 private final boolean mEnableOsuQueries; 288 private final SIMAccessor mSIMAccessor; 289 private final UserManager mUserManager; 290 291 private FrameworkFacade mFacade; 292 private Clock mClock; 293 private boolean mOnlyLinkSameCredentialConfigurations; 294 private IpConfigStore mIpconfigStore; 295 private DelayedDiskWrite mWriter; 296 private int mCurrentUserId = UserHandle.USER_SYSTEM; 297 298 /* Stores a map of NetworkId to ScanCache */ 299 private ConcurrentHashMap<Integer, ScanDetailCache> mScanDetailCaches; 300 301 /** 302 * Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user, 303 * so as, framework knows not to re-add those SSIDs automatically to the Saved networks 304 */ 305 private Set<Long> mDeletedSSIDs = new HashSet<Long>(); 306 307 /* Tracks the highest priority of configured networks */ 308 private int mLastPriority = -1; 309 310 // TODO set this back to false, used for debugging b/17516271. 311 private boolean mShowNetworks = true; 312 313 /** 314 * The mLastSelectedConfiguration is used to remember which network 315 * was selected last by the user. 316 * The connection to this network may not be successful, as well 317 * the selection (i.e. network priority) might not be persisted. 318 * WiFi state machine is the only object that sets this variable. 319 */ 320 private String mLastSelectedConfiguration = null; 321 private long mLastSelectedTimeStamp = 322 WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; 323 324 /* 325 * Lost config list, whenever we read a config from networkHistory.txt that was not in 326 * wpa_supplicant.conf 327 */ 328 private HashSet<String> mLostConfigsDbg = new HashSet<String>(); 329 330 private ScanDetail mActiveScanDetail; // ScanDetail associated with active network 331 private final Object mActiveScanDetailLock = new Object(); 332 333 private class SupplicantBridgeCallbacks implements SupplicantBridge.SupplicantBridgeCallbacks { 334 @Override 335 public void notifyANQPResponse(ScanDetail scanDetail, 336 Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 337 updateAnqpCache(scanDetail, anqpElements); 338 if (anqpElements == null || anqpElements.isEmpty()) { 339 return; 340 } 341 scanDetail.propagateANQPInfo(anqpElements); 342 343 Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false); 344 Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() + " pass 2 matches: " 345 + toMatchString(matches)); 346 347 cacheScanResultForPasspointConfigs(scanDetail, matches, null); 348 } 349 @Override 350 public void notifyIconFailed(long bssid) { 351 Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION); 352 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 353 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, bssid); 354 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 355 } 356 357 } 358 359 WifiConfigManager(Context context, WifiNative wifiNative, FrameworkFacade facade, Clock clock, 360 UserManager userManager, KeyStore keyStore) { 361 mContext = context; 362 mFacade = facade; 363 mClock = clock; 364 mKeyStore = keyStore; 365 mUserManager = userManager; 366 367 if (mShowNetworks) { 368 mLocalLog = wifiNative.getLocalLog(); 369 } else { 370 mLocalLog = null; 371 } 372 373 mWifiAssociatedShortScanIntervalMs.set(mContext.getResources().getInteger( 374 R.integer.config_wifi_associated_short_scan_interval)); 375 mWifiDisconnectedShortScanIntervalMs.set(mContext.getResources().getInteger( 376 R.integer.config_wifi_disconnected_short_scan_interval)); 377 mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean( 378 R.bool.config_wifi_only_link_same_credential_configurations); 379 mMaxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger( 380 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels)); 381 mAssociatedFullScanMaxIntervalMs = mContext.getResources().getInteger( 382 R.integer.config_wifi_framework_associated_full_scan_max_interval); 383 mAssociatedFullScanBackoff.set(mContext.getResources().getInteger( 384 R.integer.config_wifi_framework_associated_full_scan_backoff)); 385 mEnableLinkDebouncing = mContext.getResources().getBoolean( 386 R.bool.config_wifi_enable_disconnection_debounce); 387 mBandAward5Ghz.set(mContext.getResources().getInteger( 388 R.integer.config_wifi_framework_5GHz_preference_boost_factor)); 389 mThresholdMinimumRssi5.set(mContext.getResources().getInteger( 390 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)); 391 mThresholdQualifiedRssi5.set(mContext.getResources().getInteger( 392 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)); 393 mThresholdSaturatedRssi5.set(mContext.getResources().getInteger( 394 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz)); 395 mThresholdMinimumRssi24.set(mContext.getResources().getInteger( 396 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)); 397 mThresholdQualifiedRssi24.set(mContext.getResources().getInteger( 398 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)); 399 mThresholdSaturatedRssi24.set(mContext.getResources().getInteger( 400 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)); 401 mEnableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean( 402 R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment); 403 mBadLinkSpeed24 = mContext.getResources().getInteger( 404 R.integer.config_wifi_framework_wifi_score_bad_link_speed_24); 405 mBadLinkSpeed5 = mContext.getResources().getInteger( 406 R.integer.config_wifi_framework_wifi_score_bad_link_speed_5); 407 mGoodLinkSpeed24 = mContext.getResources().getInteger( 408 R.integer.config_wifi_framework_wifi_score_good_link_speed_24); 409 mGoodLinkSpeed5 = mContext.getResources().getInteger( 410 R.integer.config_wifi_framework_wifi_score_good_link_speed_5); 411 mEnableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean( 412 R.bool.config_wifi_framework_enable_associated_network_selection)); 413 mCurrentNetworkBoost.set(mContext.getResources().getInteger( 414 R.integer.config_wifi_framework_current_network_boost)); 415 mNetworkSwitchingBlackListPeriodMs = mContext.getResources().getInteger( 416 R.integer.config_wifi_network_switching_blacklist_time); 417 418 boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled); 419 Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled")); 420 421 mConfiguredNetworks = new ConfigurationMap(userManager); 422 mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on); 423 mEnableOsuQueries = true; 424 mAnqpCache = new AnqpCache(mClock); 425 mSupplicantBridgeCallbacks = new SupplicantBridgeCallbacks(); 426 mSupplicantBridge = new SupplicantBridge(wifiNative, mSupplicantBridgeCallbacks); 427 mScanDetailCaches = new ConcurrentHashMap<>(16, 0.75f, 2); 428 mSIMAccessor = new SIMAccessor(mContext); 429 mWriter = new DelayedDiskWrite(); 430 mIpconfigStore = new IpConfigStore(mWriter); 431 mWifiNetworkHistory = new WifiNetworkHistory(context, mLocalLog, mWriter); 432 mWifiConfigStore = 433 new WifiConfigStore(wifiNative, mKeyStore, mLocalLog, mShowNetworks, true); 434 } 435 436 public void trimANQPCache(boolean all) { 437 mAnqpCache.clear(all, DBG); 438 } 439 440 void enableVerboseLogging(int verbose) { 441 mEnableVerboseLogging.set(verbose); 442 if (verbose > 0) { 443 sVDBG = true; 444 mShowNetworks = true; 445 } else { 446 sVDBG = false; 447 } 448 if (verbose > 1) { 449 sVVDBG = true; 450 } else { 451 sVVDBG = false; 452 } 453 } 454 455 /** 456 * Fetch the list of configured networks 457 * and enable all stored networks in supplicant. 458 */ 459 void loadAndEnableAllNetworks() { 460 if (DBG) log("Loading config and enabling all networks "); 461 loadConfiguredNetworks(); 462 enableAllNetworks(); 463 } 464 465 int getConfiguredNetworksSize() { 466 return mConfiguredNetworks.sizeForCurrentUser(); 467 } 468 469 /** 470 * Fetch the list of currently saved networks (i.e. all configured networks, excluding 471 * ephemeral networks). 472 * @param pskMap Map of preSharedKeys, keyed by the configKey of the configuration the 473 * preSharedKeys belong to 474 * @return List of networks 475 */ 476 private List<WifiConfiguration> getSavedNetworks(Map<String, String> pskMap) { 477 List<WifiConfiguration> networks = new ArrayList<>(); 478 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 479 WifiConfiguration newConfig = new WifiConfiguration(config); 480 // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to 481 // correctly handle updating existing configs that are filtered out here. 482 if (config.ephemeral) { 483 // Do not enumerate and return this configuration to anyone (e.g. WiFi Picker); 484 // treat it as unknown instead. This configuration can still be retrieved 485 // directly by its key or networkId. 486 continue; 487 } 488 489 if (pskMap != null && config.allowedKeyManagement != null 490 && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) 491 && pskMap.containsKey(config.configKey(true))) { 492 newConfig.preSharedKey = pskMap.get(config.configKey(true)); 493 } 494 networks.add(newConfig); 495 } 496 return networks; 497 } 498 499 /** 500 * This function returns all configuration, and is used for debug and creating bug reports. 501 */ 502 private List<WifiConfiguration> getAllConfiguredNetworks() { 503 List<WifiConfiguration> networks = new ArrayList<>(); 504 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 505 WifiConfiguration newConfig = new WifiConfiguration(config); 506 networks.add(newConfig); 507 } 508 return networks; 509 } 510 511 /** 512 * Fetch the list of currently saved networks (i.e. all configured networks, excluding 513 * ephemeral networks). 514 * @return List of networks 515 */ 516 public List<WifiConfiguration> getSavedNetworks() { 517 return getSavedNetworks(null); 518 } 519 520 /** 521 * Fetch the list of currently saved networks (i.e. all configured networks, excluding 522 * ephemeral networks), filled with real preSharedKeys. 523 * @return List of networks 524 */ 525 List<WifiConfiguration> getPrivilegedSavedNetworks() { 526 Map<String, String> pskMap = getCredentialsByConfigKeyMap(); 527 List<WifiConfiguration> configurations = getSavedNetworks(pskMap); 528 for (WifiConfiguration configuration : configurations) { 529 try { 530 configuration 531 .setPasspointManagementObjectTree(mMOManager.getMOTree(configuration.FQDN)); 532 } catch (IOException ioe) { 533 Log.w(TAG, "Failed to parse MO from " + configuration.FQDN + ": " + ioe); 534 } 535 } 536 return configurations; 537 } 538 539 /** 540 * Fetch the list of networkId's which are hidden in current user's configuration. 541 * @return List of networkIds 542 */ 543 public Set<Integer> getHiddenConfiguredNetworkIds() { 544 return mConfiguredNetworks.getHiddenNetworkIdsForCurrentUser(); 545 } 546 547 /** 548 * Find matching network for this scanResult 549 */ 550 WifiConfiguration getMatchingConfig(ScanResult scanResult) { 551 552 for (Map.Entry entry : mScanDetailCaches.entrySet()) { 553 Integer netId = (Integer) entry.getKey(); 554 ScanDetailCache cache = (ScanDetailCache) entry.getValue(); 555 WifiConfiguration config = getWifiConfiguration(netId); 556 if (config == null) { 557 continue; 558 } 559 if (cache.get(scanResult.BSSID) != null) { 560 return config; 561 } 562 } 563 564 return null; 565 } 566 567 /** 568 * Fetch the preSharedKeys for all networks. 569 * @return a map from configKey to preSharedKey. 570 */ 571 private Map<String, String> getCredentialsByConfigKeyMap() { 572 return readNetworkVariablesFromSupplicantFile("psk"); 573 } 574 575 /** 576 * Fetch the list of currently saved networks (i.e. all configured networks, excluding 577 * ephemeral networks) that were recently seen. 578 * 579 * @param scanResultAgeMs The maximum age (in ms) of scan results for which we calculate the 580 * RSSI values 581 * @param copy If true, the returned list will contain copies of the configurations for the 582 * saved networks. Otherwise, the returned list will contain references to these 583 * configurations. 584 * @return List of networks 585 */ 586 List<WifiConfiguration> getRecentSavedNetworks(int scanResultAgeMs, boolean copy) { 587 List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 588 589 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 590 if (config.ephemeral) { 591 // Do not enumerate and return this configuration to anyone (e.g. WiFi Picker); 592 // treat it as unknown instead. This configuration can still be retrieved 593 // directly by its key or networkId. 594 continue; 595 } 596 597 // Calculate the RSSI for scan results that are more recent than scanResultAgeMs. 598 ScanDetailCache cache = getScanDetailCache(config); 599 if (cache == null) { 600 continue; 601 } 602 config.setVisibility(cache.getVisibility(scanResultAgeMs)); 603 if (config.visibility == null) { 604 continue; 605 } 606 if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI 607 && config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) { 608 continue; 609 } 610 if (copy) { 611 networks.add(new WifiConfiguration(config)); 612 } else { 613 networks.add(config); 614 } 615 } 616 return networks; 617 } 618 619 /** 620 * Update the configuration and BSSID with latest RSSI value. 621 */ 622 void updateConfiguration(WifiInfo info) { 623 WifiConfiguration config = getWifiConfiguration(info.getNetworkId()); 624 if (config != null && getScanDetailCache(config) != null) { 625 ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID()); 626 if (scanDetail != null) { 627 ScanResult result = scanDetail.getScanResult(); 628 long previousSeen = result.seen; 629 int previousRssi = result.level; 630 631 // Update the scan result 632 scanDetail.setSeen(); 633 result.level = info.getRssi(); 634 635 // Average the RSSI value 636 result.averageRssi(previousRssi, previousSeen, 637 WifiQualifiedNetworkSelector.SCAN_RESULT_MAXIMUNM_AGE); 638 if (sVDBG) { 639 loge("updateConfiguration freq=" + result.frequency 640 + " BSSID=" + result.BSSID 641 + " RSSI=" + result.level 642 + " " + config.configKey()); 643 } 644 } 645 } 646 } 647 648 /** 649 * get the Wificonfiguration for this netId 650 * 651 * @return Wificonfiguration 652 */ 653 public WifiConfiguration getWifiConfiguration(int netId) { 654 return mConfiguredNetworks.getForCurrentUser(netId); 655 } 656 657 /** 658 * Get the Wificonfiguration for this key 659 * @return Wificonfiguration 660 */ 661 public WifiConfiguration getWifiConfiguration(String key) { 662 return mConfiguredNetworks.getByConfigKeyForCurrentUser(key); 663 } 664 665 /** 666 * Enable all networks (if disabled time expire) and save config. This will be a no-op if the 667 * list of configured networks indicates all networks as being enabled 668 */ 669 void enableAllNetworks() { 670 boolean networkEnabledStateChanged = false; 671 672 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 673 if (config != null && !config.ephemeral 674 && !config.getNetworkSelectionStatus().isNetworkEnabled()) { 675 if (tryEnableQualifiedNetwork(config)) { 676 networkEnabledStateChanged = true; 677 } 678 } 679 } 680 681 if (networkEnabledStateChanged) { 682 saveConfig(); 683 sendConfiguredNetworksChangedBroadcast(); 684 } 685 } 686 687 /** 688 * Enable a network in wpa_supplicant. 689 */ 690 boolean enableNetworkNative(WifiConfiguration config) { 691 return mWifiConfigStore.enableNetwork(config); 692 } 693 694 /** 695 * Enable all networks in wpa_supplicant. 696 */ 697 void enableAllNetworksNative() { 698 if (mWifiConfigStore.enableAllNetworks(mConfiguredNetworks.valuesForCurrentUser())) { 699 sendConfiguredNetworksChangedBroadcast(); 700 } 701 } 702 703 private boolean setNetworkPriorityNative(WifiConfiguration config, int priority) { 704 return mWifiConfigStore.setNetworkPriority(config, priority); 705 } 706 707 private boolean setSSIDNative(WifiConfiguration config, String ssid) { 708 return mWifiConfigStore.setNetworkSSID(config, ssid); 709 } 710 711 public boolean updateLastConnectUid(WifiConfiguration config, int uid) { 712 if (config != null) { 713 if (config.lastConnectUid != uid) { 714 config.lastConnectUid = uid; 715 return true; 716 } 717 } 718 return false; 719 } 720 721 /** 722 * Selects the specified network for connection. This involves 723 * updating the priority of all the networks and enabling the given 724 * network while disabling others. 725 * 726 * Selecting a network will leave the other networks disabled and 727 * a call to enableAllNetworks() needs to be issued upon a connection 728 * or a failure event from supplicant 729 * 730 * @param config network to select for connection 731 * @param updatePriorities makes config highest priority network 732 * @return false if the network id is invalid 733 */ 734 boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) { 735 if (sVDBG) localLogNetwork("selectNetwork", config.networkId); 736 if (config.networkId == INVALID_NETWORK_ID) return false; 737 if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, 738 mUserManager.getProfiles(mCurrentUserId))) { 739 loge("selectNetwork " + Integer.toString(config.networkId) + ": Network config is not " 740 + "visible to current user."); 741 return false; 742 } 743 744 // Reset the priority of each network at start or if it goes too high. 745 if (mLastPriority == -1 || mLastPriority > 1000000) { 746 if (updatePriorities) { 747 for (WifiConfiguration config2 : mConfiguredNetworks.valuesForCurrentUser()) { 748 if (config2.networkId != INVALID_NETWORK_ID) { 749 setNetworkPriorityNative(config2, 0); 750 } 751 } 752 } 753 mLastPriority = 0; 754 } 755 756 // Set to the highest priority and save the configuration. 757 if (updatePriorities) { 758 setNetworkPriorityNative(config, ++mLastPriority); 759 } 760 761 if (config.isPasspoint()) { 762 /* need to slap on the SSID of selected bssid to work */ 763 if (getScanDetailCache(config).size() != 0) { 764 ScanDetail result = getScanDetailCache(config).getFirst(); 765 if (result == null) { 766 loge("Could not find scan result for " + config.BSSID); 767 } else { 768 log("Setting SSID for " + config.networkId + " to" + result.getSSID()); 769 setSSIDNative(config, result.getSSID()); 770 } 771 772 } else { 773 loge("Could not find bssid for " + config); 774 } 775 } 776 777 mWifiConfigStore.enableHS20(config.isPasspoint()); 778 779 if (updatePriorities) { 780 saveConfig(); 781 } 782 783 updateLastConnectUid(config, uid); 784 785 writeKnownNetworkHistory(); 786 787 /* Enable the given network while disabling all other networks */ 788 selectNetworkWithoutBroadcast(config.networkId); 789 790 /* Avoid saving the config & sending a broadcast to prevent settings 791 * from displaying a disabled list of networks */ 792 return true; 793 } 794 795 /** 796 * Add/update the specified configuration and save config 797 * 798 * @param config WifiConfiguration to be saved 799 * @return network update result 800 */ 801 NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) { 802 WifiConfiguration conf; 803 804 // A new network cannot have null SSID 805 if (config == null || (config.networkId == INVALID_NETWORK_ID && config.SSID == null)) { 806 return new NetworkUpdateResult(INVALID_NETWORK_ID); 807 } 808 809 if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, 810 mUserManager.getProfiles(mCurrentUserId))) { 811 return new NetworkUpdateResult(INVALID_NETWORK_ID); 812 } 813 814 if (sVDBG) localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId); 815 if (sVDBG) { 816 logd("WifiConfigManager saveNetwork," 817 + " size=" + Integer.toString(mConfiguredNetworks.sizeForAllUsers()) 818 + " (for all users)" 819 + " SSID=" + config.SSID 820 + " Uid=" + Integer.toString(config.creatorUid) 821 + "/" + Integer.toString(config.lastUpdateUid)); 822 } 823 824 if (mDeletedEphemeralSSIDs.remove(config.SSID)) { 825 if (sVDBG) { 826 loge("WifiConfigManager: removed from ephemeral blacklist: " + config.SSID); 827 } 828 // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call 829 // below, since we're creating/modifying a config. 830 } 831 832 boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); 833 NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid); 834 int netId = result.getNetworkId(); 835 836 if (sVDBG) localLogNetwork("WifiConfigManager: saveNetwork got it back netId=", netId); 837 838 conf = mConfiguredNetworks.getForCurrentUser(netId); 839 if (conf != null) { 840 if (!conf.getNetworkSelectionStatus().isNetworkEnabled()) { 841 if (sVDBG) localLog("WifiConfigManager: re-enabling: " + conf.SSID); 842 843 // reenable autojoin, since new information has been provided 844 updateNetworkSelectionStatus(netId, 845 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 846 } 847 if (sVDBG) { 848 loge("WifiConfigManager: saveNetwork got config back netId=" 849 + Integer.toString(netId) 850 + " uid=" + Integer.toString(config.creatorUid)); 851 } 852 } 853 854 saveConfig(); 855 sendConfiguredNetworksChangedBroadcast( 856 conf, 857 result.isNewNetwork() 858 ? WifiManager.CHANGE_REASON_ADDED 859 : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 860 return result; 861 } 862 863 void noteRoamingFailure(WifiConfiguration config, int reason) { 864 if (config == null) return; 865 config.lastRoamingFailure = System.currentTimeMillis(); 866 config.roamingFailureBlackListTimeMilli = 867 2 * (config.roamingFailureBlackListTimeMilli + 1000); 868 if (config.roamingFailureBlackListTimeMilli > mNetworkSwitchingBlackListPeriodMs) { 869 config.roamingFailureBlackListTimeMilli = mNetworkSwitchingBlackListPeriodMs; 870 } 871 config.lastRoamingFailureReason = reason; 872 } 873 874 void saveWifiConfigBSSID(WifiConfiguration config, String bssid) { 875 mWifiConfigStore.setNetworkBSSID(config, bssid); 876 } 877 878 879 void updateStatus(int netId, DetailedState state) { 880 if (netId != INVALID_NETWORK_ID) { 881 WifiConfiguration config = mConfiguredNetworks.getForAllUsers(netId); 882 if (config == null) return; 883 switch (state) { 884 case CONNECTED: 885 config.status = Status.CURRENT; 886 //we successfully connected, hence remove the blacklist 887 updateNetworkSelectionStatus(netId, 888 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 889 break; 890 case DISCONNECTED: 891 //If network is already disabled, keep the status 892 if (config.status == Status.CURRENT) { 893 config.status = Status.ENABLED; 894 } 895 break; 896 default: 897 //do nothing, retain the existing state 898 break; 899 } 900 } 901 } 902 903 904 /** 905 * Disable an ephemeral SSID for the purpose of auto-joining thru scored. 906 * This SSID will never be scored anymore. 907 * The only way to "un-disable it" is if the user create a network for that SSID and then 908 * forget it. 909 * 910 * @param ssid caller must ensure that the SSID passed thru this API match 911 * the WifiConfiguration.SSID rules, and thus be surrounded by quotes. 912 * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can 913 * disconnect if this is the current network. 914 */ 915 WifiConfiguration disableEphemeralNetwork(String ssid) { 916 if (ssid == null) { 917 return null; 918 } 919 920 WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeralForCurrentUser(ssid); 921 922 mDeletedEphemeralSSIDs.add(ssid); 923 loge("Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size()); 924 925 if (foundConfig != null) { 926 loge("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId); 927 } 928 929 writeKnownNetworkHistory(); 930 return foundConfig; 931 } 932 933 /** 934 * Forget the specified network and save config 935 * 936 * @param netId network to forget 937 * @return {@code true} if it succeeds, {@code false} otherwise 938 */ 939 boolean forgetNetwork(int netId) { 940 if (mShowNetworks) localLogNetwork("forgetNetwork", netId); 941 if (!removeNetwork(netId)) { 942 loge("Failed to forget network " + netId); 943 return false; 944 } 945 saveConfig(); 946 writeKnownNetworkHistory(); 947 return true; 948 } 949 950 /** 951 * Add/update a network. Note that there is no saveConfig operation. 952 * This function is retained for compatibility with the public 953 * API. The more powerful saveNetwork() is used by the 954 * state machine 955 * 956 * @param config wifi configuration to add/update 957 * @return network Id 958 */ 959 int addOrUpdateNetwork(WifiConfiguration config, int uid) { 960 if (config == null || !WifiConfigurationUtil.isVisibleToAnyProfile(config, 961 mUserManager.getProfiles(mCurrentUserId))) { 962 return WifiConfiguration.INVALID_NETWORK_ID; 963 } 964 965 if (mShowNetworks) localLogNetwork("addOrUpdateNetwork id=", config.networkId); 966 if (config.isPasspoint()) { 967 /* create a temporary SSID with providerFriendlyName */ 968 Long csum = getChecksum(config.FQDN); 969 config.SSID = csum.toString(); 970 config.enterpriseConfig.setDomainSuffixMatch(config.FQDN); 971 } 972 973 NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid); 974 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 975 WifiConfiguration conf = mConfiguredNetworks.getForCurrentUser(result.getNetworkId()); 976 if (conf != null) { 977 sendConfiguredNetworksChangedBroadcast( 978 conf, 979 result.isNewNetwork 980 ? WifiManager.CHANGE_REASON_ADDED 981 : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 982 } 983 } 984 985 return result.getNetworkId(); 986 } 987 988 public int addPasspointManagementObject(String managementObject) { 989 try { 990 mMOManager.addSP(managementObject); 991 return 0; 992 } catch (IOException | SAXException e) { 993 return -1; 994 } 995 } 996 997 public int modifyPasspointMo(String fqdn, List<PasspointManagementObjectDefinition> mos) { 998 try { 999 return mMOManager.modifySP(fqdn, mos); 1000 } catch (IOException | SAXException e) { 1001 return -1; 1002 } 1003 } 1004 1005 public boolean queryPasspointIcon(long bssid, String fileName) { 1006 return mSupplicantBridge.doIconQuery(bssid, fileName); 1007 } 1008 1009 public int matchProviderWithCurrentNetwork(String fqdn) { 1010 ScanDetail scanDetail = null; 1011 synchronized (mActiveScanDetailLock) { 1012 scanDetail = mActiveScanDetail; 1013 } 1014 if (scanDetail == null) { 1015 return PasspointMatch.None.ordinal(); 1016 } 1017 HomeSP homeSP = mMOManager.getHomeSP(fqdn); 1018 if (homeSP == null) { 1019 return PasspointMatch.None.ordinal(); 1020 } 1021 1022 ANQPData anqpData = mAnqpCache.getEntry(scanDetail.getNetworkDetail()); 1023 1024 Map<Constants.ANQPElementType, ANQPElement> anqpElements = 1025 anqpData != null ? anqpData.getANQPElements() : null; 1026 1027 return homeSP.match(scanDetail.getNetworkDetail(), anqpElements, mSIMAccessor).ordinal(); 1028 } 1029 1030 /** 1031 * General PnoNetwork list sorting algorithm: 1032 * 1, Place the fully enabled networks first. Among the fully enabled networks, 1033 * sort them in the oder determined by the return of |compareConfigurations| method 1034 * implementation. 1035 * 2. Next place all the temporarily disabled networks. Among the temporarily disabled 1036 * networks, sort them in the order determined by the return of |compareConfigurations| method 1037 * implementation. 1038 * 3. Place the permanently disabled networks last. The order among permanently disabled 1039 * networks doesn't matter. 1040 */ 1041 private static class PnoListComparator implements Comparator<WifiConfiguration> { 1042 1043 public final int ENABLED_NETWORK_SCORE = 3; 1044 public final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; 1045 public final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; 1046 1047 @Override 1048 public int compare(WifiConfiguration a, WifiConfiguration b) { 1049 int configAScore = getPnoNetworkSortScore(a); 1050 int configBScore = getPnoNetworkSortScore(b); 1051 if (configAScore == configBScore) { 1052 return compareConfigurations(a, b); 1053 } else { 1054 return Integer.compare(configBScore, configAScore); 1055 } 1056 } 1057 1058 // This needs to be implemented by the connected/disconnected PNO list comparator. 1059 public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) { 1060 return 0; 1061 } 1062 1063 /** 1064 * Returns an integer representing a score for each configuration. The scores are assigned 1065 * based on the status of the configuration. The scores are assigned according to the order: 1066 * Fully enabled network > Temporarily disabled network > Permanently disabled network. 1067 */ 1068 private int getPnoNetworkSortScore(WifiConfiguration config) { 1069 if (config.getNetworkSelectionStatus().isNetworkEnabled()) { 1070 return ENABLED_NETWORK_SCORE; 1071 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { 1072 return TEMPORARY_DISABLED_NETWORK_SCORE; 1073 } else { 1074 return PERMANENTLY_DISABLED_NETWORK_SCORE; 1075 } 1076 } 1077 } 1078 1079 /** 1080 * Disconnected PnoNetwork list sorting algorithm: 1081 * Place the configurations in descending order of their |numAssociation| values. If networks 1082 * have the same |numAssociation|, then sort them in descending order of their |priority| 1083 * values. 1084 */ 1085 private static final PnoListComparator sDisconnectedPnoListComparator = 1086 new PnoListComparator() { 1087 @Override 1088 public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) { 1089 if (a.numAssociation != b.numAssociation) { 1090 return Long.compare(b.numAssociation, a.numAssociation); 1091 } else { 1092 return Integer.compare(b.priority, a.priority); 1093 } 1094 } 1095 }; 1096 1097 /** 1098 * Retrieves an updated list of priorities for all the saved networks before 1099 * enabling disconnected PNO (wpa_supplicant based PNO). 1100 * 1101 * @return list of networks with updated priorities. 1102 */ 1103 public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveDisconnectedPnoNetworkList() { 1104 return retrievePnoNetworkList(true, sDisconnectedPnoListComparator); 1105 } 1106 1107 /** 1108 * Retrieves an updated list of priorities for all the saved networks before 1109 * enabling/disabling disconnected PNO (wpa_supplicant based PNO). 1110 * 1111 * wpa_supplicant uses the priority of networks to build the list of SSID's to monitor 1112 * during PNO. If there are a lot of saved networks, this list will be truncated and we 1113 * might end up not connecting to the networks we use most frequently. So, We want the networks 1114 * to be re-sorted based on the relative |numAssociation| values. 1115 * 1116 * @param enablePno boolean indicating whether PNO is being enabled or disabled. 1117 * @return list of networks with updated priorities. 1118 */ 1119 public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveDisconnectedPnoNetworkList( 1120 boolean enablePno) { 1121 return retrievePnoNetworkList(enablePno, sDisconnectedPnoListComparator); 1122 } 1123 1124 /** 1125 * Connected PnoNetwork list sorting algorithm: 1126 * Place the configurations with |lastSeenInQualifiedNetworkSelection| set first. If networks 1127 * have the same value, then sort them in descending order of their |numAssociation| 1128 * values. 1129 */ 1130 private static final PnoListComparator sConnectedPnoListComparator = 1131 new PnoListComparator() { 1132 @Override 1133 public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) { 1134 boolean isConfigALastSeen = 1135 a.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection(); 1136 boolean isConfigBLastSeen = 1137 b.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection(); 1138 if (isConfigALastSeen != isConfigBLastSeen) { 1139 return Boolean.compare(isConfigBLastSeen, isConfigALastSeen); 1140 } else { 1141 return Long.compare(b.numAssociation, a.numAssociation); 1142 } 1143 } 1144 }; 1145 1146 /** 1147 * Retrieves an updated list of priorities for all the saved networks before 1148 * enabling connected PNO (HAL based ePno). 1149 * 1150 * @return list of networks with updated priorities. 1151 */ 1152 public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveConnectedPnoNetworkList() { 1153 return retrievePnoNetworkList(true, sConnectedPnoListComparator); 1154 } 1155 1156 /** 1157 * Create a PnoNetwork object from the provided WifiConfiguration. 1158 * @param config Configuration corresponding to the network. 1159 * @param newPriority New priority to be assigned to the network. 1160 */ 1161 private static WifiScanner.PnoSettings.PnoNetwork createPnoNetworkFromWifiConfiguration( 1162 WifiConfiguration config, int newPriority) { 1163 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 1164 new WifiScanner.PnoSettings.PnoNetwork(config.SSID); 1165 pnoNetwork.networkId = config.networkId; 1166 pnoNetwork.priority = newPriority; 1167 if (config.hiddenSSID) { 1168 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN; 1169 } 1170 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND; 1171 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND; 1172 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 1173 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK; 1174 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) 1175 || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) { 1176 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL; 1177 } else { 1178 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN; 1179 } 1180 return pnoNetwork; 1181 } 1182 1183 /** 1184 * Retrieves an updated list of priorities for all the saved networks before 1185 * enabling/disabling PNO. 1186 * 1187 * @param enablePno boolean indicating whether PNO is being enabled or disabled. 1188 * @return list of networks with updated priorities. 1189 */ 1190 private ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList( 1191 boolean enablePno, PnoListComparator pnoListComparator) { 1192 ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); 1193 ArrayList<WifiConfiguration> wifiConfigurations = 1194 new ArrayList<>(mConfiguredNetworks.valuesForCurrentUser()); 1195 if (enablePno) { 1196 Collections.sort(wifiConfigurations, pnoListComparator); 1197 // Let's use the network list size as the highest priority and then go down from there. 1198 // So, the most frequently connected network has the highest priority now. 1199 int priority = wifiConfigurations.size(); 1200 if (DBG) { 1201 Log.d(TAG, "Retrieve network priorities before PNO. Max priority: " + priority); 1202 } 1203 for (WifiConfiguration config : wifiConfigurations) { 1204 pnoList.add(createPnoNetworkFromWifiConfiguration(config, priority)); 1205 priority--; 1206 } 1207 } else { 1208 // Revert the priorities back to the saved config values after PNO. 1209 if (DBG) Log.d(TAG, "Retrieve network priorities after PNO."); 1210 for (WifiConfiguration config : wifiConfigurations) { 1211 pnoList.add(createPnoNetworkFromWifiConfiguration(config, config.priority)); 1212 } 1213 } 1214 return pnoList; 1215 } 1216 1217 /** 1218 * Remove a network. Note that there is no saveConfig operation. 1219 * This function is retained for compatibility with the public 1220 * API. The more powerful forgetNetwork() is used by the 1221 * state machine for network removal 1222 * 1223 * @param netId network to be removed 1224 * @return {@code true} if it succeeds, {@code false} otherwise 1225 */ 1226 boolean removeNetwork(int netId) { 1227 if (mShowNetworks) localLogNetwork("removeNetwork", netId); 1228 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1229 if (!removeConfigAndSendBroadcastIfNeeded(config)) { 1230 return false; 1231 } 1232 if (config.isPasspoint()) { 1233 writePasspointConfigs(config.FQDN, null); 1234 } 1235 return true; 1236 } 1237 1238 private static Long getChecksum(String source) { 1239 Checksum csum = new CRC32(); 1240 csum.update(source.getBytes(), 0, source.getBytes().length); 1241 return csum.getValue(); 1242 } 1243 1244 private boolean removeConfigWithoutBroadcast(WifiConfiguration config) { 1245 if (config == null) { 1246 return false; 1247 } 1248 if (!mWifiConfigStore.removeNetwork(config)) { 1249 loge("Failed to remove network " + config.networkId); 1250 return false; 1251 } 1252 if (config.configKey().equals(mLastSelectedConfiguration)) { 1253 mLastSelectedConfiguration = null; 1254 } 1255 mConfiguredNetworks.remove(config.networkId); 1256 mScanDetailCaches.remove(config.networkId); 1257 return true; 1258 } 1259 1260 private boolean removeConfigAndSendBroadcastIfNeeded(WifiConfiguration config) { 1261 if (!removeConfigWithoutBroadcast(config)) { 1262 return false; 1263 } 1264 String key = config.configKey(); 1265 if (sVDBG) { 1266 logd("removeNetwork " + " key=" + key + " config.id=" + config.networkId); 1267 } 1268 if (config.selfAdded || config.linkedConfigurations != null 1269 || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1270 if (!TextUtils.isEmpty(config.SSID)) { 1271 /* Remember that we deleted this PSK SSID */ 1272 if (config.SSID != null) { 1273 Long csum = getChecksum(config.SSID); 1274 mDeletedSSIDs.add(csum); 1275 logd("removeNetwork " 1276 + " key=" + key 1277 + " config.id=" + config.networkId 1278 + " crc=" + csum); 1279 } else { 1280 logd("removeNetwork " 1281 + " key=" + key 1282 + " config.id=" + config.networkId); 1283 } 1284 } 1285 } 1286 writeIpAndProxyConfigurations(); 1287 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 1288 if (!config.ephemeral) { 1289 removeUserSelectionPreference(key); 1290 } 1291 writeKnownNetworkHistory(); 1292 return true; 1293 } 1294 1295 private void removeUserSelectionPreference(String configKey) { 1296 if (DBG) { 1297 Log.d(TAG, "removeUserSelectionPreference: key is " + configKey); 1298 } 1299 if (configKey == null) { 1300 return; 1301 } 1302 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 1303 WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1304 String connectChoice = status.getConnectChoice(); 1305 if (connectChoice != null && connectChoice.equals(configKey)) { 1306 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID 1307 + " : " + config.networkId); 1308 status.setConnectChoice(null); 1309 status.setConnectChoiceTimestamp(WifiConfiguration.NetworkSelectionStatus 1310 .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1311 } 1312 } 1313 } 1314 1315 /* 1316 * Remove all networks associated with an application 1317 * 1318 * @param packageName name of the package of networks to remove 1319 * @return {@code true} if all networks removed successfully, {@code false} otherwise 1320 */ 1321 boolean removeNetworksForApp(ApplicationInfo app) { 1322 if (app == null || app.packageName == null) { 1323 return false; 1324 } 1325 1326 boolean success = true; 1327 1328 WifiConfiguration [] copiedConfigs = 1329 mConfiguredNetworks.valuesForCurrentUser().toArray(new WifiConfiguration[0]); 1330 for (WifiConfiguration config : copiedConfigs) { 1331 if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) { 1332 continue; 1333 } 1334 if (mShowNetworks) { 1335 localLog("Removing network " + config.SSID 1336 + ", application \"" + app.packageName + "\" uninstalled" 1337 + " from user " + UserHandle.getUserId(app.uid)); 1338 } 1339 success &= removeNetwork(config.networkId); 1340 } 1341 1342 saveConfig(); 1343 1344 return success; 1345 } 1346 1347 boolean removeNetworksForUser(int userId) { 1348 boolean success = true; 1349 1350 WifiConfiguration[] copiedConfigs = 1351 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1352 for (WifiConfiguration config : copiedConfigs) { 1353 if (userId != UserHandle.getUserId(config.creatorUid)) { 1354 continue; 1355 } 1356 success &= removeNetwork(config.networkId); 1357 if (mShowNetworks) { 1358 localLog("Removing network " + config.SSID 1359 + ", user " + userId + " removed"); 1360 } 1361 } 1362 1363 return success; 1364 } 1365 1366 /** 1367 * Enable a network. Note that there is no saveConfig operation. 1368 * This function is retained for compatibility with the public 1369 * API. The more powerful selectNetwork()/saveNetwork() is used by the 1370 * state machine for connecting to a network 1371 * 1372 * @param netId network to be enabled 1373 * @return {@code true} if it succeeds, {@code false} otherwise 1374 */ 1375 boolean enableNetwork(int netId, boolean disableOthers, int uid) { 1376 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1377 if (config == null) { 1378 return false; 1379 } 1380 boolean ret = true; 1381 if (disableOthers) { 1382 ret = selectNetworkWithoutBroadcast(netId); 1383 if (sVDBG) { 1384 localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId); 1385 } 1386 updateLastConnectUid(getWifiConfiguration(netId), uid); 1387 1388 writeKnownNetworkHistory(); 1389 sendConfiguredNetworksChangedBroadcast(); 1390 } else { 1391 if (sVDBG) localLogNetwork("enableNetwork(disableOthers=false) ", netId); 1392 WifiConfiguration enabledNetwork; 1393 synchronized (mConfiguredNetworks) { // !!! Useless synchronization! 1394 enabledNetwork = mConfiguredNetworks.getForCurrentUser(netId); 1395 } 1396 // check just in case the network was removed by someone else. 1397 if (enabledNetwork != null) { 1398 sendConfiguredNetworksChangedBroadcast(enabledNetwork, 1399 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1400 } 1401 } 1402 return ret; 1403 } 1404 1405 boolean selectNetworkWithoutBroadcast(int netId) { 1406 return mWifiConfigStore.selectNetwork( 1407 mConfiguredNetworks.getForCurrentUser(netId), 1408 mConfiguredNetworks.valuesForCurrentUser()); 1409 } 1410 1411 /** 1412 * Disable a network in wpa_supplicant. 1413 */ 1414 boolean disableNetworkNative(WifiConfiguration config) { 1415 return mWifiConfigStore.disableNetwork(config); 1416 } 1417 1418 /** 1419 * Disable all networks in wpa_supplicant. 1420 */ 1421 void disableAllNetworksNative() { 1422 mWifiConfigStore.disableAllNetworks(mConfiguredNetworks.valuesForCurrentUser()); 1423 } 1424 1425 /** 1426 * Disable a network. Note that there is no saveConfig operation. 1427 * @param netId network to be disabled 1428 * @return {@code true} if it succeeds, {@code false} otherwise 1429 */ 1430 boolean disableNetwork(int netId) { 1431 return mWifiConfigStore.disableNetwork(mConfiguredNetworks.getForCurrentUser(netId)); 1432 } 1433 1434 /** 1435 * Update a network according to the update reason and its current state 1436 * @param netId The network ID of the network need update 1437 * @param reason The reason to update the network 1438 * @return false if no change made to the input configure file, can due to error or need not 1439 * true the input config file has been changed 1440 */ 1441 boolean updateNetworkSelectionStatus(int netId, int reason) { 1442 WifiConfiguration config = getWifiConfiguration(netId); 1443 return updateNetworkSelectionStatus(config, reason); 1444 } 1445 1446 /** 1447 * Update a network according to the update reason and its current state 1448 * @param config the network need update 1449 * @param reason The reason to update the network 1450 * @return false if no change made to the input configure file, can due to error or need not 1451 * true the input config file has been changed 1452 */ 1453 boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) { 1454 if (config == null) { 1455 return false; 1456 } 1457 1458 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1459 if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { 1460 updateNetworkStatus(config, WifiConfiguration.NetworkSelectionStatus 1461 .NETWORK_SELECTION_ENABLE); 1462 localLog("Enable network:" + config.configKey()); 1463 return true; 1464 } 1465 1466 networkStatus.incrementDisableReasonCounter(reason); 1467 if (DBG) { 1468 localLog("Network:" + config.SSID + "disable counter of " 1469 + WifiConfiguration.NetworkSelectionStatus.getNetworkDisableReasonString(reason) 1470 + " is: " + networkStatus.getDisableReasonCounter(reason) + "and threshold is: " 1471 + NETWORK_SELECTION_DISABLE_THRESHOLD[reason]); 1472 } 1473 1474 if (networkStatus.getDisableReasonCounter(reason) 1475 >= NETWORK_SELECTION_DISABLE_THRESHOLD[reason]) { 1476 return updateNetworkStatus(config, reason); 1477 } 1478 return true; 1479 } 1480 1481 /** 1482 * Check the config. If it is temporarily disabled, check the disable time is expired or not, If 1483 * expired, enabled it again for qualified network selection. 1484 * @param networkId the id of the network to be checked for possible unblock (due to timeout) 1485 * @return true if network status has been changed 1486 * false network status is not changed 1487 */ 1488 boolean tryEnableQualifiedNetwork(int networkId) { 1489 WifiConfiguration config = getWifiConfiguration(networkId); 1490 if (config == null) { 1491 localLog("updateQualifiedNetworkstatus invalid network."); 1492 return false; 1493 } 1494 return tryEnableQualifiedNetwork(config); 1495 } 1496 1497 /** 1498 * Check the config. If it is temporarily disabled, check the disable is expired or not, If 1499 * expired, enabled it again for qualified network selection. 1500 * @param config network to be checked for possible unblock (due to timeout) 1501 * @return true if network status has been changed 1502 * false network status is not changed 1503 */ 1504 boolean tryEnableQualifiedNetwork(WifiConfiguration config) { 1505 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1506 if (networkStatus.isNetworkTemporaryDisabled()) { 1507 //time difference in minutes 1508 long timeDifference = (System.currentTimeMillis() 1509 - networkStatus.getDisableTime()) / 1000 / 60; 1510 if (timeDifference < 0 || timeDifference 1511 >= NETWORK_SELECTION_DISABLE_TIMEOUT[ 1512 networkStatus.getNetworkSelectionDisableReason()]) { 1513 updateNetworkSelectionStatus(config.networkId, 1514 networkStatus.NETWORK_SELECTION_ENABLE); 1515 return true; 1516 } 1517 } 1518 return false; 1519 } 1520 1521 /** 1522 * Update a network's status. Note that there is no saveConfig operation. 1523 * @param config network to be updated 1524 * @param reason reason code for updated 1525 * @return false if no change made to the input configure file, can due to error or need not 1526 * true the input config file has been changed 1527 */ 1528 boolean updateNetworkStatus(WifiConfiguration config, int reason) { 1529 localLog("updateNetworkStatus:" + (config == null ? null : config.SSID)); 1530 if (config == null) { 1531 return false; 1532 } 1533 1534 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1535 if (reason < 0 || reason >= WifiConfiguration.NetworkSelectionStatus 1536 .NETWORK_SELECTION_DISABLED_MAX) { 1537 localLog("Invalid Network disable reason:" + reason); 1538 return false; 1539 } 1540 1541 if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { 1542 if (networkStatus.isNetworkEnabled()) { 1543 if (DBG) { 1544 localLog("Need not change Qualified network Selection status since" 1545 + " already enabled"); 1546 } 1547 return false; 1548 } 1549 networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus 1550 .NETWORK_SELECTION_ENABLED); 1551 networkStatus.setNetworkSelectionDisableReason(reason); 1552 networkStatus.setDisableTime( 1553 WifiConfiguration.NetworkSelectionStatus 1554 .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1555 networkStatus.clearDisableReasonCounter(); 1556 String disableTime = DateFormat.getDateTimeInstance().format(new Date()); 1557 if (DBG) { 1558 localLog("Re-enable network: " + config.SSID + " at " + disableTime); 1559 } 1560 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1561 } else { 1562 //disable the network 1563 if (networkStatus.isNetworkPermanentlyDisabled()) { 1564 //alreay permanent disable 1565 if (DBG) { 1566 localLog("Do nothing. Alreay permanent disabled! " 1567 + WifiConfiguration.NetworkSelectionStatus 1568 .getNetworkDisableReasonString(reason)); 1569 } 1570 return false; 1571 } else if (networkStatus.isNetworkTemporaryDisabled() 1572 && reason < WifiConfiguration.NetworkSelectionStatus 1573 .DISABLED_TLS_VERSION_MISMATCH) { 1574 //alreay temporarily disable 1575 if (DBG) { 1576 localLog("Do nothing. Already temporarily disabled! " 1577 + WifiConfiguration.NetworkSelectionStatus 1578 .getNetworkDisableReasonString(reason)); 1579 } 1580 return false; 1581 } 1582 1583 if (networkStatus.isNetworkEnabled()) { 1584 disableNetworkNative(config); 1585 sendConfiguredNetworksChangedBroadcast(config, 1586 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1587 localLog("Disable network " + config.SSID + " reason:" 1588 + WifiConfiguration.NetworkSelectionStatus 1589 .getNetworkDisableReasonString(reason)); 1590 } 1591 if (reason < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) { 1592 networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus 1593 .NETWORK_SELECTION_TEMPORARY_DISABLED); 1594 networkStatus.setDisableTime(System.currentTimeMillis()); 1595 } else { 1596 networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus 1597 .NETWORK_SELECTION_PERMANENTLY_DISABLED); 1598 } 1599 networkStatus.setNetworkSelectionDisableReason(reason); 1600 if (DBG) { 1601 String disableTime = DateFormat.getDateTimeInstance().format(new Date()); 1602 localLog("Network:" + config.SSID + "Configure new status:" 1603 + networkStatus.getNetworkStatusString() + " with reason:" 1604 + networkStatus.getNetworkDisableReasonString() + " at: " + disableTime); 1605 } 1606 } 1607 return true; 1608 } 1609 1610 /** 1611 * Save the configured networks in supplicant to disk 1612 * @return {@code true} if it succeeds, {@code false} otherwise 1613 */ 1614 boolean saveConfig() { 1615 return mWifiConfigStore.saveConfig(); 1616 } 1617 1618 /** 1619 * Start WPS pin method configuration with pin obtained 1620 * from the access point 1621 * @param config WPS configuration 1622 * @return Wps result containing status and pin 1623 */ 1624 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) { 1625 return mWifiConfigStore.startWpsWithPinFromAccessPoint( 1626 config, mConfiguredNetworks.valuesForCurrentUser()); 1627 } 1628 1629 /** 1630 * Start WPS pin method configuration with obtained 1631 * from the device 1632 * @return WpsResult indicating status and pin 1633 */ 1634 WpsResult startWpsWithPinFromDevice(WpsInfo config) { 1635 return mWifiConfigStore.startWpsWithPinFromDevice( 1636 config, mConfiguredNetworks.valuesForCurrentUser()); 1637 } 1638 1639 /** 1640 * Start WPS push button configuration 1641 * @param config WPS configuration 1642 * @return WpsResult indicating status and pin 1643 */ 1644 WpsResult startWpsPbc(WpsInfo config) { 1645 return mWifiConfigStore.startWpsPbc( 1646 config, mConfiguredNetworks.valuesForCurrentUser()); 1647 } 1648 1649 /** 1650 * Fetch the static IP configuration for a given network id 1651 */ 1652 StaticIpConfiguration getStaticIpConfiguration(int netId) { 1653 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1654 if (config != null) { 1655 return config.getStaticIpConfiguration(); 1656 } 1657 return null; 1658 } 1659 1660 /** 1661 * Set the static IP configuration for a given network id 1662 */ 1663 void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) { 1664 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1665 if (config != null) { 1666 config.setStaticIpConfiguration(staticIpConfiguration); 1667 } 1668 } 1669 1670 /** 1671 * set default GW MAC address 1672 */ 1673 void setDefaultGwMacAddress(int netId, String macAddress) { 1674 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1675 if (config != null) { 1676 //update defaultGwMacAddress 1677 config.defaultGwMacAddress = macAddress; 1678 } 1679 } 1680 1681 1682 /** 1683 * Fetch the proxy properties for a given network id 1684 * @param netId id 1685 * @return ProxyInfo for the network id 1686 */ 1687 ProxyInfo getProxyProperties(int netId) { 1688 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1689 if (config != null) { 1690 return config.getHttpProxy(); 1691 } 1692 return null; 1693 } 1694 1695 /** 1696 * Return if the specified network is using static IP 1697 * @param netId id 1698 * @return {@code true} if using static ip for netId 1699 */ 1700 boolean isUsingStaticIp(int netId) { 1701 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1702 if (config != null && config.getIpAssignment() == IpAssignment.STATIC) { 1703 return true; 1704 } 1705 return false; 1706 } 1707 1708 boolean isEphemeral(int netId) { 1709 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1710 return config != null && config.ephemeral; 1711 } 1712 1713 boolean getMeteredHint(int netId) { 1714 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1715 return config != null && config.meteredHint; 1716 } 1717 1718 /** 1719 * Should be called when a single network configuration is made. 1720 * @param network The network configuration that changed. 1721 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 1722 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 1723 */ 1724 private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, 1725 int reason) { 1726 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 1727 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1728 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 1729 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); 1730 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 1731 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1732 } 1733 1734 /** 1735 * Should be called when multiple network configuration changes are made. 1736 */ 1737 private void sendConfiguredNetworksChangedBroadcast() { 1738 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 1739 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1740 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 1741 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1742 } 1743 1744 void loadConfiguredNetworks() { 1745 1746 final Map<String, WifiConfiguration> configs = new HashMap<>(); 1747 final SparseArray<Map<String, String>> networkExtras = new SparseArray<>(); 1748 mLastPriority = mWifiConfigStore.loadNetworks(configs, networkExtras); 1749 1750 readNetworkHistory(configs); 1751 readPasspointConfig(configs, networkExtras); 1752 1753 // We are only now updating mConfiguredNetworks for two reasons: 1754 // 1) The information required to compute configKeys is spread across wpa_supplicant.conf 1755 // and networkHistory.txt. Thus, we had to load both files first. 1756 // 2) mConfiguredNetworks caches a Passpoint network's FQDN the moment the network is added. 1757 // Thus, we had to load the FQDNs first. 1758 mConfiguredNetworks.clear(); 1759 for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) { 1760 final String configKey = entry.getKey(); 1761 final WifiConfiguration config = entry.getValue(); 1762 if (!configKey.equals(config.configKey())) { 1763 if (mShowNetworks) { 1764 log("Ignoring network " + config.networkId + " because the configKey loaded " 1765 + "from wpa_supplicant.conf is not valid."); 1766 } 1767 mWifiConfigStore.removeNetwork(config); 1768 continue; 1769 } 1770 mConfiguredNetworks.put(config); 1771 } 1772 1773 readIpAndProxyConfigurations(); 1774 1775 sendConfiguredNetworksChangedBroadcast(); 1776 1777 if (mShowNetworks) { 1778 localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.sizeForAllUsers() 1779 + " networks (for all users)"); 1780 } 1781 1782 if (mConfiguredNetworks.sizeForAllUsers() == 0) { 1783 // no networks? Lets log if the file contents 1784 logKernelTime(); 1785 logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE); 1786 logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE_BACKUP); 1787 logContents(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE); 1788 } 1789 } 1790 1791 private void logContents(String file) { 1792 localLogAndLogcat("--- Begin " + file + " ---"); 1793 BufferedReader reader = null; 1794 try { 1795 reader = new BufferedReader(new FileReader(file)); 1796 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1797 localLogAndLogcat(line); 1798 } 1799 } catch (FileNotFoundException e) { 1800 localLog("Could not open " + file + ", " + e); 1801 Log.w(TAG, "Could not open " + file + ", " + e); 1802 } catch (IOException e) { 1803 localLog("Could not read " + file + ", " + e); 1804 Log.w(TAG, "Could not read " + file + ", " + e); 1805 } finally { 1806 try { 1807 if (reader != null) { 1808 reader.close(); 1809 } 1810 } catch (IOException e) { 1811 // Just ignore the fact that we couldn't close 1812 } 1813 } 1814 localLogAndLogcat("--- End " + file + " Contents ---"); 1815 } 1816 1817 private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) { 1818 return mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key); 1819 } 1820 1821 private String readNetworkVariableFromSupplicantFile(String configKey, String key) { 1822 long start = SystemClock.elapsedRealtimeNanos(); 1823 Map<String, String> data = mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key); 1824 long end = SystemClock.elapsedRealtimeNanos(); 1825 1826 if (sVDBG) { 1827 localLog("readNetworkVariableFromSupplicantFile configKey=[" + configKey + "] key=" 1828 + key + " duration=" + (long) (end - start)); 1829 } 1830 return data.get(configKey); 1831 } 1832 1833 boolean needsUnlockedKeyStore() { 1834 1835 // Any network using certificates to authenticate access requires 1836 // unlocked key store; unless the certificates can be stored with 1837 // hardware encryption 1838 1839 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 1840 1841 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) 1842 && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1843 1844 if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 1845 return true; 1846 } 1847 } 1848 } 1849 1850 return false; 1851 } 1852 1853 void readPasspointConfig(Map<String, WifiConfiguration> configs, 1854 SparseArray<Map<String, String>> networkExtras) { 1855 List<HomeSP> homeSPs; 1856 try { 1857 homeSPs = mMOManager.loadAllSPs(); 1858 } catch (IOException e) { 1859 loge("Could not read " + PPS_FILE + " : " + e); 1860 return; 1861 } 1862 1863 int matchedConfigs = 0; 1864 for (HomeSP homeSp : homeSPs) { 1865 String fqdn = homeSp.getFQDN(); 1866 Log.d(TAG, "Looking for " + fqdn); 1867 for (WifiConfiguration config : configs.values()) { 1868 Log.d(TAG, "Testing " + config.SSID); 1869 1870 if (config.enterpriseConfig == null) { 1871 continue; 1872 } 1873 final String configFqdn = 1874 networkExtras.get(config.networkId).get(WifiConfigStore.ID_STRING_KEY_FQDN); 1875 if (configFqdn != null && configFqdn.equals(fqdn)) { 1876 Log.d(TAG, "Matched " + configFqdn + " with " + config.networkId); 1877 ++matchedConfigs; 1878 config.FQDN = fqdn; 1879 config.providerFriendlyName = homeSp.getFriendlyName(); 1880 1881 HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums(); 1882 config.roamingConsortiumIds = new long[roamingConsortiumIds.size()]; 1883 int i = 0; 1884 for (long id : roamingConsortiumIds) { 1885 config.roamingConsortiumIds[i] = id; 1886 i++; 1887 } 1888 IMSIParameter imsiParameter = homeSp.getCredential().getImsi(); 1889 config.enterpriseConfig.setPlmn( 1890 imsiParameter != null ? imsiParameter.toString() : null); 1891 config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm()); 1892 } 1893 } 1894 } 1895 1896 Log.d(TAG, "loaded " + matchedConfigs + " passpoint configs"); 1897 } 1898 1899 public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) { 1900 mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() { 1901 @Override 1902 public void onWriteCalled(DataOutputStream out) throws IOException { 1903 try { 1904 if (homeSP != null) { 1905 mMOManager.addSP(homeSP); 1906 } else { 1907 mMOManager.removeSP(fqdn); 1908 } 1909 } catch (IOException e) { 1910 loge("Could not write " + PPS_FILE + " : " + e); 1911 } 1912 } 1913 }, false); 1914 } 1915 1916 /** 1917 * Write network history, WifiConfigurations and mScanDetailCaches to file. 1918 */ 1919 private void readNetworkHistory(Map<String, WifiConfiguration> configs) { 1920 mWifiNetworkHistory.readNetworkHistory(configs, 1921 mScanDetailCaches, 1922 mDeletedSSIDs, 1923 mDeletedEphemeralSSIDs); 1924 } 1925 1926 /** 1927 * Read Network history from file, merge it into mConfiguredNetowrks and mScanDetailCaches 1928 */ 1929 public void writeKnownNetworkHistory() { 1930 final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 1931 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 1932 networks.add(new WifiConfiguration(config)); 1933 } 1934 mWifiNetworkHistory.writeKnownNetworkHistory(networks, 1935 mScanDetailCaches, 1936 mDeletedSSIDs, 1937 mDeletedEphemeralSSIDs); 1938 } 1939 1940 public void setAndEnableLastSelectedConfiguration(int netId) { 1941 if (sVDBG) { 1942 loge("setLastSelectedConfiguration " + Integer.toString(netId)); 1943 } 1944 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 1945 mLastSelectedConfiguration = null; 1946 mLastSelectedTimeStamp = -1; 1947 } else { 1948 WifiConfiguration selected = getWifiConfiguration(netId); 1949 if (selected == null) { 1950 mLastSelectedConfiguration = null; 1951 mLastSelectedTimeStamp = -1; 1952 } else { 1953 mLastSelectedConfiguration = selected.configKey(); 1954 mLastSelectedTimeStamp = System.currentTimeMillis(); 1955 updateNetworkSelectionStatus(netId, 1956 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 1957 if (sVDBG) { 1958 loge("setLastSelectedConfiguration now: " + mLastSelectedConfiguration); 1959 } 1960 } 1961 } 1962 } 1963 1964 public void setLatestUserSelectedConfiguration(WifiConfiguration network) { 1965 if (network != null) { 1966 mLastSelectedConfiguration = network.configKey(); 1967 mLastSelectedTimeStamp = System.currentTimeMillis(); 1968 } 1969 } 1970 1971 public String getLastSelectedConfiguration() { 1972 return mLastSelectedConfiguration; 1973 } 1974 1975 public long getLastSelectedTimeStamp() { 1976 return mLastSelectedTimeStamp; 1977 } 1978 1979 public boolean isLastSelectedConfiguration(WifiConfiguration config) { 1980 return (mLastSelectedConfiguration != null 1981 && config != null 1982 && mLastSelectedConfiguration.equals(config.configKey())); 1983 } 1984 1985 private void writeIpAndProxyConfigurations() { 1986 final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 1987 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 1988 if (!config.ephemeral) { 1989 networks.put(configKey(config), config.getIpConfiguration()); 1990 } 1991 } 1992 1993 mIpconfigStore.writeIpAndProxyConfigurations(IP_CONFIG_FILE, networks); 1994 } 1995 1996 private void readIpAndProxyConfigurations() { 1997 SparseArray<IpConfiguration> networks = 1998 mIpconfigStore.readIpAndProxyConfigurations(IP_CONFIG_FILE); 1999 2000 if (networks == null || networks.size() == 0) { 2001 // IpConfigStore.readIpAndProxyConfigurations has already logged an error. 2002 return; 2003 } 2004 2005 for (int i = 0; i < networks.size(); i++) { 2006 int id = networks.keyAt(i); 2007 WifiConfiguration config = mConfiguredNetworks.getByConfigKeyIDForAllUsers(id); 2008 // This is the only place the map is looked up through a (dangerous) hash-value! 2009 2010 if (config == null || config.ephemeral) { 2011 loge("configuration found for missing network, nid=" + id 2012 + ", ignored, networks.size=" + Integer.toString(networks.size())); 2013 } else { 2014 config.setIpConfiguration(networks.valueAt(i)); 2015 } 2016 } 2017 } 2018 2019 private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) { 2020 /* 2021 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 2022 * network configuration. Otherwise, the networkId should 2023 * refer to an existing configuration. 2024 */ 2025 2026 if (sVDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); 2027 if (config.isPasspoint() && !mMOManager.isEnabled()) { 2028 Log.e(TAG, "Passpoint is not enabled"); 2029 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2030 } 2031 2032 boolean newNetwork = false; 2033 boolean existingMO = false; 2034 WifiConfiguration currentConfig; 2035 // networkId of INVALID_NETWORK_ID means we want to create a new network 2036 if (config.networkId == INVALID_NETWORK_ID) { 2037 // Try to fetch the existing config using configKey 2038 currentConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()); 2039 if (currentConfig != null) { 2040 config.networkId = currentConfig.networkId; 2041 } else { 2042 if (mMOManager.getHomeSP(config.FQDN) != null) { 2043 loge("addOrUpdateNetworkNative passpoint " + config.FQDN 2044 + " was found, but no network Id"); 2045 existingMO = true; 2046 } 2047 newNetwork = true; 2048 } 2049 } else { 2050 // Fetch the existing config using networkID 2051 currentConfig = mConfiguredNetworks.getForCurrentUser(config.networkId); 2052 } 2053 if (!mWifiConfigStore.addOrUpdateNetwork(config, currentConfig)) { 2054 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2055 } 2056 int netId = config.networkId; 2057 String savedConfigKey = config.configKey(); 2058 2059 /* An update of the network variables requires reading them 2060 * back from the supplicant to update mConfiguredNetworks. 2061 * This is because some of the variables (SSID, wep keys & 2062 * passphrases) reflect different values when read back than 2063 * when written. For example, wep key is stored as * irrespective 2064 * of the value sent to the supplicant. 2065 */ 2066 if (currentConfig == null) { 2067 currentConfig = new WifiConfiguration(); 2068 currentConfig.setIpAssignment(IpAssignment.DHCP); 2069 currentConfig.setProxySettings(ProxySettings.NONE); 2070 currentConfig.networkId = netId; 2071 if (config != null) { 2072 // Carry over the creation parameters 2073 currentConfig.selfAdded = config.selfAdded; 2074 currentConfig.didSelfAdd = config.didSelfAdd; 2075 currentConfig.ephemeral = config.ephemeral; 2076 currentConfig.meteredHint = config.meteredHint; 2077 currentConfig.lastConnectUid = config.lastConnectUid; 2078 currentConfig.lastUpdateUid = config.lastUpdateUid; 2079 currentConfig.creatorUid = config.creatorUid; 2080 currentConfig.creatorName = config.creatorName; 2081 currentConfig.lastUpdateName = config.lastUpdateName; 2082 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration; 2083 currentConfig.FQDN = config.FQDN; 2084 currentConfig.providerFriendlyName = config.providerFriendlyName; 2085 currentConfig.roamingConsortiumIds = config.roamingConsortiumIds; 2086 currentConfig.validatedInternetAccess = config.validatedInternetAccess; 2087 currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports; 2088 currentConfig.updateTime = config.updateTime; 2089 currentConfig.creationTime = config.creationTime; 2090 currentConfig.shared = config.shared; 2091 } 2092 if (DBG) { 2093 log("created new config netId=" + Integer.toString(netId) 2094 + " uid=" + Integer.toString(currentConfig.creatorUid) 2095 + " name=" + currentConfig.creatorName); 2096 } 2097 } 2098 2099 /* save HomeSP object for passpoint networks */ 2100 HomeSP homeSP = null; 2101 2102 if (!existingMO && config.isPasspoint()) { 2103 try { 2104 if (config.updateIdentifier == null) { // Only create an MO for r1 networks 2105 Credential credential = 2106 new Credential(config.enterpriseConfig, mKeyStore, !newNetwork); 2107 HashSet<Long> roamingConsortiumIds = new HashSet<Long>(); 2108 for (Long roamingConsortiumId : config.roamingConsortiumIds) { 2109 roamingConsortiumIds.add(roamingConsortiumId); 2110 } 2111 2112 homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN, 2113 roamingConsortiumIds, Collections.<String>emptySet(), 2114 Collections.<Long>emptySet(), Collections.<Long>emptyList(), 2115 config.providerFriendlyName, null, credential); 2116 2117 log("created a homeSP object for " + config.networkId + ":" + config.SSID); 2118 } 2119 2120 /* fix enterprise config properties for passpoint */ 2121 currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm()); 2122 currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn()); 2123 } catch (IOException ioe) { 2124 Log.e(TAG, "Failed to create Passpoint config: " + ioe); 2125 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2126 } 2127 } 2128 2129 if (uid != WifiConfiguration.UNKNOWN_UID) { 2130 if (newNetwork) { 2131 currentConfig.creatorUid = uid; 2132 } else { 2133 currentConfig.lastUpdateUid = uid; 2134 } 2135 } 2136 2137 // For debug, record the time the configuration was modified 2138 StringBuilder sb = new StringBuilder(); 2139 sb.append("time="); 2140 Calendar c = Calendar.getInstance(); 2141 c.setTimeInMillis(System.currentTimeMillis()); 2142 sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); 2143 2144 if (newNetwork) { 2145 currentConfig.creationTime = sb.toString(); 2146 } else { 2147 currentConfig.updateTime = sb.toString(); 2148 } 2149 2150 if (currentConfig.status == WifiConfiguration.Status.ENABLED) { 2151 // Make sure autojoin remain in sync with user modifying the configuration 2152 updateNetworkSelectionStatus(currentConfig.networkId, 2153 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 2154 } 2155 2156 if (currentConfig.configKey().equals(getLastSelectedConfiguration()) 2157 && currentConfig.ephemeral) { 2158 // Make the config non-ephemeral since the user just explicitly clicked it. 2159 currentConfig.ephemeral = false; 2160 if (DBG) { 2161 log("remove ephemeral status netId=" + Integer.toString(netId) 2162 + " " + currentConfig.configKey()); 2163 } 2164 } 2165 2166 if (sVDBG) log("will read network variables netId=" + Integer.toString(netId)); 2167 2168 readNetworkVariables(currentConfig); 2169 // When we read back the config from wpa_supplicant, some of the default values are set 2170 // which could change the configKey. 2171 if (!savedConfigKey.equals(currentConfig.configKey())) { 2172 if (!mWifiConfigStore.saveNetworkMetadata(currentConfig)) { 2173 loge("Failed to set network metadata. Removing config " + config.networkId); 2174 mWifiConfigStore.removeNetwork(config); 2175 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2176 } 2177 } 2178 2179 // Persist configuration paramaters that are not saved by supplicant. 2180 if (config.lastUpdateName != null) { 2181 currentConfig.lastUpdateName = config.lastUpdateName; 2182 } 2183 if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) { 2184 currentConfig.lastUpdateUid = config.lastUpdateUid; 2185 } 2186 2187 mConfiguredNetworks.put(currentConfig); 2188 2189 NetworkUpdateResult result = 2190 writeIpAndProxyConfigurationsOnChange(currentConfig, config, newNetwork); 2191 result.setIsNewNetwork(newNetwork); 2192 result.setNetworkId(netId); 2193 2194 if (homeSP != null) { 2195 writePasspointConfigs(null, homeSP); 2196 } 2197 2198 saveConfig(); 2199 writeKnownNetworkHistory(); 2200 2201 return result; 2202 } 2203 2204 public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) { 2205 WifiConfiguration config = mConfiguredNetworks.getByFQDNForCurrentUser(homeSP.getFQDN()); 2206 if (config == null) { 2207 Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN()); 2208 } 2209 return config; 2210 } 2211 2212 public HomeSP getHomeSPForConfig(WifiConfiguration config) { 2213 WifiConfiguration storedConfig = mConfiguredNetworks.getForCurrentUser(config.networkId); 2214 return storedConfig != null && storedConfig.isPasspoint() 2215 ? mMOManager.getHomeSP(storedConfig.FQDN) 2216 : null; 2217 } 2218 2219 public ScanDetailCache getScanDetailCache(WifiConfiguration config) { 2220 if (config == null) return null; 2221 ScanDetailCache cache = mScanDetailCaches.get(config.networkId); 2222 if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { 2223 cache = new ScanDetailCache(config); 2224 mScanDetailCaches.put(config.networkId, cache); 2225 } 2226 return cache; 2227 } 2228 2229 /** 2230 * This function run thru the Saved WifiConfigurations and check if some should be linked. 2231 * @param config 2232 */ 2233 public void linkConfiguration(WifiConfiguration config) { 2234 if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, 2235 mUserManager.getProfiles(mCurrentUserId))) { 2236 loge("linkConfiguration: Attempting to link config " + config.configKey() 2237 + " that is not visible to the current user."); 2238 return; 2239 } 2240 2241 if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) { 2242 // Ignore configurations with large number of BSSIDs 2243 return; 2244 } 2245 if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 2246 // Only link WPA_PSK config 2247 return; 2248 } 2249 for (WifiConfiguration link : mConfiguredNetworks.valuesForCurrentUser()) { 2250 boolean doLink = false; 2251 2252 if (link.configKey().equals(config.configKey())) { 2253 continue; 2254 } 2255 2256 if (link.ephemeral) { 2257 continue; 2258 } 2259 2260 // Autojoin will be allowed to dynamically jump from a linked configuration 2261 // to another, hence only link configurations that have equivalent level of security 2262 if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) { 2263 continue; 2264 } 2265 2266 ScanDetailCache linkedScanDetailCache = getScanDetailCache(link); 2267 if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) { 2268 // Ignore configurations with large number of BSSIDs 2269 continue; 2270 } 2271 2272 if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) { 2273 // If both default GW are known, link only if they are equal 2274 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) { 2275 if (sVDBG) { 2276 loge("linkConfiguration link due to same gw " + link.SSID 2277 + " and " + config.SSID + " GW " + config.defaultGwMacAddress); 2278 } 2279 doLink = true; 2280 } 2281 } else { 2282 // We do not know BOTH default gateways hence we will try to link 2283 // hoping that WifiConfigurations are indeed behind the same gateway. 2284 // once both WifiConfiguration have been tried and thus once both efault gateways 2285 // are known we will revisit the choice of linking them 2286 if ((getScanDetailCache(config) != null) 2287 && (getScanDetailCache(config).size() <= 6)) { 2288 2289 for (String abssid : getScanDetailCache(config).keySet()) { 2290 for (String bbssid : linkedScanDetailCache.keySet()) { 2291 if (sVVDBG) { 2292 loge("linkConfiguration try to link due to DBDC BSSID match " 2293 + link.SSID + " and " + config.SSID + " bssida " + abssid 2294 + " bssidb " + bbssid); 2295 } 2296 if (abssid.regionMatches(true, 0, bbssid, 0, 16)) { 2297 // If first 16 ascii characters of BSSID matches, 2298 // we assume this is a DBDC 2299 doLink = true; 2300 } 2301 } 2302 } 2303 } 2304 } 2305 2306 if (doLink && mOnlyLinkSameCredentialConfigurations) { 2307 String apsk = 2308 readNetworkVariableFromSupplicantFile(link.configKey(), "psk"); 2309 String bpsk = 2310 readNetworkVariableFromSupplicantFile(config.configKey(), "psk"); 2311 if (apsk == null || bpsk == null 2312 || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk) 2313 || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK) 2314 || !apsk.equals(bpsk)) { 2315 doLink = false; 2316 } 2317 } 2318 2319 if (doLink) { 2320 if (sVDBG) { 2321 loge("linkConfiguration: will link " + link.configKey() 2322 + " and " + config.configKey()); 2323 } 2324 if (link.linkedConfigurations == null) { 2325 link.linkedConfigurations = new HashMap<String, Integer>(); 2326 } 2327 if (config.linkedConfigurations == null) { 2328 config.linkedConfigurations = new HashMap<String, Integer>(); 2329 } 2330 if (link.linkedConfigurations.get(config.configKey()) == null) { 2331 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 2332 } 2333 if (config.linkedConfigurations.get(link.configKey()) == null) { 2334 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 2335 } 2336 } else { 2337 if (link.linkedConfigurations != null 2338 && (link.linkedConfigurations.get(config.configKey()) != null)) { 2339 if (sVDBG) { 2340 loge("linkConfiguration: un-link " + config.configKey() 2341 + " from " + link.configKey()); 2342 } 2343 link.linkedConfigurations.remove(config.configKey()); 2344 } 2345 if (config.linkedConfigurations != null 2346 && (config.linkedConfigurations.get(link.configKey()) != null)) { 2347 if (sVDBG) { 2348 loge("linkConfiguration: un-link " + link.configKey() 2349 + " from " + config.configKey()); 2350 } 2351 config.linkedConfigurations.remove(link.configKey()); 2352 } 2353 } 2354 } 2355 } 2356 2357 public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) { 2358 if (config == null) { 2359 return null; 2360 } 2361 long now_ms = System.currentTimeMillis(); 2362 2363 HashSet<Integer> channels = new HashSet<Integer>(); 2364 2365 //get channels for this configuration, if there are at least 2 BSSIDs 2366 if (getScanDetailCache(config) == null && config.linkedConfigurations == null) { 2367 return null; 2368 } 2369 2370 if (sVDBG) { 2371 StringBuilder dbg = new StringBuilder(); 2372 dbg.append("makeChannelList age=" + Integer.toString(age) 2373 + " for " + config.configKey() 2374 + " max=" + mMaxNumActiveChannelsForPartialScans); 2375 if (getScanDetailCache(config) != null) { 2376 dbg.append(" bssids=" + getScanDetailCache(config).size()); 2377 } 2378 if (config.linkedConfigurations != null) { 2379 dbg.append(" linked=" + config.linkedConfigurations.size()); 2380 } 2381 loge(dbg.toString()); 2382 } 2383 2384 int numChannels = 0; 2385 if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) { 2386 for (ScanDetail scanDetail : getScanDetailCache(config).values()) { 2387 ScanResult result = scanDetail.getScanResult(); 2388 //TODO : cout active and passive channels separately 2389 if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) { 2390 break; 2391 } 2392 if (sVDBG) { 2393 boolean test = (now_ms - result.seen) < age; 2394 loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency) 2395 + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test); 2396 } 2397 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2398 channels.add(result.frequency); 2399 numChannels++; 2400 } 2401 } 2402 } 2403 2404 //get channels for linked configurations 2405 if (config.linkedConfigurations != null) { 2406 for (String key : config.linkedConfigurations.keySet()) { 2407 WifiConfiguration linked = getWifiConfiguration(key); 2408 if (linked == null) { 2409 continue; 2410 } 2411 if (getScanDetailCache(linked) == null) { 2412 continue; 2413 } 2414 for (ScanDetail scanDetail : getScanDetailCache(linked).values()) { 2415 ScanResult result = scanDetail.getScanResult(); 2416 if (sVDBG) { 2417 loge("has link: " + result.BSSID 2418 + " freq=" + Integer.toString(result.frequency) 2419 + " age=" + Long.toString(now_ms - result.seen)); 2420 } 2421 if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) { 2422 break; 2423 } 2424 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2425 channels.add(result.frequency); 2426 numChannels++; 2427 } 2428 } 2429 } 2430 } 2431 return channels; 2432 } 2433 2434 private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) { 2435 if (!mMOManager.isConfigured()) { 2436 if (mEnableOsuQueries) { 2437 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 2438 List<Constants.ANQPElementType> querySet = 2439 ANQPFactory.buildQueryList(networkDetail, false, true); 2440 2441 if (networkDetail.queriable(querySet)) { 2442 querySet = mAnqpCache.initiate(networkDetail, querySet); 2443 if (querySet != null) { 2444 mSupplicantBridge.startANQP(scanDetail, querySet); 2445 } 2446 updateAnqpCache(scanDetail, networkDetail.getANQPElements()); 2447 } 2448 } 2449 return null; 2450 } 2451 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 2452 if (!networkDetail.hasInterworking()) { 2453 return null; 2454 } 2455 updateAnqpCache(scanDetail, networkDetail.getANQPElements()); 2456 2457 Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true); 2458 Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() 2459 + " pass 1 matches: " + toMatchString(matches)); 2460 return matches; 2461 } 2462 2463 private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) { 2464 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 2465 2466 ANQPData anqpData = mAnqpCache.getEntry(networkDetail); 2467 2468 Map<Constants.ANQPElementType, ANQPElement> anqpElements = 2469 anqpData != null ? anqpData.getANQPElements() : null; 2470 2471 boolean queried = !query; 2472 Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values(); 2473 Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size()); 2474 Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString() 2475 + ", anqp " + (anqpData != null ? "present" : "missing") 2476 + ", query " + query + ", home sps: " + homeSPs.size()); 2477 2478 for (HomeSP homeSP : homeSPs) { 2479 PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor); 2480 2481 Log.d(Utils.hs2LogTag(getClass()), " -- " 2482 + homeSP.getFQDN() + ": match " + match + ", queried " + queried); 2483 2484 if ((match == PasspointMatch.Incomplete || mEnableOsuQueries) && !queried) { 2485 boolean matchSet = match == PasspointMatch.Incomplete; 2486 boolean osu = mEnableOsuQueries; 2487 List<Constants.ANQPElementType> querySet = 2488 ANQPFactory.buildQueryList(networkDetail, matchSet, osu); 2489 if (networkDetail.queriable(querySet)) { 2490 querySet = mAnqpCache.initiate(networkDetail, querySet); 2491 if (querySet != null) { 2492 mSupplicantBridge.startANQP(scanDetail, querySet); 2493 } 2494 } 2495 queried = true; 2496 } 2497 matches.put(homeSP, match); 2498 } 2499 return matches; 2500 } 2501 2502 public Map<Constants.ANQPElementType, ANQPElement> getANQPData(NetworkDetail network) { 2503 ANQPData data = mAnqpCache.getEntry(network); 2504 return data != null ? data.getANQPElements() : null; 2505 } 2506 2507 public SIMAccessor getSIMAccessor() { 2508 return mSIMAccessor; 2509 } 2510 2511 public void notifyANQPDone(Long bssid, boolean success) { 2512 mSupplicantBridge.notifyANQPDone(bssid, success); 2513 } 2514 2515 public void notifyIconReceived(IconEvent iconEvent) { 2516 Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION); 2517 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2518 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, iconEvent.getBSSID()); 2519 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_FILE, iconEvent.getFileName()); 2520 try { 2521 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_DATA, 2522 mSupplicantBridge.retrieveIcon(iconEvent)); 2523 } catch (IOException ioe) { 2524 /* Simply omit the icon data as a failure indication */ 2525 } 2526 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 2527 2528 } 2529 2530 private void updateAnqpCache(ScanDetail scanDetail, 2531 Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 2532 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 2533 2534 if (anqpElements == null) { 2535 // Try to pull cached data if query failed. 2536 ANQPData data = mAnqpCache.getEntry(networkDetail); 2537 if (data != null) { 2538 scanDetail.propagateANQPInfo(data.getANQPElements()); 2539 } 2540 return; 2541 } 2542 2543 mAnqpCache.update(networkDetail, anqpElements); 2544 } 2545 2546 private static String toMatchString(Map<HomeSP, PasspointMatch> matches) { 2547 StringBuilder sb = new StringBuilder(); 2548 for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) { 2549 sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue()); 2550 } 2551 return sb.toString(); 2552 } 2553 2554 private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail, 2555 Map<HomeSP, PasspointMatch> matches, 2556 List<WifiConfiguration> associatedWifiConfigurations) { 2557 2558 for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) { 2559 PasspointMatch match = entry.getValue(); 2560 if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) { 2561 WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey()); 2562 if (config != null) { 2563 cacheScanResultForConfig(config, scanDetail, entry.getValue()); 2564 if (associatedWifiConfigurations != null) { 2565 associatedWifiConfigurations.add(config); 2566 } 2567 } else { 2568 Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '" 2569 + entry.getKey().getFQDN() + "'"); 2570 /* perhaps the configuration was deleted?? */ 2571 } 2572 } 2573 } 2574 } 2575 2576 private void cacheScanResultForConfig( 2577 WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) { 2578 2579 ScanResult scanResult = scanDetail.getScanResult(); 2580 2581 ScanDetailCache scanDetailCache = getScanDetailCache(config); 2582 if (scanDetailCache == null) { 2583 Log.w(TAG, "Could not allocate scan cache for " + config.SSID); 2584 return; 2585 } 2586 2587 // Adding a new BSSID 2588 ScanResult result = scanDetailCache.get(scanResult.BSSID); 2589 if (result != null) { 2590 // transfer the black list status 2591 scanResult.blackListTimestamp = result.blackListTimestamp; 2592 scanResult.numIpConfigFailures = result.numIpConfigFailures; 2593 scanResult.numConnection = result.numConnection; 2594 scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate; 2595 } 2596 2597 if (config.ephemeral) { 2598 // For an ephemeral Wi-Fi config, the ScanResult should be considered 2599 // untrusted. 2600 scanResult.untrusted = true; 2601 } 2602 2603 if (scanDetailCache.size() > (MAX_NUM_SCAN_CACHE_ENTRIES + 64)) { 2604 long now_dbg = 0; 2605 if (sVVDBG) { 2606 loge(" Will trim config " + config.configKey() 2607 + " size " + scanDetailCache.size()); 2608 2609 for (ScanDetail sd : scanDetailCache.values()) { 2610 loge(" " + sd.getBSSIDString() + " " + sd.getSeen()); 2611 } 2612 now_dbg = SystemClock.elapsedRealtimeNanos(); 2613 } 2614 // Trim the scan result cache to MAX_NUM_SCAN_CACHE_ENTRIES entries max 2615 // Since this operation is expensive, make sure it is not performed 2616 // until the cache has grown significantly above the trim treshold 2617 scanDetailCache.trim(MAX_NUM_SCAN_CACHE_ENTRIES); 2618 if (sVVDBG) { 2619 long diff = SystemClock.elapsedRealtimeNanos() - now_dbg; 2620 loge(" Finished trimming config, time(ns) " + diff); 2621 for (ScanDetail sd : scanDetailCache.values()) { 2622 loge(" " + sd.getBSSIDString() + " " + sd.getSeen()); 2623 } 2624 } 2625 } 2626 2627 // Add the scan result to this WifiConfiguration 2628 if (passpointMatch != null) { 2629 scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config)); 2630 } else { 2631 scanDetailCache.put(scanDetail); 2632 } 2633 2634 // Since we added a scan result to this configuration, re-attempt linking 2635 linkConfiguration(config); 2636 } 2637 2638 private boolean isEncryptionWep(String encryption) { 2639 return encryption.contains("WEP"); 2640 } 2641 2642 private boolean isEncryptionPsk(String encryption) { 2643 return encryption.contains("PSK"); 2644 } 2645 2646 private boolean isEncryptionEap(String encryption) { 2647 return encryption.contains("EAP"); 2648 } 2649 2650 public boolean isOpenNetwork(String encryption) { 2651 if (!isEncryptionWep(encryption) && !isEncryptionPsk(encryption) 2652 && !isEncryptionEap(encryption)) { 2653 return true; 2654 } 2655 return false; 2656 } 2657 2658 public boolean isOpenNetwork(ScanResult scan) { 2659 String scanResultEncrypt = scan.capabilities; 2660 return isOpenNetwork(scanResultEncrypt); 2661 } 2662 2663 public boolean isOpenNetwork(WifiConfiguration config) { 2664 String configEncrypt = config.configKey(); 2665 return isOpenNetwork(configEncrypt); 2666 } 2667 2668 /** 2669 * create a mapping between the scandetail and the Wificonfiguration it associated with 2670 * because Passpoint, one BSSID can associated with multiple SSIDs 2671 * @param scanDetail input a scanDetail from the scan result 2672 * @return List<WifiConfiguration> a list of WifiConfigurations associated to this scanDetail 2673 */ 2674 public List<WifiConfiguration> updateSavedNetworkWithNewScanDetail(ScanDetail scanDetail) { 2675 2676 ScanResult scanResult = scanDetail.getScanResult(); 2677 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 2678 List<WifiConfiguration> associatedWifiConfigurations = new ArrayList<WifiConfiguration>(); 2679 2680 if (scanResult == null) { 2681 return null; 2682 } 2683 2684 String ssid = "\"" + scanResult.SSID + "\""; 2685 2686 if (networkDetail.hasInterworking()) { 2687 Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail); 2688 if (matches != null) { 2689 cacheScanResultForPasspointConfigs(scanDetail, matches, 2690 associatedWifiConfigurations); 2691 //Do not return here. A BSSID can belong to both passpoint network and non-passpoint 2692 //Network 2693 } 2694 } 2695 2696 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 2697 boolean found = false; 2698 if (config.SSID == null || !config.SSID.equals(ssid)) { 2699 continue; 2700 } 2701 if (DBG) { 2702 localLog("updateSavedNetworkWithNewScanDetail(): try " + config.configKey() 2703 + " SSID=" + config.SSID + " " + scanResult.SSID + " " 2704 + scanResult.capabilities); 2705 } 2706 2707 String scanResultEncrypt = scanResult.capabilities; 2708 String configEncrypt = config.configKey(); 2709 if (isEncryptionWep(scanResultEncrypt) && isEncryptionWep(configEncrypt) 2710 || (isEncryptionPsk(scanResultEncrypt) && isEncryptionPsk(configEncrypt)) 2711 || (isEncryptionEap(scanResultEncrypt) && isEncryptionEap(configEncrypt)) 2712 || (isOpenNetwork(scanResultEncrypt) && isOpenNetwork(configEncrypt))) { 2713 found = true; 2714 } 2715 2716 if (found) { 2717 cacheScanResultForConfig(config, scanDetail, null); 2718 associatedWifiConfigurations.add(config); 2719 } 2720 } 2721 2722 if (associatedWifiConfigurations.size() == 0) { 2723 return null; 2724 } else { 2725 return associatedWifiConfigurations; 2726 } 2727 } 2728 2729 /** 2730 * Handles the switch to a different foreground user: 2731 * - Removes all ephemeral networks 2732 * - Disables private network configurations belonging to the previous foreground user 2733 * - Enables private network configurations belonging to the new foreground user 2734 * 2735 * @param userId The identifier of the new foreground user, after the switch. 2736 * 2737 * TODO(b/26785736): Terminate background users if the new foreground user has one or more 2738 * private network configurations. 2739 */ 2740 public void handleUserSwitch(int userId) { 2741 mCurrentUserId = userId; 2742 Set<WifiConfiguration> ephemeralConfigs = new HashSet<>(); 2743 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 2744 if (config.ephemeral) { 2745 ephemeralConfigs.add(config); 2746 } 2747 } 2748 if (!ephemeralConfigs.isEmpty()) { 2749 for (WifiConfiguration config : ephemeralConfigs) { 2750 removeConfigWithoutBroadcast(config); 2751 } 2752 saveConfig(); 2753 writeKnownNetworkHistory(); 2754 } 2755 2756 final List<WifiConfiguration> hiddenConfigurations = 2757 mConfiguredNetworks.handleUserSwitch(mCurrentUserId); 2758 for (WifiConfiguration network : hiddenConfigurations) { 2759 disableNetworkNative(network); 2760 } 2761 enableAllNetworks(); 2762 2763 // TODO(b/26785746): This broadcast is unnecessary if either of the following is true: 2764 // * The user switch did not change the list of visible networks 2765 // * The user switch revealed additional networks that were temporarily disabled and got 2766 // re-enabled now (because enableAllNetworks() sent the same broadcast already). 2767 sendConfiguredNetworksChangedBroadcast(); 2768 } 2769 2770 public int getCurrentUserId() { 2771 return mCurrentUserId; 2772 } 2773 2774 public boolean isCurrentUserProfile(int userId) { 2775 if (userId == mCurrentUserId) { 2776 return true; 2777 } 2778 final UserInfo parent = mUserManager.getProfileParent(userId); 2779 return parent != null && parent.id == mCurrentUserId; 2780 } 2781 2782 /* Compare current and new configuration and write to file on change */ 2783 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 2784 WifiConfiguration currentConfig, 2785 WifiConfiguration newConfig, 2786 boolean isNewNetwork) { 2787 boolean ipChanged = false; 2788 boolean proxyChanged = false; 2789 2790 switch (newConfig.getIpAssignment()) { 2791 case STATIC: 2792 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 2793 ipChanged = true; 2794 } else { 2795 ipChanged = !Objects.equals( 2796 currentConfig.getStaticIpConfiguration(), 2797 newConfig.getStaticIpConfiguration()); 2798 } 2799 break; 2800 case DHCP: 2801 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 2802 ipChanged = true; 2803 } 2804 break; 2805 case UNASSIGNED: 2806 /* Ignore */ 2807 break; 2808 default: 2809 loge("Ignore invalid ip assignment during write"); 2810 break; 2811 } 2812 2813 switch (newConfig.getProxySettings()) { 2814 case STATIC: 2815 case PAC: 2816 ProxyInfo newHttpProxy = newConfig.getHttpProxy(); 2817 ProxyInfo currentHttpProxy = currentConfig.getHttpProxy(); 2818 2819 if (newHttpProxy != null) { 2820 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 2821 } else { 2822 proxyChanged = (currentHttpProxy != null); 2823 } 2824 break; 2825 case NONE: 2826 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) { 2827 proxyChanged = true; 2828 } 2829 break; 2830 case UNASSIGNED: 2831 /* Ignore */ 2832 break; 2833 default: 2834 loge("Ignore invalid proxy configuration during write"); 2835 break; 2836 } 2837 2838 if (ipChanged) { 2839 currentConfig.setIpAssignment(newConfig.getIpAssignment()); 2840 currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration()); 2841 log("IP config changed SSID = " + currentConfig.SSID); 2842 if (currentConfig.getStaticIpConfiguration() != null) { 2843 log(" static configuration: " 2844 + currentConfig.getStaticIpConfiguration().toString()); 2845 } 2846 } 2847 2848 if (proxyChanged) { 2849 currentConfig.setProxySettings(newConfig.getProxySettings()); 2850 currentConfig.setHttpProxy(newConfig.getHttpProxy()); 2851 log("proxy changed SSID = " + currentConfig.SSID); 2852 if (currentConfig.getHttpProxy() != null) { 2853 log(" proxyProperties: " + currentConfig.getHttpProxy().toString()); 2854 } 2855 } 2856 2857 if (ipChanged || proxyChanged || isNewNetwork) { 2858 if (sVDBG) { 2859 logd("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " 2860 + newConfig.SSID + " path: " + IP_CONFIG_FILE); 2861 } 2862 writeIpAndProxyConfigurations(); 2863 } 2864 return new NetworkUpdateResult(ipChanged, proxyChanged); 2865 } 2866 2867 /** 2868 * Read the variables from the supplicant daemon that are needed to 2869 * fill in the WifiConfiguration object. 2870 * 2871 * @param config the {@link WifiConfiguration} object to be filled in. 2872 */ 2873 private void readNetworkVariables(WifiConfiguration config) { 2874 mWifiConfigStore.readNetworkVariables(config); 2875 } 2876 2877 /* return the allowed key management based on a scan result */ 2878 2879 public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 2880 2881 WifiConfiguration config = new WifiConfiguration(); 2882 2883 config.SSID = "\"" + result.SSID + "\""; 2884 2885 if (sVDBG) { 2886 loge("WifiConfiguration from scan results " 2887 + config.SSID + " cap " + result.capabilities); 2888 } 2889 2890 if (result.capabilities.contains("PSK") || result.capabilities.contains("EAP") 2891 || result.capabilities.contains("WEP")) { 2892 if (result.capabilities.contains("PSK")) { 2893 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 2894 } 2895 2896 if (result.capabilities.contains("EAP")) { 2897 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 2898 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 2899 } 2900 2901 if (result.capabilities.contains("WEP")) { 2902 config.allowedKeyManagement.set(KeyMgmt.NONE); 2903 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); 2904 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 2905 } 2906 } else { 2907 config.allowedKeyManagement.set(KeyMgmt.NONE); 2908 } 2909 2910 return config; 2911 } 2912 2913 public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) { 2914 ScanResult result = scanDetail.getScanResult(); 2915 return wifiConfigurationFromScanResult(result); 2916 } 2917 2918 /* Returns a unique for a given configuration */ 2919 private static int configKey(WifiConfiguration config) { 2920 String key = config.configKey(); 2921 return key.hashCode(); 2922 } 2923 2924 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2925 pw.println("Dump of WifiConfigManager"); 2926 pw.println("mLastPriority " + mLastPriority); 2927 pw.println("Configured networks"); 2928 for (WifiConfiguration conf : getAllConfiguredNetworks()) { 2929 pw.println(conf); 2930 } 2931 pw.println(); 2932 if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) { 2933 pw.println("LostConfigs: "); 2934 for (String s : mLostConfigsDbg) { 2935 pw.println(s); 2936 } 2937 } 2938 if (mLocalLog != null) { 2939 pw.println("WifiConfigManager - Log Begin ----"); 2940 mLocalLog.dump(fd, pw, args); 2941 pw.println("WifiConfigManager - Log End ----"); 2942 } 2943 if (mMOManager.isConfigured()) { 2944 pw.println("Begin dump of ANQP Cache"); 2945 mAnqpCache.dump(pw); 2946 pw.println("End dump of ANQP Cache"); 2947 } 2948 } 2949 2950 public String getConfigFile() { 2951 return IP_CONFIG_FILE; 2952 } 2953 2954 protected void logd(String s) { 2955 Log.d(TAG, s); 2956 } 2957 2958 protected void loge(String s) { 2959 loge(s, false); 2960 } 2961 2962 protected void loge(String s, boolean stack) { 2963 if (stack) { 2964 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2965 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 2966 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 2967 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 2968 } else { 2969 Log.e(TAG, s); 2970 } 2971 } 2972 2973 private void logKernelTime() { 2974 long kernelTimeMs = System.nanoTime() / (1000 * 1000); 2975 StringBuilder builder = new StringBuilder(); 2976 builder.append("kernel time = ") 2977 .append(kernelTimeMs / 1000) 2978 .append(".") 2979 .append(kernelTimeMs % 1000) 2980 .append("\n"); 2981 localLog(builder.toString()); 2982 } 2983 2984 protected void log(String s) { 2985 Log.d(TAG, s); 2986 } 2987 2988 private void localLog(String s) { 2989 if (mLocalLog != null) { 2990 mLocalLog.log(s); 2991 } 2992 } 2993 2994 private void localLogAndLogcat(String s) { 2995 localLog(s); 2996 Log.d(TAG, s); 2997 } 2998 2999 private void localLogNetwork(String s, int netId) { 3000 if (mLocalLog == null) { 3001 return; 3002 } 3003 3004 WifiConfiguration config; 3005 synchronized (mConfiguredNetworks) { // !!! Useless synchronization 3006 config = mConfiguredNetworks.getForAllUsers(netId); 3007 } 3008 3009 if (config != null) { 3010 mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId 3011 + " status=" + config.status 3012 + " key=" + config.configKey()); 3013 } else { 3014 mLocalLog.log(s + " " + netId); 3015 } 3016 } 3017 3018 static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 3019 String client = config.getClientCertificateAlias(); 3020 if (!TextUtils.isEmpty(client)) { 3021 // a valid client certificate is configured 3022 3023 // BUGBUG: keyStore.get() never returns certBytes; because it is not 3024 // taking WIFI_UID as a parameter. It always looks for certificate 3025 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 3026 // all certificates need software keystore until we get the get() API 3027 // fixed. 3028 3029 return true; 3030 } 3031 3032 /* 3033 try { 3034 3035 if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials 3036 .USER_CERTIFICATE + client); 3037 3038 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 3039 if (factory == null) { 3040 Slog.e(TAG, "Error getting certificate factory"); 3041 return; 3042 } 3043 3044 byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); 3045 if (certBytes != null) { 3046 Certificate cert = (X509Certificate) factory.generateCertificate( 3047 new ByteArrayInputStream(certBytes)); 3048 3049 if (cert != null) { 3050 mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); 3051 3052 if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials 3053 .USER_CERTIFICATE + client); 3054 if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : 3055 "does not need" ) + " software key store"); 3056 } else { 3057 Slog.d(TAG, "could not generate certificate"); 3058 } 3059 } else { 3060 Slog.e(TAG, "Could not load client certificate " + Credentials 3061 .USER_CERTIFICATE + client); 3062 mNeedsSoftwareKeystore = true; 3063 } 3064 3065 } catch(CertificateException e) { 3066 Slog.e(TAG, "Could not read certificates"); 3067 mCaCert = null; 3068 mClientCertificate = null; 3069 } 3070 */ 3071 3072 return false; 3073 } 3074 3075 /** 3076 * Checks if the network is a sim config. 3077 * @param config Config corresponding to the network. 3078 * @return true if it is a sim config, false otherwise. 3079 */ 3080 public boolean isSimConfig(WifiConfiguration config) { 3081 return mWifiConfigStore.isSimConfig(config); 3082 } 3083 3084 /** 3085 * Resets all sim networks from the network list. 3086 */ 3087 public void resetSimNetworks() { 3088 mWifiConfigStore.resetSimNetworks(mConfiguredNetworks.valuesForCurrentUser()); 3089 } 3090 3091 boolean isNetworkConfigured(WifiConfiguration config) { 3092 // Check if either we have a network Id or a WifiConfiguration 3093 // matching the one we are trying to add. 3094 3095 if (config.networkId != INVALID_NETWORK_ID) { 3096 return (mConfiguredNetworks.getForCurrentUser(config.networkId) != null); 3097 } 3098 3099 return (mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()) != null); 3100 } 3101 3102 /** 3103 * Checks if uid has access to modify the configuration corresponding to networkId. 3104 * 3105 * The conditions checked are, in descending priority order: 3106 * - Disallow modification if the the configuration is not visible to the uid. 3107 * - Allow modification if the uid represents the Device Owner app. 3108 * - Allow modification if both of the following are true: 3109 * - The uid represents the configuration's creator or an app holding OVERRIDE_CONFIG_WIFI. 3110 * - The modification is only for administrative annotation (e.g. when connecting) or the 3111 * configuration is not lockdown eligible (which currently means that it was not last 3112 * updated by the DO). 3113 * - Allow modification if configuration lockdown is explicitly disabled and the uid represents 3114 * an app holding OVERRIDE_CONFIG_WIFI. 3115 * - In all other cases, disallow modification. 3116 */ 3117 boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) { 3118 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(networkId); 3119 3120 if (config == null) { 3121 loge("canModifyNetwork: cannot find config networkId " + networkId); 3122 return false; 3123 } 3124 3125 final DevicePolicyManagerInternal dpmi = LocalServices.getService( 3126 DevicePolicyManagerInternal.class); 3127 3128 final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 3129 DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 3130 3131 if (isUidDeviceOwner) { 3132 return true; 3133 } 3134 3135 final boolean isCreator = (config.creatorUid == uid); 3136 3137 if (onlyAnnotate) { 3138 return isCreator || checkConfigOverridePermission(uid); 3139 } 3140 3141 // Check if device has DPM capability. If it has and dpmi is still null, then we 3142 // treat this case with suspicion and bail out. 3143 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) 3144 && dpmi == null) { 3145 return false; 3146 } 3147 3148 // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner. 3149 3150 final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy( 3151 config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 3152 if (!isConfigEligibleForLockdown) { 3153 return isCreator || checkConfigOverridePermission(uid); 3154 } 3155 3156 final ContentResolver resolver = mContext.getContentResolver(); 3157 final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, 3158 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; 3159 return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid); 3160 } 3161 3162 /** 3163 * Checks if uid has access to modify config. 3164 */ 3165 boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) { 3166 if (config == null) { 3167 loge("canModifyNetowrk recieved null configuration"); 3168 return false; 3169 } 3170 3171 // Resolve the correct network id. 3172 int netid; 3173 if (config.networkId != INVALID_NETWORK_ID) { 3174 netid = config.networkId; 3175 } else { 3176 WifiConfiguration test = 3177 mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()); 3178 if (test == null) { 3179 return false; 3180 } else { 3181 netid = test.networkId; 3182 } 3183 } 3184 3185 return canModifyNetwork(uid, netid, onlyAnnotate); 3186 } 3187 3188 boolean checkConfigOverridePermission(int uid) { 3189 try { 3190 return (mFacade.checkUidPermission( 3191 android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid) 3192 == PackageManager.PERMISSION_GRANTED); 3193 } catch (RemoteException e) { 3194 return false; 3195 } 3196 } 3197 3198 /** called when CS ask WiFistateMachine to disconnect the current network 3199 * because the score is bad. 3200 */ 3201 void handleBadNetworkDisconnectReport(int netId, WifiInfo info) { 3202 /* TODO verify the bad network is current */ 3203 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 3204 if (config != null) { 3205 if ((info.is24GHz() && info.getRssi() 3206 <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND) 3207 || (info.is5GHz() && info.getRssi() 3208 <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND)) { 3209 // We do not block due to bad RSSI since network selection should not select bad 3210 // RSSI candidate 3211 } else { 3212 // We got disabled but RSSI is good, so disable hard 3213 updateNetworkSelectionStatus(config, 3214 WifiConfiguration.NetworkSelectionStatus.DISABLED_BAD_LINK); 3215 } 3216 } 3217 // Record last time Connectivity Service switched us away from WiFi and onto Cell 3218 mLastUnwantedNetworkDisconnectTimestamp = System.currentTimeMillis(); 3219 } 3220 3221 int getMaxDhcpRetries() { 3222 return mFacade.getIntegerSetting(mContext, 3223 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 3224 DEFAULT_MAX_DHCP_RETRIES); 3225 } 3226 3227 void clearBssidBlacklist() { 3228 mWifiConfigStore.clearBssidBlacklist(); 3229 } 3230 3231 void blackListBssid(String bssid) { 3232 mWifiConfigStore.blackListBssid(bssid); 3233 } 3234 3235 public boolean isBssidBlacklisted(String bssid) { 3236 return mWifiConfigStore.isBssidBlacklisted(bssid); 3237 } 3238 3239 public boolean getEnableAutoJoinWhenAssociated() { 3240 return mEnableAutoJoinWhenAssociated.get(); 3241 } 3242 3243 public void setEnableAutoJoinWhenAssociated(boolean enabled) { 3244 mEnableAutoJoinWhenAssociated.set(enabled); 3245 } 3246 3247 public void setActiveScanDetail(ScanDetail activeScanDetail) { 3248 synchronized (mActiveScanDetailLock) { 3249 mActiveScanDetail = activeScanDetail; 3250 } 3251 } 3252} 3253