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