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