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