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