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