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