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