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