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