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