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