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