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