WifiConfigManager.java revision 2af03130d7f85823223b8591dc52858d851b301d
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.WifiSsid; 43import android.net.wifi.WpsInfo; 44import android.net.wifi.WpsResult; 45import android.os.Environment; 46import android.os.FileObserver; 47import android.os.Process; 48import android.os.RemoteException; 49import android.os.SystemClock; 50import android.os.UserHandle; 51import android.os.UserManager; 52import android.provider.Settings; 53import android.security.Credentials; 54import android.security.KeyChain; 55import android.security.KeyStore; 56import android.text.TextUtils; 57import android.util.ArraySet; 58import android.util.LocalLog; 59import android.util.Log; 60import android.util.SparseArray; 61 62import com.android.internal.R; 63import com.android.server.LocalServices; 64import com.android.server.net.DelayedDiskWrite; 65import com.android.server.net.IpConfigStore; 66import com.android.server.wifi.anqp.ANQPElement; 67import com.android.server.wifi.anqp.ANQPFactory; 68import com.android.server.wifi.anqp.Constants; 69import com.android.server.wifi.hotspot2.ANQPData; 70import com.android.server.wifi.hotspot2.AnqpCache; 71import com.android.server.wifi.hotspot2.IconEvent; 72import com.android.server.wifi.hotspot2.NetworkDetail; 73import com.android.server.wifi.hotspot2.PasspointMatch; 74import com.android.server.wifi.hotspot2.SupplicantBridge; 75import com.android.server.wifi.hotspot2.Utils; 76import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager; 77import com.android.server.wifi.hotspot2.pps.Credential; 78import com.android.server.wifi.hotspot2.pps.HomeSP; 79 80import org.xml.sax.SAXException; 81 82import java.io.BufferedReader; 83import java.io.DataOutputStream; 84import java.io.File; 85import java.io.FileDescriptor; 86import java.io.FileNotFoundException; 87import java.io.FileReader; 88import java.io.IOException; 89import java.io.PrintWriter; 90import java.nio.charset.StandardCharsets; 91import java.security.PrivateKey; 92import java.security.cert.Certificate; 93import java.security.cert.CertificateException; 94import java.security.cert.X509Certificate; 95import java.text.DateFormat; 96import java.util.ArrayList; 97import java.util.Arrays; 98import java.util.BitSet; 99import java.util.Calendar; 100import java.util.Collection; 101import java.util.Collections; 102import java.util.Comparator; 103import java.util.Date; 104import java.util.HashMap; 105import java.util.HashSet; 106import java.util.List; 107import java.util.Map; 108import java.util.Objects; 109import java.util.Set; 110import java.util.concurrent.atomic.AtomicBoolean; 111import java.util.concurrent.atomic.AtomicInteger; 112import java.util.regex.Pattern; 113import java.util.zip.CRC32; 114import java.util.zip.Checksum; 115 116 117/** 118 * This class provides the API to manage configured 119 * wifi networks. The API is not thread safe is being 120 * used only from WifiStateMachine. 121 * 122 * It deals with the following 123 * - Add/update/remove a WifiConfiguration 124 * The configuration contains two types of information. 125 * = IP and proxy configuration that is handled by WifiConfigManager and 126 * is saved to disk on any change. 127 * 128 * The format of configuration file is as follows: 129 * <version> 130 * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS> 131 * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS> 132 * .. 133 * 134 * (key, value) pairs for a given network are grouped together and can 135 * be in any order. A EOS at the end of a set of (key, value) pairs 136 * indicates that the next set of (key, value) pairs are for a new 137 * network. A network is identified by a unique ID_KEY. If there is no 138 * ID_KEY in the (key, value) pairs, the data is discarded. 139 * 140 * An invalid version on read would result in discarding the contents of 141 * the file. On the next write, the latest version is written to file. 142 * 143 * Any failures during read or write to the configuration file are ignored 144 * without reporting to the user since the likelihood of these errors are 145 * low and the impact on connectivity is low. 146 * 147 * = SSID & security details that is pushed to the supplicant. 148 * supplicant saves these details to the disk on calling 149 * saveConfigCommand(). 150 * 151 * We have two kinds of APIs exposed: 152 * > public API calls that provide fine grained control 153 * - enableNetwork, disableNetwork, addOrUpdateNetwork(), 154 * removeNetwork(). For these calls, the config is not persisted 155 * to the disk. (TODO: deprecate these calls in WifiManager) 156 * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork(). 157 * These calls persist the supplicant config to disk. 158 * 159 * - Maintain a list of configured networks for quick access 160 * 161 */ 162public class WifiConfigManager { 163 164 private Context mContext; 165 public static final String TAG = "WifiConfigManager"; 166 private static final boolean DBG = true; 167 private static boolean VDBG = false; 168 private static boolean VVDBG = false; 169 170 private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; 171 private static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp"; 172 private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf"; 173 174 /* configured networks with network id as the key */ 175 private final ConfigurationMap mConfiguredNetworks; 176 177 /* A network id is a unique identifier for a network configured in the 178 * supplicant. Network ids are generated when the supplicant reads 179 * the configuration file at start and can thus change for networks. 180 * We store the IP configuration for networks along with a unique id 181 * that is generated from SSID and security type of the network. A mapping 182 * from the generated unique id to network id of the network is needed to 183 * map supplicant config to IP configuration. */ 184 185 /* Stores a map of NetworkId to ScanCache */ 186 private HashMap<Integer, ScanDetailCache> mScanDetailCaches; 187 188 /** 189 * Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user, 190 * so as, framework knows not to re-add those SSIDs automatically to the Saved networks 191 */ 192 private Set<Long> mDeletedSSIDs = new HashSet<Long>(); 193 194 /** 195 * Framework keeps a list of ephemeral SSIDs that where deleted by user, 196 * so as, framework knows not to autojoin again those SSIDs based on scorer input. 197 * The list is never cleared up. 198 * 199 * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field. 200 */ 201 public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>(); 202 203 /* Tracks the highest priority of configured networks */ 204 private int mLastPriority = -1; 205 206 private static final String ipConfigFile = Environment.getDataDirectory() + 207 "/misc/wifi/ipconfig.txt"; 208 209 // This is the only variable whose contents will not be interpreted by wpa_supplicant. We use it 210 // to store metadata that allows us to correlate a wpa_supplicant.conf entry with additional 211 // information about the same network stored in other files. The metadata is stored as a 212 // serialized JSON dictionary. 213 public static final String ID_STRING_VAR_NAME = "id_str"; 214 public static final String ID_STRING_KEY_FQDN = "fqdn"; 215 public static final String ID_STRING_KEY_CREATOR_UID = "creatorUid"; 216 public static final String ID_STRING_KEY_CONFIG_KEY = "configKey"; 217 218 // The Wifi verbose log is provided as a way to persist the verbose logging settings 219 // for testing purpose. 220 // It is not intended for normal use. 221 private static final String WIFI_VERBOSE_LOGS_KEY 222 = "WIFI_VERBOSE_LOGS"; 223 224 // As we keep deleted PSK WifiConfiguration for a while, the PSK of 225 // those deleted WifiConfiguration is set to this random unused PSK 226 private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted"; 227 228 /** 229 * The threshold for each kind of error. If a network continuously encounter the same error more 230 * than the threshold times, this network will be disabled. -1 means unavailable. 231 */ 232 private static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = { 233 -1, // threshold for NETWORK_SELECTION_ENABLE 234 1, // threshold for DISABLED_BAD_LINK 235 5, // threshold for DISABLED_ASSOCIATION_REJECTION 236 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 237 5, // threshold for DISABLED_DHCP_FAILURE 238 5, // threshold for DISABLED_DNS_FAILURE 239 6, // threshold for DISABLED_TLS_VERSION_MISMATCH 240 1, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 241 1, // threshold for DISABLED_NO_INTERNET 242 1 // threshold for DISABLED_BY_WIFI_MANAGER 243 }; 244 245 /** 246 * Timeout for each kind of error. After the timeout minutes, unblock the network again. 247 */ 248 private static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT = { 249 Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE 250 15, // threshold for DISABLED_BAD_LINK 251 5, // threshold for DISABLED_ASSOCIATION_REJECTION 252 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 253 5, // threshold for DISABLED_DHCP_FAILURE 254 5, // threshold for DISABLED_DNS_FAILURE 255 Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION 256 Integer.MAX_VALUE, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 257 Integer.MAX_VALUE, // threshold for DISABLED_NO_INTERNET 258 Integer.MAX_VALUE // threshold for DISABLED_BY_WIFI_MANAGER 259 }; 260 261 public int maxTxPacketForFullScans = 8; 262 public int maxRxPacketForFullScans = 16; 263 264 public int maxTxPacketForPartialScans = 40; 265 public int maxRxPacketForPartialScans = 80; 266 267 public int associatedFullScanMaxIntervalMilli = 300000; 268 269 // Sane value for roam blacklisting (not switching to a network if already associated) 270 // 2 days 271 public int networkSwitchingBlackListPeriodMilli = 2 * 24 * 60 * 60 * 1000; 272 273 public int badLinkSpeed24 = 6; 274 public int badLinkSpeed5 = 12; 275 public int goodLinkSpeed24 = 24; 276 public int goodLinkSpeed5 = 36; 277 278 public int maxAuthErrorsToBlacklist = 4; 279 public int maxConnectionErrorsToBlacklist = 4; 280 public int wifiConfigBlacklistMinTimeMilli = 1000 * 60 * 5; 281 282 // How long a disconnected config remain considered as the last user selection 283 public int wifiConfigLastSelectionHysteresis = 1000 * 60 * 3; 284 285 // Boost RSSI values of associated networks 286 public int associatedHysteresisHigh = +14; 287 public int associatedHysteresisLow = +8; 288 289 boolean showNetworks = true; // TODO set this back to false, used for debugging 17516271 290 291 public boolean roamOnAny = false; 292 public boolean onlyLinkSameCredentialConfigurations = true; 293 294 public boolean enableLinkDebouncing = true; 295 public boolean enable5GHzPreference = true; 296 public boolean enableWifiCellularHandoverUserTriggeredAdjustment = true; 297 298 public static final int maxNumScanCacheEntries = 128; 299 300 public final AtomicBoolean enableHalBasedPno = new AtomicBoolean(false); 301 public final AtomicBoolean enableSsidWhitelist = new AtomicBoolean(false); 302 public final AtomicBoolean enableAutoJoinWhenAssociated = new AtomicBoolean(true); 303 public final AtomicBoolean enableFullBandScanWhenAssociated = new AtomicBoolean(true); 304 public final AtomicBoolean enableChipWakeUpWhenAssociated = new AtomicBoolean(true); 305 public final AtomicBoolean enableRssiPollWhenAssociated = new AtomicBoolean(true); 306 public AtomicInteger thresholdSaturatedRssi5 = new AtomicInteger( 307 WifiQualifiedNetworkSelector.RSSI_SATURATION_5G_BAND); 308 public AtomicInteger thresholdQualifiedRssi5 = new AtomicInteger( 309 WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND); 310 public AtomicInteger thresholdMinimumRssi5 = new AtomicInteger( 311 WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI); 312 public AtomicInteger thresholdSaturatedRssi24 = new AtomicInteger( 313 WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); 314 public AtomicInteger thresholdQualifiedRssi24 = new AtomicInteger( 315 WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND); 316 public AtomicInteger thresholdMinimumRssi24 = new AtomicInteger( 317 WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI); 318 public final AtomicInteger maxTxPacketForNetworkSwitching = new AtomicInteger(40); 319 public final AtomicInteger maxRxPacketForNetworkSwitching = new AtomicInteger(80); 320 public final AtomicInteger enableVerboseLogging = new AtomicInteger(0); 321 public final AtomicInteger associatedFullScanBackoff = 322 new AtomicInteger(12); // Will be divided by 8 by WifiStateMachine 323 public final AtomicInteger alwaysEnableScansWhileAssociated = new AtomicInteger(0); 324 public final AtomicInteger maxNumPassiveChannelsForPartialScans = new AtomicInteger(2); 325 public final AtomicInteger maxNumActiveChannelsForPartialScans = new AtomicInteger(6); 326 public final AtomicInteger wifiDisconnectedShortScanIntervalMilli = new AtomicInteger(15000); 327 public final AtomicInteger wifiDisconnectedLongScanIntervalMilli = new AtomicInteger(120000); 328 public final AtomicInteger wifiAssociatedShortScanIntervalMilli = new AtomicInteger(20000); 329 public final AtomicInteger wifiAssociatedLongScanIntervalMilli = new AtomicInteger(180000); 330 public AtomicInteger currentNetworkBoost = new AtomicInteger( 331 WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD); 332 public AtomicInteger bandAward5Ghz = new AtomicInteger( 333 WifiQualifiedNetworkSelector.BAND_AWARD_5GHz); 334 335 /** 336 * Regex pattern for extracting a connect choice. 337 * Matches a strings like the following: 338 * <configKey>=([0:9]+) 339 */ 340 private static Pattern mConnectChoice = 341 Pattern.compile("(.*)=([0-9]+)"); 342 343 344 /* Enterprise configuration keys */ 345 /** 346 * In old configurations, the "private_key" field was used. However, newer 347 * configurations use the key_id field with the engine_id set to "keystore". 348 * If this field is found in the configuration, the migration code is 349 * triggered. 350 */ 351 public static final String OLD_PRIVATE_KEY_NAME = "private_key"; 352 353 /** 354 * This represents an empty value of an enterprise field. 355 * NULL is used at wpa_supplicant to indicate an empty value 356 */ 357 static final String EMPTY_VALUE = "NULL"; 358 359 /** 360 * If Connectivity Service has triggered an unwanted network disconnect 361 */ 362 public long lastUnwantedNetworkDisconnectTimestamp = 0; 363 364 /** 365 * The maximum number of times we will retry a connection to an access point 366 * for which we have failed in acquiring an IP address from DHCP. A value of 367 * N means that we will make N+1 connection attempts in all. 368 * <p> 369 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default 370 * value if a Settings value is not present. 371 */ 372 private static final int DEFAULT_MAX_DHCP_RETRIES = 9; 373 374 375 private final LocalLog mLocalLog; 376 private final WpaConfigFileObserver mFileObserver; 377 378 private WifiNative mWifiNative; 379 private final KeyStore mKeyStore = KeyStore.getInstance(); 380 381 private IpConfigStore mIpconfigStore; 382 private DelayedDiskWrite mWriter; 383 384 private final WifiNetworkHistory mWifiNetworkHistory; 385 /** 386 * The lastSelectedConfiguration is used to remember which network 387 * was selected last by the user. 388 * The connection to this network may not be successful, as well 389 * the selection (i.e. network priority) might not be persisted. 390 * WiFi state machine is the only object that sets this variable. 391 */ 392 private String lastSelectedConfiguration = null; 393 private long mLastSelectedTimeStamp = 394 WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; 395 396 /* 397 * BSSID blacklist, i.e. list of BSSID we want to avoid 398 */ 399 HashSet<String> mBssidBlacklist = new HashSet<String>(); 400 401 /* 402 * Lost config list, whenever we read a config from networkHistory.txt that was not in 403 * wpa_supplicant.conf 404 */ 405 HashSet<String> mLostConfigsDbg = new HashSet<String>(); 406 407 private final AnqpCache mAnqpCache; 408 private final SupplicantBridge mSupplicantBridge; 409 private final SupplicantBridgeCallbacks mSupplicantBridgeCallbacks; 410 411 private final PasspointManagementObjectManager mMOManager; 412 private final boolean mEnableOsuQueries; 413 private final SIMAccessor mSIMAccessor; 414 415 private WifiStateMachine mWifiStateMachine; 416 private FrameworkFacade mFacade; 417 private Clock mClock; 418 419 private class SupplicantBridgeCallbacks implements SupplicantBridge.SupplicantBridgeCallbacks { 420 @Override 421 public void notifyANQPResponse(ScanDetail scanDetail, 422 Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 423 updateAnqpCache(scanDetail, anqpElements); 424 if (anqpElements == null || anqpElements.isEmpty()) { 425 return; 426 } 427 scanDetail.propagateANQPInfo(anqpElements); 428 429 Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false); 430 Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() + " pass 2 matches: " 431 + toMatchString(matches)); 432 433 cacheScanResultForPasspointConfigs(scanDetail, matches, null); 434 } 435 @Override 436 public void notifyIconFailed(long bssid) { 437 Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION); 438 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 439 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, bssid); 440 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 441 } 442 443 } 444 445 private class SupplicantSaver implements WifiEnterpriseConfig.SupplicantSaver { 446 private final int mNetId; 447 private final String mSetterSSID; 448 449 SupplicantSaver(int netId, String setterSSID) { 450 mNetId = netId; 451 mSetterSSID = setterSSID; 452 } 453 454 @Override 455 public boolean saveValue(String key, String value) { 456 if (key.equals(WifiEnterpriseConfig.PASSWORD_KEY) 457 && value != null && value.equals("*")) { 458 // No need to try to set an obfuscated password, which will fail 459 return true; 460 } 461 if (key.equals(WifiEnterpriseConfig.REALM_KEY) 462 || key.equals(WifiEnterpriseConfig.PLMN_KEY)) { 463 // No need to save realm or PLMN in supplicant 464 return true; 465 } 466 // TODO: We need a way to clear values in wpa_supplicant as opposed to 467 // mapping unset values to empty strings. 468 if (value == null) { 469 value = "\"\""; 470 } 471 if (!mWifiNative.setNetworkVariable(mNetId, key, value)) { 472 loge(mSetterSSID + ": failed to set " + key + ": " + value); 473 return false; 474 } 475 return true; 476 } 477 } 478 479 private class SupplicantLoader implements WifiEnterpriseConfig.SupplicantLoader { 480 private final int mNetId; 481 482 SupplicantLoader(int netId) { 483 mNetId = netId; 484 } 485 486 @Override 487 public String loadValue(String key) { 488 String value = mWifiNative.getNetworkVariable(mNetId, key); 489 if (!TextUtils.isEmpty(value)) { 490 if (!enterpriseConfigKeyShouldBeQuoted(key)) { 491 value = removeDoubleQuotes(value); 492 } 493 return value; 494 } else { 495 return null; 496 } 497 } 498 } 499 500 WifiConfigManager(Context c, WifiStateMachine w, WifiNative wn, FrameworkFacade f, 501 Clock clock, UserManager userManager) { 502 mContext = c; 503 mFacade = f; 504 mClock = clock; 505 mWifiNative = wn; 506 mWifiStateMachine = w; 507 508 if (showNetworks) { 509 mLocalLog = mWifiNative.getLocalLog(); 510 mFileObserver = new WpaConfigFileObserver(); 511 mFileObserver.startWatching(); 512 } else { 513 mLocalLog = null; 514 mFileObserver = null; 515 } 516 517 wifiAssociatedShortScanIntervalMilli.set(mContext.getResources().getInteger( 518 R.integer.config_wifi_associated_short_scan_interval)); 519 wifiAssociatedLongScanIntervalMilli.set(mContext.getResources().getInteger( 520 R.integer.config_wifi_associated_short_scan_interval)); 521 wifiDisconnectedShortScanIntervalMilli.set(mContext.getResources().getInteger( 522 R.integer.config_wifi_disconnected_short_scan_interval)); 523 wifiDisconnectedLongScanIntervalMilli.set(mContext.getResources().getInteger( 524 R.integer.config_wifi_disconnected_long_scan_interval)); 525 526 onlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean( 527 R.bool.config_wifi_only_link_same_credential_configurations); 528 maxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger( 529 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels)); 530 maxNumPassiveChannelsForPartialScans.set(mContext.getResources().getInteger( 531 R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels)); 532 associatedFullScanMaxIntervalMilli = mContext.getResources().getInteger( 533 R.integer.config_wifi_framework_associated_full_scan_max_interval); 534 associatedFullScanBackoff.set(mContext.getResources().getInteger( 535 R.integer.config_wifi_framework_associated_full_scan_backoff)); 536 enableLinkDebouncing = mContext.getResources().getBoolean( 537 R.bool.config_wifi_enable_disconnection_debounce); 538 539 enable5GHzPreference = mContext.getResources().getBoolean( 540 R.bool.config_wifi_enable_5GHz_preference); 541 542 bandAward5Ghz.set(mContext.getResources().getInteger( 543 R.integer.config_wifi_framework_5GHz_preference_boost_factor)); 544 545 associatedHysteresisHigh = mContext.getResources().getInteger( 546 R.integer.config_wifi_framework_current_association_hysteresis_high); 547 associatedHysteresisLow = mContext.getResources().getInteger( 548 R.integer.config_wifi_framework_current_association_hysteresis_low); 549 550 thresholdMinimumRssi5.set(mContext.getResources().getInteger( 551 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)); 552 thresholdQualifiedRssi5.set(mContext.getResources().getInteger( 553 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)); 554 thresholdSaturatedRssi5.set(mContext.getResources().getInteger( 555 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz)); 556 thresholdMinimumRssi24.set(mContext.getResources().getInteger( 557 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)); 558 thresholdQualifiedRssi24.set(mContext.getResources().getInteger( 559 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)); 560 thresholdSaturatedRssi24.set(mContext.getResources().getInteger( 561 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)); 562 563 enableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean( 564 R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment); 565 566 badLinkSpeed24 = mContext.getResources().getInteger( 567 R.integer.config_wifi_framework_wifi_score_bad_link_speed_24); 568 badLinkSpeed5 = mContext.getResources().getInteger( 569 R.integer.config_wifi_framework_wifi_score_bad_link_speed_5); 570 goodLinkSpeed24 = mContext.getResources().getInteger( 571 R.integer.config_wifi_framework_wifi_score_good_link_speed_24); 572 goodLinkSpeed5 = mContext.getResources().getInteger( 573 R.integer.config_wifi_framework_wifi_score_good_link_speed_5); 574 575 maxAuthErrorsToBlacklist = mContext.getResources().getInteger( 576 R.integer.config_wifi_framework_max_auth_errors_to_blacklist); 577 maxConnectionErrorsToBlacklist = mContext.getResources().getInteger( 578 R.integer.config_wifi_framework_max_connection_errors_to_blacklist); 579 wifiConfigBlacklistMinTimeMilli = mContext.getResources().getInteger( 580 R.integer.config_wifi_framework_network_black_list_min_time_milli); 581 582 enableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean( 583 R.bool.config_wifi_framework_enable_associated_network_selection)); 584 585 currentNetworkBoost.set(mContext.getResources().getInteger( 586 R.integer.config_wifi_framework_current_network_boost)); 587 networkSwitchingBlackListPeriodMilli = mContext.getResources().getInteger( 588 R.integer.config_wifi_network_switching_blacklist_time); 589 590 enableHalBasedPno.set(mContext.getResources().getBoolean( 591 R.bool.config_wifi_hal_pno_enable)); 592 593 enableSsidWhitelist.set(mContext.getResources().getBoolean( 594 R.bool.config_wifi_ssid_white_list_enable)); 595 if (!enableHalBasedPno.get() && enableSsidWhitelist.get()) { 596 enableSsidWhitelist.set(false); 597 } 598 599 boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled); 600 Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled")); 601 602 mConfiguredNetworks = new ConfigurationMap(userManager); 603 mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on); 604 mEnableOsuQueries = true; 605 mAnqpCache = new AnqpCache(mClock); 606 mSupplicantBridgeCallbacks = new SupplicantBridgeCallbacks(); 607 mSupplicantBridge = new SupplicantBridge(mWifiNative, mSupplicantBridgeCallbacks); 608 mScanDetailCaches = new HashMap<>(); 609 610 mSIMAccessor = new SIMAccessor(mContext); 611 mWriter = new DelayedDiskWrite(); 612 mIpconfigStore = new IpConfigStore(mWriter); 613 614 mWifiNetworkHistory = new WifiNetworkHistory(c, mLocalLog, mWriter); 615 } 616 617 public void trimANQPCache(boolean all) { 618 mAnqpCache.clear(all, DBG); 619 } 620 621 void enableVerboseLogging(int verbose) { 622 enableVerboseLogging.set(verbose); 623 if (verbose > 0) { 624 VDBG = true; 625 showNetworks = true; 626 } else { 627 VDBG = false; 628 } 629 if (verbose > 1) { 630 VVDBG = true; 631 } else { 632 VVDBG = false; 633 } 634 } 635 636 class WpaConfigFileObserver extends FileObserver { 637 638 public WpaConfigFileObserver() { 639 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 640 } 641 642 @Override 643 public void onEvent(int event, String path) { 644 if (event == CLOSE_WRITE) { 645 File file = new File(SUPPLICANT_CONFIG_FILE); 646 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 647 } 648 } 649 } 650 651 652 /** 653 * Fetch the list of configured networks 654 * and enable all stored networks in supplicant. 655 */ 656 void loadAndEnableAllNetworks() { 657 if (DBG) log("Loading config and enabling all networks "); 658 loadConfiguredNetworks(); 659 enableAllNetworks(); 660 } 661 662 int getConfiguredNetworksSize() { 663 return mConfiguredNetworks.sizeForCurrentUser(); 664 } 665 666 private List<WifiConfiguration> 667 getConfiguredNetworks(Map<String, String> pskMap) { 668 List<WifiConfiguration> networks = new ArrayList<>(); 669 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 670 WifiConfiguration newConfig = new WifiConfiguration(config); 671 // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to 672 // correctly handle updating existing configs that are filtered out here. 673 if (config.ephemeral) { 674 // Do not enumerate and return this configuration to any one, 675 // for instance WiFi Picker. 676 // instead treat it as unknown. the configuration can still be retrieved 677 // directly by the key or networkId 678 continue; 679 } 680 681 if (pskMap != null && config.allowedKeyManagement != null 682 && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) 683 && pskMap.containsKey(config.SSID)) { 684 newConfig.preSharedKey = pskMap.get(config.SSID); 685 } 686 networks.add(newConfig); 687 } 688 return networks; 689 } 690 691 /** 692 * This function returns all configuration, and is used for cebug and creating bug reports. 693 */ 694 private List<WifiConfiguration> 695 getAllConfiguredNetworks() { 696 List<WifiConfiguration> networks = new ArrayList<>(); 697 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 698 WifiConfiguration newConfig = new WifiConfiguration(config); 699 networks.add(newConfig); 700 } 701 return networks; 702 } 703 704 /** 705 * Fetch the list of currently configured networks 706 * @return List of networks 707 */ 708 public List<WifiConfiguration> getConfiguredNetworks() { 709 return getConfiguredNetworks(null); 710 } 711 712 /** 713 * Fetch the list of currently configured networks, filled with real preSharedKeys 714 * @return List of networks 715 */ 716 List<WifiConfiguration> getPrivilegedConfiguredNetworks() { 717 Map<String, String> pskMap = getCredentialsBySsidMap(); 718 List<WifiConfiguration> configurations = getConfiguredNetworks(pskMap); 719 for (WifiConfiguration configuration : configurations) { 720 try { 721 configuration 722 .setPasspointManagementObjectTree(mMOManager.getMOTree(configuration.FQDN)); 723 } catch (IOException ioe) { 724 Log.w(TAG, "Failed to parse MO from " + configuration.FQDN + ": " + ioe); 725 } 726 } 727 return configurations; 728 } 729 730 /** 731 * Find matching network for this scanResult 732 */ 733 WifiConfiguration getMatchingConfig(ScanResult scanResult) { 734 735 for (Map.Entry entry : mScanDetailCaches.entrySet()) { 736 Integer netId = (Integer) entry.getKey(); 737 ScanDetailCache cache = (ScanDetailCache) entry.getValue(); 738 WifiConfiguration config = getWifiConfiguration(netId); 739 if (config == null) 740 continue; 741 if (cache.get(scanResult.BSSID) != null) { 742 return config; 743 } 744 } 745 746 return null; 747 } 748 749 /** 750 * Fetch the preSharedKeys for all networks. 751 * @return a map from Ssid to preSharedKey. 752 */ 753 private Map<String, String> getCredentialsBySsidMap() { 754 return readNetworkVariablesFromSupplicantFile("psk"); 755 } 756 757 /** 758 * Fetch the list of currently configured networks that were recently seen 759 * 760 * @return List of networks 761 */ 762 List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) { 763 List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 764 765 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 766 if (config.ephemeral) { 767 // Do not enumerate and return this configuration to any one, 768 // instead treat it as unknown. the configuration can still be retrieved 769 // directly by the key or networkId 770 continue; 771 } 772 773 // Calculate the RSSI for scan results that are more recent than milli 774 ScanDetailCache cache = getScanDetailCache(config); 775 if (cache == null) { 776 continue; 777 } 778 config.setVisibility(cache.getVisibility(milli)); 779 if (config.visibility == null) { 780 continue; 781 } 782 if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI && 783 config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) { 784 continue; 785 } 786 if (copy) { 787 networks.add(new WifiConfiguration(config)); 788 } else { 789 networks.add(config); 790 } 791 } 792 return networks; 793 } 794 795 /** 796 * Update the configuration and BSSID with latest RSSI value. 797 */ 798 void updateConfiguration(WifiInfo info) { 799 WifiConfiguration config = getWifiConfiguration(info.getNetworkId()); 800 if (config != null && getScanDetailCache(config) != null) { 801 ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID()); 802 if (scanDetail != null) { 803 ScanResult result = scanDetail.getScanResult(); 804 long previousSeen = result.seen; 805 int previousRssi = result.level; 806 807 // Update the scan result 808 scanDetail.setSeen(); 809 result.level = info.getRssi(); 810 811 // Average the RSSI value 812 result.averageRssi(previousRssi, previousSeen, 813 WifiQualifiedNetworkSelector.SCAN_RESULT_MAXIMUNM_AGE); 814 if (VDBG) { 815 loge("updateConfiguration freq=" + result.frequency 816 + " BSSID=" + result.BSSID 817 + " RSSI=" + result.level 818 + " " + config.configKey()); 819 } 820 } 821 } 822 } 823 824 /** 825 * get the Wificonfiguration for this netId 826 * 827 * @return Wificonfiguration 828 */ 829 public WifiConfiguration getWifiConfiguration(int netId) { 830 return mConfiguredNetworks.getForCurrentUser(netId); 831 } 832 833 /** 834 * Get the Wificonfiguration for this key 835 * @return Wificonfiguration 836 */ 837 public WifiConfiguration getWifiConfiguration(String key) { 838 return mConfiguredNetworks.getByConfigKeyForCurrentUser(key); 839 } 840 841 /** 842 * Enable all networks (if disabled time expire) and save config. This will be a no-op if the 843 * list of configured networks indicates all networks as being enabled 844 */ 845 void enableAllNetworks() { 846 boolean networkEnabledStateChanged = false; 847 848 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 849 if (config != null && !config.ephemeral 850 && !config.getNetworkSelectionStatus().isNetworkEnabled()) { 851 if (tryEnableQualifiedNetwork(config)) { 852 networkEnabledStateChanged = true; 853 } 854 } 855 } 856 857 if (networkEnabledStateChanged) { 858 mWifiNative.saveConfig(); 859 sendConfiguredNetworksChangedBroadcast(); 860 } 861 } 862 863 /** 864 * Enable a network in wpa_supplicant. 865 */ 866 boolean enableNetworkNative(WifiConfiguration config) { 867 if (VDBG) localLog("enableNetworkNative: " + config); 868 if (!mWifiNative.enableNetwork(config.networkId)) { 869 loge("Enable network in wpa_supplicant failed on " + config.networkId); 870 return false; 871 } 872 config.status = Status.ENABLED; 873 return true; 874 } 875 876 /** 877 * Enable all networks in wpa_supplicant. 878 */ 879 void enableAllNetworksNative() { 880 if (VDBG) localLog("enableAllNetworksNative"); 881 boolean networkEnabledStateChanged = false; 882 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 883 if (config != null && !config.ephemeral 884 && !config.getNetworkSelectionStatus().isNetworkEnabled()) { 885 if (enableNetworkNative(config)) { 886 networkEnabledStateChanged = true; 887 } 888 } 889 } 890 if (networkEnabledStateChanged) { 891 mWifiNative.saveConfig(); 892 sendConfiguredNetworksChangedBroadcast(); 893 } 894 } 895 896 private boolean setNetworkPriorityNative(int netId, int priority) { 897 return mWifiNative.setNetworkVariable(netId, 898 WifiConfiguration.priorityVarName, Integer.toString(priority)); 899 } 900 901 private boolean setSSIDNative(int netId, String ssid) { 902 return mWifiNative.setNetworkVariable(netId, WifiConfiguration.ssidVarName, 903 encodeSSID(ssid)); 904 } 905 906 public boolean updateLastConnectUid(WifiConfiguration config, int uid) { 907 if (config != null) { 908 if (config.lastConnectUid != uid) { 909 config.lastConnectUid = uid; 910 return true; 911 } 912 } 913 return false; 914 } 915 916 /** 917 * Selects the specified network for connection. This involves 918 * updating the priority of all the networks and enabling the given 919 * network while disabling others. 920 * 921 * Selecting a network will leave the other networks disabled and 922 * a call to enableAllNetworks() needs to be issued upon a connection 923 * or a failure event from supplicant 924 * 925 * @param config network to select for connection 926 * @param updatePriorities makes config highest priority network 927 * @return false if the network id is invalid 928 */ 929 boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) { 930 if (VDBG) localLogNetwork("selectNetwork", config.networkId); 931 if (config.networkId == INVALID_NETWORK_ID) return false; 932 if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, 933 mWifiStateMachine.getCurrentUserProfiles())) { 934 loge("selectNetwork " + Integer.toString(config.networkId) + ": Network config is not " 935 + "visible to current user."); 936 return false; 937 } 938 939 // Reset the priority of each network at start or if it goes too high. 940 if (mLastPriority == -1 || mLastPriority > 1000000) { 941 for (WifiConfiguration config2 : mConfiguredNetworks.valuesForCurrentUser()) { 942 if (updatePriorities) { 943 if (config2.networkId != INVALID_NETWORK_ID) { 944 config2.priority = 0; 945 setNetworkPriorityNative(config2.networkId, config.priority); 946 } 947 } 948 } 949 mLastPriority = 0; 950 } 951 952 // Set to the highest priority and save the configuration. 953 if (updatePriorities) { 954 config.priority = ++mLastPriority; 955 setNetworkPriorityNative(config.networkId, config.priority); 956 } 957 958 if (config.isPasspoint()) { 959 /* need to slap on the SSID of selected bssid to work */ 960 if (getScanDetailCache(config).size() != 0) { 961 ScanDetail result = getScanDetailCache(config).getFirst(); 962 if (result == null) { 963 loge("Could not find scan result for " + config.BSSID); 964 } else { 965 log("Setting SSID for " + config.networkId + " to" + result.getSSID()); 966 setSSIDNative(config.networkId, result.getSSID()); 967 config.SSID = result.getSSID(); 968 } 969 970 } else { 971 loge("Could not find bssid for " + config); 972 } 973 } 974 975 mWifiNative.setHs20(config.isPasspoint()); 976 977 if (updatePriorities) 978 mWifiNative.saveConfig(); 979 980 updateLastConnectUid(config, uid); 981 982 writeKnownNetworkHistory(); 983 984 985 /* Enable the given network while disabling all other networks */ 986 selectNetworkWithoutBroadcast(config.networkId); 987 988 /* Avoid saving the config & sending a broadcast to prevent settings 989 * from displaying a disabled list of networks */ 990 return true; 991 } 992 993 /** 994 * Add/update the specified configuration and save config 995 * 996 * @param config WifiConfiguration to be saved 997 * @return network update result 998 */ 999 NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) { 1000 WifiConfiguration conf; 1001 1002 // A new network cannot have null SSID 1003 if (config == null || (config.networkId == INVALID_NETWORK_ID && 1004 config.SSID == null)) { 1005 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1006 } 1007 1008 if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, 1009 mWifiStateMachine.getCurrentUserProfiles())) { 1010 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1011 } 1012 1013 if (VDBG) localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId); 1014 if (VDBG) { 1015 logd("WifiConfigManager saveNetwork," 1016 + " size=" + Integer.toString(mConfiguredNetworks.sizeForAllUsers()) 1017 + " (for all users)" 1018 + " SSID=" + config.SSID 1019 + " Uid=" + Integer.toString(config.creatorUid) 1020 + "/" + Integer.toString(config.lastUpdateUid)); 1021 } 1022 1023 if (mDeletedEphemeralSSIDs.remove(config.SSID)) { 1024 if (VDBG) { 1025 loge("WifiConfigManager: removed from ephemeral blacklist: " + config.SSID); 1026 } 1027 // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call 1028 // below, since we're creating/modifying a config. 1029 } 1030 1031 boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); 1032 NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid); 1033 int netId = result.getNetworkId(); 1034 1035 if (VDBG) localLogNetwork("WifiConfigManager: saveNetwork got it back netId=", netId); 1036 1037 conf = mConfiguredNetworks.getForCurrentUser(netId); 1038 if (conf != null) { 1039 if (!conf.getNetworkSelectionStatus().isNetworkEnabled()) { 1040 if (VDBG) localLog("WifiConfigManager: re-enabling: " + conf.SSID); 1041 1042 // reenable autojoin, since new information has been provided 1043 updateNetworkSelectionStatus(netId, 1044 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 1045 } 1046 if (VDBG) { 1047 loge("WifiConfigManager: saveNetwork got config back netId=" 1048 + Integer.toString(netId) 1049 + " uid=" + Integer.toString(config.creatorUid)); 1050 } 1051 } 1052 1053 mWifiNative.saveConfig(); 1054 sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ? 1055 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1056 return result; 1057 } 1058 1059 void noteRoamingFailure(WifiConfiguration config, int reason) { 1060 if (config == null) return; 1061 config.lastRoamingFailure = System.currentTimeMillis(); 1062 config.roamingFailureBlackListTimeMilli 1063 = 2 * (config.roamingFailureBlackListTimeMilli + 1000); 1064 if (config.roamingFailureBlackListTimeMilli 1065 > networkSwitchingBlackListPeriodMilli) { 1066 config.roamingFailureBlackListTimeMilli = 1067 networkSwitchingBlackListPeriodMilli; 1068 } 1069 config.lastRoamingFailureReason = reason; 1070 } 1071 1072 void saveWifiConfigBSSID(WifiConfiguration config) { 1073 // Sanity check the config is valid 1074 if (config == null || (config.networkId == INVALID_NETWORK_ID && 1075 config.SSID == null)) { 1076 return; 1077 } 1078 1079 // If Network Selection specified a BSSID then write it in the network block 1080 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1081 String bssid = networkStatus.getNetworkSelectionBSSID(); 1082 if (bssid != null) { 1083 localLog("saveWifiConfigBSSID Setting BSSID for " + config.configKey() 1084 + " to " + bssid); 1085 if (!mWifiNative.setNetworkVariable( 1086 config.networkId, 1087 WifiConfiguration.bssidVarName, 1088 bssid)) { 1089 loge("failed to set BSSID: " + bssid); 1090 } else if (bssid.equals("any")) { 1091 // Paranoia, we just want to make sure that we restore the config to normal 1092 mWifiNative.saveConfig(); 1093 } 1094 } 1095 } 1096 1097 1098 void updateStatus(int netId, DetailedState state) { 1099 if (netId != INVALID_NETWORK_ID) { 1100 WifiConfiguration config = mConfiguredNetworks.getForAllUsers(netId); 1101 if (config == null) return; 1102 switch (state) { 1103 case CONNECTED: 1104 config.status = Status.CURRENT; 1105 //we successfully connected, hence remove the blacklist 1106 updateNetworkSelectionStatus(netId, 1107 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 1108 break; 1109 case DISCONNECTED: 1110 //If network is already disabled, keep the status 1111 if (config.status == Status.CURRENT) { 1112 config.status = Status.ENABLED; 1113 } 1114 break; 1115 default: 1116 //do nothing, retain the existing state 1117 break; 1118 } 1119 } 1120 } 1121 1122 1123 /** 1124 * Disable an ephemeral SSID for the purpose of auto-joining thru scored. 1125 * This SSID will never be scored anymore. 1126 * The only way to "un-disable it" is if the user create a network for that SSID and then 1127 * forget it. 1128 * 1129 * @param SSID caller must ensure that the SSID passed thru this API match 1130 * the WifiConfiguration.SSID rules, and thus be surrounded by quotes. 1131 * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can 1132 * disconnect if this is the current network. 1133 */ 1134 WifiConfiguration disableEphemeralNetwork(String SSID) { 1135 if (SSID == null) { 1136 return null; 1137 } 1138 1139 WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeralForCurrentUser(SSID); 1140 1141 mDeletedEphemeralSSIDs.add(SSID); 1142 loge("Forget ephemeral SSID " + SSID + " num=" + mDeletedEphemeralSSIDs.size()); 1143 1144 if (foundConfig != null) { 1145 loge("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId); 1146 } 1147 1148 writeKnownNetworkHistory(); 1149 return foundConfig; 1150 } 1151 1152 /** 1153 * Forget the specified network and save config 1154 * 1155 * @param netId network to forget 1156 * @return {@code true} if it succeeds, {@code false} otherwise 1157 */ 1158 boolean forgetNetwork(int netId) { 1159 if (showNetworks) localLogNetwork("forgetNetwork", netId); 1160 1161 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1162 1163 boolean remove = removeConfigAndSendBroadcastIfNeeded(netId); 1164 if (!remove) { 1165 //success but we dont want to remove the network from supplicant conf file 1166 return true; 1167 } 1168 if (mWifiNative.removeNetwork(netId)) { 1169 if (config != null && config.isPasspoint()) { 1170 writePasspointConfigs(config.FQDN, null); 1171 } 1172 mWifiNative.saveConfig(); 1173 writeKnownNetworkHistory(); 1174 return true; 1175 } else { 1176 loge("Failed to remove network " + netId); 1177 return false; 1178 } 1179 } 1180 1181 /** 1182 * Add/update a network. Note that there is no saveConfig operation. 1183 * This function is retained for compatibility with the public 1184 * API. The more powerful saveNetwork() is used by the 1185 * state machine 1186 * 1187 * @param config wifi configuration to add/update 1188 * @return network Id 1189 */ 1190 int addOrUpdateNetwork(WifiConfiguration config, int uid) { 1191 if (config == null || !WifiConfigurationUtil.isVisibleToAnyProfile(config, 1192 mWifiStateMachine.getCurrentUserProfiles())) { 1193 return WifiConfiguration.INVALID_NETWORK_ID; 1194 } 1195 1196 if (showNetworks) localLogNetwork("addOrUpdateNetwork id=", config.networkId); 1197 if (config.isPasspoint()) { 1198 /* create a temporary SSID with providerFriendlyName */ 1199 Long csum = getChecksum(config.FQDN); 1200 config.SSID = csum.toString(); 1201 config.enterpriseConfig.setDomainSuffixMatch(config.FQDN); 1202 } 1203 1204 NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid); 1205 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 1206 WifiConfiguration conf = mConfiguredNetworks.getForCurrentUser(result.getNetworkId()); 1207 if (conf != null) { 1208 sendConfiguredNetworksChangedBroadcast(conf, 1209 result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED : 1210 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1211 } 1212 } 1213 1214 return result.getNetworkId(); 1215 } 1216 1217 public int addPasspointManagementObject(String managementObject) { 1218 try { 1219 mMOManager.addSP(managementObject); 1220 return 0; 1221 } catch (IOException | SAXException e) { 1222 return -1; 1223 } 1224 } 1225 1226 public int modifyPasspointMo(String fqdn, List<PasspointManagementObjectDefinition> mos) { 1227 try { 1228 return mMOManager.modifySP(fqdn, mos); 1229 } catch (IOException | SAXException e) { 1230 return -1; 1231 } 1232 } 1233 1234 public boolean queryPasspointIcon(long bssid, String fileName) { 1235 return mSupplicantBridge.doIconQuery(bssid, fileName); 1236 } 1237 1238 public int matchProviderWithCurrentNetwork(String fqdn) { 1239 ScanDetail scanDetail = mWifiStateMachine.getActiveScanDetail(); 1240 if (scanDetail == null) { 1241 return PasspointMatch.None.ordinal(); 1242 } 1243 HomeSP homeSP = mMOManager.getHomeSP(fqdn); 1244 if (homeSP == null) { 1245 return PasspointMatch.None.ordinal(); 1246 } 1247 1248 ANQPData anqpData = mAnqpCache.getEntry(scanDetail.getNetworkDetail()); 1249 1250 Map<Constants.ANQPElementType, ANQPElement> anqpElements = 1251 anqpData != null ? anqpData.getANQPElements() : null; 1252 1253 return homeSP.match(scanDetail.getNetworkDetail(), anqpElements, mSIMAccessor).ordinal(); 1254 } 1255 1256 /** 1257 * General PnoNetwork list sorting algorithm: 1258 * 1, Place the fully enabled networks first. Among the fully enabled networks, 1259 * sort them in the oder determined by the return of |compareConfigurations| method 1260 * implementation. 1261 * 2. Next place all the temporarily disabled networks. Among the temporarily disabled 1262 * networks, sort them in the order determined by the return of |compareConfigurations| method 1263 * implementation. 1264 * 3. Place the permanently disabled networks last. The order among permanently disabled 1265 * networks doesn't matter. 1266 */ 1267 private static class PnoListComparator implements Comparator<WifiConfiguration> { 1268 1269 public final int ENABLED_NETWORK_SCORE = 3; 1270 public final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; 1271 public final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; 1272 1273 @Override 1274 public int compare(WifiConfiguration a, WifiConfiguration b) { 1275 int configAScore = getPnoNetworkSortScore(a); 1276 int configBScore = getPnoNetworkSortScore(b); 1277 if (configAScore == configBScore) { 1278 return compareConfigurations(a, b); 1279 } else { 1280 return Integer.compare(configBScore, configAScore); 1281 } 1282 } 1283 1284 // This needs to be implemented by the connected/disconnected PNO list comparator. 1285 public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) { 1286 return 0; 1287 } 1288 1289 /** 1290 * Returns an integer representing a score for each configuration. The scores are assigned 1291 * based on the status of the configuration. The scores are assigned according to the order: 1292 * Fully enabled network > Temporarily disabled network > Permanently disabled network. 1293 */ 1294 private int getPnoNetworkSortScore(WifiConfiguration config) { 1295 if (config.getNetworkSelectionStatus().isNetworkEnabled()) { 1296 return ENABLED_NETWORK_SCORE; 1297 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { 1298 return TEMPORARY_DISABLED_NETWORK_SCORE; 1299 } else { 1300 return PERMANENTLY_DISABLED_NETWORK_SCORE; 1301 } 1302 } 1303 } 1304 1305 /** 1306 * Disconnected PnoNetwork list sorting algorithm: 1307 * Place the configurations in descending order of their |numAssociation| values. If networks 1308 * have the same |numAssociation|, then sort them in descending order of their |priority| 1309 * values. 1310 */ 1311 private static final PnoListComparator sDisconnectedPnoListComparator = 1312 new PnoListComparator() { 1313 @Override 1314 public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) { 1315 if (a.numAssociation != b.numAssociation) { 1316 return Long.compare(b.numAssociation, a.numAssociation); 1317 } else { 1318 return Integer.compare(b.priority, a.priority); 1319 } 1320 } 1321 }; 1322 1323 /** 1324 * Retrieves an updated list of priorities for all the saved networks before 1325 * enabling disconnected PNO (wpa_supplicant based PNO). 1326 * 1327 * @return list of networks with updated priorities. 1328 */ 1329 public ArrayList<WifiNative.WifiPnoNetwork> retrieveDisconnectedWifiPnoNetworkList() { 1330 return retrieveWifiPnoNetworkList(true, sDisconnectedPnoListComparator); 1331 } 1332 1333 /** 1334 * Retrieves an updated list of priorities for all the saved networks before 1335 * enabling/disabling disconnected PNO (wpa_supplicant based PNO). 1336 * 1337 * wpa_supplicant uses the priority of networks to build the list of SSID's to monitor 1338 * during PNO. If there are a lot of saved networks, this list will be truncated and we 1339 * might end up not connecting to the networks we use most frequently. So, We want the networks 1340 * to be re-sorted based on the relative |numAssociation| values. 1341 * 1342 * @param enablePno boolean indicating whether PNO is being enabled or disabled. 1343 * @return list of networks with updated priorities. 1344 */ 1345 public ArrayList<WifiNative.WifiPnoNetwork> retrieveDisconnectedWifiPnoNetworkList( 1346 boolean enablePno) { 1347 return retrieveWifiPnoNetworkList(enablePno, sDisconnectedPnoListComparator); 1348 } 1349 1350 /** 1351 * Connected PnoNetwork list sorting algorithm: 1352 * Place the configurations with |lastSeenInQualifiedNetworkSelection| set first. If networks 1353 * have the same value, then sort them in descending order of their |numAssociation| 1354 * values. 1355 */ 1356 private static final PnoListComparator sConnectedPnoListComparator = 1357 new PnoListComparator() { 1358 @Override 1359 public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) { 1360 boolean isConfigALastSeen = 1361 a.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection(); 1362 boolean isConfigBLastSeen = 1363 b.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection(); 1364 if (isConfigALastSeen != isConfigBLastSeen) { 1365 return Boolean.compare(isConfigBLastSeen, isConfigALastSeen); 1366 } else { 1367 return Long.compare(b.numAssociation, a.numAssociation); 1368 } 1369 } 1370 }; 1371 1372 /** 1373 * Retrieves an updated list of priorities for all the saved networks before 1374 * enabling connected PNO (HAL based ePno). 1375 * 1376 * @return list of networks with updated priorities. 1377 */ 1378 public ArrayList<WifiNative.WifiPnoNetwork> retrieveConnectedWifiPnoNetworkList() { 1379 return retrieveWifiPnoNetworkList(true, sConnectedPnoListComparator); 1380 } 1381 1382 /** 1383 * Retrieves an updated list of priorities for all the saved networks before 1384 * enabling/disabling PNO. 1385 * 1386 * @param enablePno boolean indicating whether PNO is being enabled or disabled. 1387 * @return list of networks with updated priorities. 1388 */ 1389 private ArrayList<WifiNative.WifiPnoNetwork> retrieveWifiPnoNetworkList( 1390 boolean enablePno, PnoListComparator pnoListComparator) { 1391 ArrayList<WifiNative.WifiPnoNetwork> pnoList = 1392 new ArrayList<WifiNative.WifiPnoNetwork>(); 1393 ArrayList<WifiConfiguration> wifiConfigurations = 1394 new ArrayList<WifiConfiguration>(mConfiguredNetworks.valuesForCurrentUser()); 1395 if (enablePno) { 1396 Collections.sort(wifiConfigurations, pnoListComparator); 1397 // Let's use the network list size as the highest priority and then go down from there. 1398 // So, the most frequently connected network has the highest priority now. 1399 int priority = wifiConfigurations.size(); 1400 if (DBG) { 1401 Log.d(TAG, "Retrieve network priorities before PNO. Max priority: " + priority); 1402 } 1403 // Initialize the RSSI threshold with sane value: 1404 // Use the 2.4GHz threshold since most WifiConfigurations are dual bands 1405 // There is very little penalty with triggering too soon, i.e. if PNO finds a network 1406 // that has an RSSI too low for us to attempt joining it. 1407 int threshold = thresholdMinimumRssi24.get(); 1408 for (WifiConfiguration config : wifiConfigurations) { 1409 pnoList.add(new WifiNative.WifiPnoNetwork(config, threshold, priority)); 1410 priority--; 1411 } 1412 } else { 1413 // Revert the priorities back to the saved config values after PNO. 1414 if (DBG) Log.d(TAG, "Retrieve network priorities after PNO."); 1415 for (WifiConfiguration config : wifiConfigurations) { 1416 pnoList.add(new WifiNative.WifiPnoNetwork(config, 0, config.priority)); 1417 } 1418 } 1419 return pnoList; 1420 } 1421 1422 String[] getWhiteListedSsids(WifiConfiguration config) { 1423 int num_ssids = 0; 1424 String nonQuoteSSID; 1425 int length; 1426 if (enableSsidWhitelist.get() == false) 1427 return null; 1428 List<String> list = new ArrayList<String>(); 1429 if (config == null) 1430 return null; 1431 if (config.linkedConfigurations == null) { 1432 return null; 1433 } 1434 if (config.SSID == null || TextUtils.isEmpty(config.SSID)) { 1435 return null; 1436 } 1437 for (String configKey : config.linkedConfigurations.keySet()) { 1438 // Sanity check that the linked configuration is still valid 1439 WifiConfiguration link = getWifiConfiguration(configKey); 1440 if (link == null) { 1441 continue; 1442 } 1443 1444 if (!link.getNetworkSelectionStatus().isNetworkEnabled()) { 1445 continue; 1446 } 1447 1448 if (link.hiddenSSID == true) { 1449 continue; 1450 } 1451 1452 if (link.SSID == null || TextUtils.isEmpty(link.SSID)) { 1453 continue; 1454 } 1455 1456 length = link.SSID.length(); 1457 if (length > 2 && (link.SSID.charAt(0) == '"') && link.SSID.charAt(length - 1) == '"') { 1458 nonQuoteSSID = link.SSID.substring(1, length - 1); 1459 } else { 1460 nonQuoteSSID = link.SSID; 1461 } 1462 1463 list.add(nonQuoteSSID); 1464 } 1465 1466 if (list.size() != 0) { 1467 length = config.SSID.length(); 1468 if (length > 2 && (config.SSID.charAt(0) == '"') 1469 && config.SSID.charAt(length - 1) == '"') { 1470 nonQuoteSSID = config.SSID.substring(1, length - 1); 1471 } else { 1472 nonQuoteSSID = config.SSID; 1473 } 1474 1475 list.add(nonQuoteSSID); 1476 } 1477 1478 return (String[])list.toArray(new String[0]); 1479 } 1480 1481 /** 1482 * Remove a network. Note that there is no saveConfig operation. 1483 * This function is retained for compatibility with the public 1484 * API. The more powerful forgetNetwork() is used by the 1485 * state machine for network removal 1486 * 1487 * @param netId network to be removed 1488 * @return {@code true} if it succeeds, {@code false} otherwise 1489 */ 1490 boolean removeNetwork(int netId) { 1491 if (showNetworks) localLogNetwork("removeNetwork", netId); 1492 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1493 if (config == null) { 1494 return false; 1495 } 1496 1497 boolean ret = mWifiNative.removeNetwork(netId); 1498 if (ret) { 1499 removeConfigAndSendBroadcastIfNeeded(netId); 1500 if (config.isPasspoint()) { 1501 writePasspointConfigs(config.FQDN, null); 1502 } 1503 } 1504 return ret; 1505 } 1506 1507 1508 static private Long getChecksum(String source) { 1509 Checksum csum = new CRC32(); 1510 csum.update(source.getBytes(), 0, source.getBytes().length); 1511 return csum.getValue(); 1512 } 1513 1514 private boolean removeConfigAndSendBroadcastIfNeeded(int netId) { 1515 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1516 if (config != null) { 1517 String key = config.configKey(); 1518 if (VDBG) { 1519 loge("removeNetwork " + netId + " key=" + key + " config.id=" + config.networkId); 1520 } 1521 1522 // cancel the last user choice 1523 if (key.equals(lastSelectedConfiguration)) { 1524 lastSelectedConfiguration = null; 1525 } 1526 1527 // Remove any associated keys 1528 if (config.enterpriseConfig != null) { 1529 removeKeys(config.enterpriseConfig); 1530 } 1531 1532 if (config.selfAdded || config.linkedConfigurations != null 1533 || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1534 if (!TextUtils.isEmpty(config.SSID)) { 1535 /* Remember that we deleted this PSK SSID */ 1536 if (config.SSID != null) { 1537 Long csum = getChecksum(config.SSID); 1538 mDeletedSSIDs.add(csum); 1539 loge("removeNetwork " + netId 1540 + " key=" + key 1541 + " config.id=" + config.networkId 1542 + " crc=" + csum); 1543 } else { 1544 loge("removeNetwork " + netId 1545 + " key=" + key 1546 + " config.id=" + config.networkId); 1547 } 1548 } 1549 } 1550 1551 mConfiguredNetworks.remove(netId); 1552 mScanDetailCaches.remove(netId); 1553 1554 writeIpAndProxyConfigurations(); 1555 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 1556 if (!config.ephemeral) { 1557 removeUserSelectionPreference(key); 1558 } 1559 1560 writeKnownNetworkHistory(); 1561 } 1562 return true; 1563 } 1564 1565 private void removeUserSelectionPreference(String configKey) { 1566 if (DBG) { 1567 Log.d(TAG, "removeUserSelectionPreference: key is " + configKey); 1568 } 1569 if (configKey == null) { 1570 return; 1571 } 1572 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 1573 WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus(); 1574 String connectChoice = status.getConnectChoice(); 1575 if (connectChoice != null && connectChoice.equals(configKey)) { 1576 Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID 1577 + " : " + config.networkId); 1578 status.setConnectChoice(null); 1579 status.setConnectChoiceTimestamp(WifiConfiguration.NetworkSelectionStatus 1580 .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1581 } 1582 } 1583 } 1584 1585 /* 1586 * Remove all networks associated with an application 1587 * 1588 * @param packageName name of the package of networks to remove 1589 * @return {@code true} if all networks removed successfully, {@code false} otherwise 1590 */ 1591 boolean removeNetworksForApp(ApplicationInfo app) { 1592 if (app == null || app.packageName == null) { 1593 return false; 1594 } 1595 1596 boolean success = true; 1597 1598 WifiConfiguration [] copiedConfigs = 1599 mConfiguredNetworks.valuesForCurrentUser().toArray(new WifiConfiguration[0]); 1600 for (WifiConfiguration config : copiedConfigs) { 1601 if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) { 1602 continue; 1603 } 1604 if (showNetworks) { 1605 localLog("Removing network " + config.SSID 1606 + ", application \"" + app.packageName + "\" uninstalled" 1607 + " from user " + UserHandle.getUserId(app.uid)); 1608 } 1609 success &= removeNetwork(config.networkId); 1610 } 1611 1612 mWifiNative.saveConfig(); 1613 1614 return success; 1615 } 1616 1617 boolean removeNetworksForUser(int userId) { 1618 boolean success = true; 1619 1620 WifiConfiguration[] copiedConfigs = 1621 mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]); 1622 for (WifiConfiguration config : copiedConfigs) { 1623 if (userId != UserHandle.getUserId(config.creatorUid)) { 1624 continue; 1625 } 1626 success &= removeNetwork(config.networkId); 1627 if (showNetworks) { 1628 localLog("Removing network " + config.SSID 1629 + ", user " + userId + " removed"); 1630 } 1631 } 1632 1633 return success; 1634 } 1635 1636 /** 1637 * Enable a network. Note that there is no saveConfig operation. 1638 * This function is retained for compatibility with the public 1639 * API. The more powerful selectNetwork()/saveNetwork() is used by the 1640 * state machine for connecting to a network 1641 * 1642 * @param netId network to be enabled 1643 * @return {@code true} if it succeeds, {@code false} otherwise 1644 */ 1645 boolean enableNetwork(int netId, boolean disableOthers, int uid) { 1646 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1647 if (config == null) { 1648 return false; 1649 } 1650 boolean ret = true; 1651 if (disableOthers) { 1652 ret = selectNetworkWithoutBroadcast(netId); 1653 if (VDBG) localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId); 1654 updateLastConnectUid(getWifiConfiguration(netId), uid); 1655 1656 writeKnownNetworkHistory(); 1657 sendConfiguredNetworksChangedBroadcast(); 1658 } else { 1659 if (VDBG) localLogNetwork("enableNetwork(disableOthers=false) ", netId); 1660 WifiConfiguration enabledNetwork; 1661 synchronized(mConfiguredNetworks) { // !!! Useless synchronization! 1662 enabledNetwork = mConfiguredNetworks.getForCurrentUser(netId); 1663 } 1664 // check just in case the network was removed by someone else. 1665 if (enabledNetwork != null) { 1666 sendConfiguredNetworksChangedBroadcast(enabledNetwork, 1667 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1668 } 1669 } 1670 return ret; 1671 } 1672 1673 boolean selectNetworkWithoutBroadcast(int netId) { 1674 if (VDBG) localLog("selectNetworkWithoutBroadcast: " + netId); 1675 final WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1676 if (config == null) { 1677 return false; 1678 } 1679 if (!mWifiNative.selectNetwork(netId)) { 1680 loge("Select network in wpa_supplicant failed on " + netId); 1681 return false; 1682 } 1683 config.status = Status.ENABLED; 1684 markAllNetworksDisabledExcept(netId); 1685 return true; 1686 } 1687 1688 /** 1689 * Disable a network in wpa_supplicant. 1690 */ 1691 boolean disableNetworkNative(WifiConfiguration config) { 1692 if (VDBG) localLog("disableNetworkNative: " + config); 1693 if (!mWifiNative.disableNetwork(config.networkId)) { 1694 loge("Disable network in wpa_supplicant failed on " + config.networkId); 1695 return false; 1696 } 1697 config.status = Status.DISABLED; 1698 return true; 1699 } 1700 1701 /** 1702 * Disable all networks in wpa_supplicant. 1703 */ 1704 void disableAllNetworksNative() { 1705 if (VDBG) localLog("disableAllNetworks"); 1706 boolean networkDisabled = false; 1707 for (WifiConfiguration enabled : mConfiguredNetworks.getEnabledNetworksForCurrentUser()) { 1708 if (disableNetworkNative(enabled)) { 1709 networkDisabled = true; 1710 } 1711 } 1712 if (networkDisabled) { 1713 sendConfiguredNetworksChangedBroadcast(); 1714 } 1715 } 1716 1717 /** 1718 * Disable a network. Note that there is no saveConfig operation. 1719 * @param netId network to be disabled 1720 * @return {@code true} if it succeeds, {@code false} otherwise 1721 */ 1722 boolean disableNetwork(int netId) { 1723 WifiConfiguration config = getWifiConfiguration(netId); 1724 if (config == null) { 1725 return false; 1726 } 1727 boolean ret = disableNetworkNative(config); 1728 if (ret) { 1729 mWifiStateMachine.registerNetworkDisabled(netId); 1730 } 1731 return ret; 1732 } 1733 1734 /** 1735 * Update a network according to the update reason and its current state 1736 * @param netId The network ID of the network need update 1737 * @param reason The reason to update the network 1738 * @return false if no change made to the input configure file, can due to error or need not 1739 * true the input config file has been changed 1740 */ 1741 boolean updateNetworkSelectionStatus(int netId, int reason) { 1742 WifiConfiguration config = getWifiConfiguration(netId); 1743 return updateNetworkSelectionStatus(config, reason); 1744 } 1745 1746 /** 1747 * Update a network according to the update reason and its current state 1748 * @param config the network need update 1749 * @param reason The reason to update the network 1750 * @return false if no change made to the input configure file, can due to error or need not 1751 * true the input config file has been changed 1752 */ 1753 boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) { 1754 if (config == null) { 1755 return false; 1756 } 1757 1758 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1759 if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { 1760 updateNetworkStatus(config, WifiConfiguration.NetworkSelectionStatus 1761 .NETWORK_SELECTION_ENABLE); 1762 localLog("Enable network:" + config.configKey()); 1763 return true; 1764 } 1765 1766 networkStatus.incrementDisableReasonCounter(reason); 1767 if (DBG) { 1768 localLog("Network:" + config.SSID + "disable counter of " 1769 + WifiConfiguration.NetworkSelectionStatus.getNetworkDisableReasonString(reason) 1770 + " is: " + networkStatus.getDisableReasonCounter(reason) + "and threshold is: " 1771 + NETWORK_SELECTION_DISABLE_THRESHOLD[reason]); 1772 } 1773 1774 if (networkStatus.getDisableReasonCounter(reason) 1775 >= NETWORK_SELECTION_DISABLE_THRESHOLD[reason]) { 1776 return updateNetworkStatus(config, reason); 1777 } 1778 return true; 1779 } 1780 1781 /** 1782 * Check the config. If it is temporarily disabled, check the disable time is expired or not, If 1783 * expired, enabled it again for qualified network selection. 1784 * @param networkId the id of the network to be checked for possible unblock (due to timeout) 1785 * @return true if network status has been changed 1786 * false network status is not changed 1787 */ 1788 boolean tryEnableQualifiedNetwork(int networkId) { 1789 WifiConfiguration config = getWifiConfiguration(networkId); 1790 if (config == null) { 1791 localLog("updateQualifiedNetworkstatus invalid network."); 1792 return false; 1793 } 1794 return tryEnableQualifiedNetwork(config); 1795 } 1796 1797 /** 1798 * Check the config. If it is temporarily disabled, check the disable is expired or not, If 1799 * expired, enabled it again for qualified network selection. 1800 * @param config network to be checked for possible unblock (due to timeout) 1801 * @return true if network status has been changed 1802 * false network status is not changed 1803 */ 1804 boolean tryEnableQualifiedNetwork(WifiConfiguration config) { 1805 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1806 if (networkStatus.isNetworkTemporaryDisabled()) { 1807 //time difference in minutes 1808 long timeDifference = (System.currentTimeMillis() 1809 - networkStatus.getDisableTime()) / 1000 / 60; 1810 if (timeDifference < 0 || timeDifference 1811 >= NETWORK_SELECTION_DISABLE_TIMEOUT[ 1812 networkStatus.getNetworkSelectionDisableReason()]) { 1813 updateNetworkSelectionStatus(config.networkId, 1814 networkStatus.NETWORK_SELECTION_ENABLE); 1815 return true; 1816 } 1817 } 1818 return false; 1819 } 1820 1821 /** 1822 * Update a network's status. Note that there is no saveConfig operation. 1823 * @param config network to be updated 1824 * @param reason reason code for updated 1825 * @return false if no change made to the input configure file, can due to error or need not 1826 * true the input config file has been changed 1827 */ 1828 boolean updateNetworkStatus(WifiConfiguration config, int reason) { 1829 localLog("updateNetworkStatus:" + (config == null ? null : config.SSID)); 1830 if (config == null) { 1831 return false; 1832 } 1833 1834 WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 1835 if (reason < 0 || reason >= WifiConfiguration.NetworkSelectionStatus 1836 .NETWORK_SELECTION_DISABLED_MAX) { 1837 localLog("Invalid Network disable reason:" + reason); 1838 return false; 1839 } 1840 1841 if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { 1842 if (networkStatus.isNetworkEnabled()) { 1843 if (DBG) { 1844 localLog("Need not change Qualified network Selection status since" 1845 + " already enabled"); 1846 } 1847 return false; 1848 } 1849 networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus 1850 .NETWORK_SELECTION_ENABLED); 1851 networkStatus.setNetworkSelectionDisableReason(reason); 1852 networkStatus.setDisableTime( 1853 WifiConfiguration.NetworkSelectionStatus 1854 .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 1855 networkStatus.clearDisableReasonCounter(); 1856 String disableTime = DateFormat.getDateTimeInstance().format(new Date()); 1857 if (DBG) { 1858 localLog("Re-enable network: " + config.SSID + " at " + disableTime); 1859 } 1860 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1861 } else { 1862 //disable the network 1863 if (networkStatus.isNetworkPermanentlyDisabled()) { 1864 //alreay permanent disable 1865 if (DBG) { 1866 localLog("Do nothing. Alreay permanent disabled! " 1867 + WifiConfiguration.NetworkSelectionStatus 1868 .getNetworkDisableReasonString(reason)); 1869 } 1870 return false; 1871 } else if (networkStatus.isNetworkTemporaryDisabled() 1872 && reason < WifiConfiguration.NetworkSelectionStatus 1873 .DISABLED_TLS_VERSION_MISMATCH) { 1874 //alreay temporarily disable 1875 if (DBG) { 1876 localLog("Do nothing. Already temporarily disabled! " 1877 + WifiConfiguration.NetworkSelectionStatus 1878 .getNetworkDisableReasonString(reason)); 1879 } 1880 return false; 1881 } 1882 1883 if (networkStatus.isNetworkEnabled()) { 1884 disableNetworkNative(config); 1885 sendConfiguredNetworksChangedBroadcast(config, 1886 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1887 localLog("Disable network " + config.SSID + " reason:" 1888 + WifiConfiguration.NetworkSelectionStatus 1889 .getNetworkDisableReasonString(reason)); 1890 } 1891 if (reason < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) { 1892 networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus 1893 .NETWORK_SELECTION_TEMPORARY_DISABLED); 1894 networkStatus.setDisableTime(System.currentTimeMillis()); 1895 } else { 1896 networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus 1897 .NETWORK_SELECTION_PERMANENTLY_DISABLED); 1898 } 1899 networkStatus.setNetworkSelectionDisableReason(reason); 1900 if (DBG) { 1901 String disableTime = DateFormat.getDateTimeInstance().format(new Date()); 1902 localLog("Network:" + config.SSID + "Configure new status:" 1903 + networkStatus.getNetworkStatusString() + " with reason:" 1904 + networkStatus.getNetworkDisableReasonString() + " at: " + disableTime); 1905 } 1906 } 1907 return true; 1908 } 1909 1910 /** 1911 * Save the configured networks in supplicant to disk 1912 * @return {@code true} if it succeeds, {@code false} otherwise 1913 */ 1914 boolean saveConfig() { 1915 return mWifiNative.saveConfig(); 1916 } 1917 1918 /** 1919 * Start WPS pin method configuration with pin obtained 1920 * from the access point 1921 * @param config WPS configuration 1922 * @return Wps result containing status and pin 1923 */ 1924 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) { 1925 WpsResult result = new WpsResult(); 1926 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 1927 /* WPS leaves all networks disabled */ 1928 markAllNetworksDisabled(); 1929 result.status = WpsResult.Status.SUCCESS; 1930 } else { 1931 loge("Failed to start WPS pin method configuration"); 1932 result.status = WpsResult.Status.FAILURE; 1933 } 1934 return result; 1935 } 1936 1937 /** 1938 * Start WPS pin method configuration with obtained 1939 * from the device 1940 * @return WpsResult indicating status and pin 1941 */ 1942 WpsResult startWpsWithPinFromDevice(WpsInfo config) { 1943 WpsResult result = new WpsResult(); 1944 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 1945 /* WPS leaves all networks disabled */ 1946 if (!TextUtils.isEmpty(result.pin)) { 1947 markAllNetworksDisabled(); 1948 result.status = WpsResult.Status.SUCCESS; 1949 } else { 1950 loge("Failed to start WPS pin method configuration"); 1951 result.status = WpsResult.Status.FAILURE; 1952 } 1953 return result; 1954 } 1955 1956 /** 1957 * Start WPS push button configuration 1958 * @param config WPS configuration 1959 * @return WpsResult indicating status and pin 1960 */ 1961 WpsResult startWpsPbc(WpsInfo config) { 1962 WpsResult result = new WpsResult(); 1963 if (mWifiNative.startWpsPbc(config.BSSID)) { 1964 /* WPS leaves all networks disabled */ 1965 markAllNetworksDisabled(); 1966 result.status = WpsResult.Status.SUCCESS; 1967 } else { 1968 loge("Failed to start WPS push button configuration"); 1969 result.status = WpsResult.Status.FAILURE; 1970 } 1971 return result; 1972 } 1973 1974 /** 1975 * Fetch the static IP configuration for a given network id 1976 */ 1977 StaticIpConfiguration getStaticIpConfiguration(int netId) { 1978 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1979 if (config != null) { 1980 return config.getStaticIpConfiguration(); 1981 } 1982 return null; 1983 } 1984 1985 /** 1986 * Set the static IP configuration for a given network id 1987 */ 1988 void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) { 1989 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 1990 if (config != null) { 1991 config.setStaticIpConfiguration(staticIpConfiguration); 1992 } 1993 } 1994 1995 /** 1996 * set default GW MAC address 1997 */ 1998 void setDefaultGwMacAddress(int netId, String macAddress) { 1999 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 2000 if (config != null) { 2001 //update defaultGwMacAddress 2002 config.defaultGwMacAddress = macAddress; 2003 } 2004 } 2005 2006 2007 /** 2008 * Fetch the proxy properties for a given network id 2009 * @param netId id 2010 * @return ProxyInfo for the network id 2011 */ 2012 ProxyInfo getProxyProperties(int netId) { 2013 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 2014 if (config != null) { 2015 return config.getHttpProxy(); 2016 } 2017 return null; 2018 } 2019 2020 /** 2021 * Return if the specified network is using static IP 2022 * @param netId id 2023 * @return {@code true} if using static ip for netId 2024 */ 2025 boolean isUsingStaticIp(int netId) { 2026 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 2027 if (config != null && config.getIpAssignment() == IpAssignment.STATIC) { 2028 return true; 2029 } 2030 return false; 2031 } 2032 2033 boolean isEphemeral(int netId) { 2034 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 2035 return config != null && config.ephemeral; 2036 } 2037 2038 /** 2039 * Should be called when a single network configuration is made. 2040 * @param network The network configuration that changed. 2041 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 2042 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 2043 */ 2044 private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, 2045 int reason) { 2046 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 2047 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2048 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 2049 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); 2050 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 2051 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 2052 } 2053 2054 /** 2055 * Should be called when multiple network configuration changes are made. 2056 */ 2057 private void sendConfiguredNetworksChangedBroadcast() { 2058 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 2059 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2060 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 2061 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 2062 } 2063 2064 void loadConfiguredNetworks() { 2065 2066 mLastPriority = 0; 2067 2068 final Map<String, WifiConfiguration> configs = new HashMap<>(); 2069 2070 final SparseArray<Map<String, String>> networkExtras = new SparseArray<>(); 2071 2072 int last_id = -1; 2073 boolean done = false; 2074 while (!done) { 2075 2076 String listStr = mWifiNative.listNetworks(last_id); 2077 if (listStr == null) 2078 return; 2079 2080 String[] lines = listStr.split("\n"); 2081 2082 if (showNetworks) { 2083 localLog("WifiConfigManager: loadConfiguredNetworks: "); 2084 for (String net : lines) { 2085 localLog(net); 2086 } 2087 } 2088 2089 // Skip the first line, which is a header 2090 for (int i = 1; i < lines.length; i++) { 2091 String[] result = lines[i].split("\t"); 2092 // network-id | ssid | bssid | flags 2093 WifiConfiguration config = new WifiConfiguration(); 2094 try { 2095 config.networkId = Integer.parseInt(result[0]); 2096 last_id = config.networkId; 2097 } catch(NumberFormatException e) { 2098 loge("Failed to read network-id '" + result[0] + "'"); 2099 continue; 2100 } 2101 2102 readNetworkVariables(config); 2103 2104 // Parse the serialized JSON dictionary in ID_STRING_VAR_NAME once and cache the 2105 // result for efficiency. 2106 Map<String, String> extras = mWifiNative.getNetworkExtra(config.networkId, 2107 ID_STRING_VAR_NAME); 2108 if (extras == null) { 2109 extras = new HashMap<String, String>(); 2110 // If ID_STRING_VAR_NAME did not contain a dictionary, assume that it contains 2111 // just a quoted FQDN. This is the legacy format that was used in Marshmallow. 2112 final String fqdn = Utils.unquote(mWifiNative.getNetworkVariable( 2113 config.networkId, ID_STRING_VAR_NAME)); 2114 if (fqdn != null) { 2115 extras.put(ID_STRING_KEY_FQDN, fqdn); 2116 config.FQDN = fqdn; 2117 // Mark the configuration as a Hotspot 2.0 network. 2118 config.providerFriendlyName = ""; 2119 } 2120 } 2121 networkExtras.put(config.networkId, extras); 2122 2123 Checksum csum = new CRC32(); 2124 if (config.SSID != null) { 2125 csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length); 2126 long d = csum.getValue(); 2127 if (mDeletedSSIDs.contains(d)) { 2128 loge(" got CRC for SSID " + config.SSID + " -> " + d + ", was deleted"); 2129 } 2130 } 2131 2132 if (config.priority > mLastPriority) { 2133 mLastPriority = config.priority; 2134 } 2135 2136 config.setIpAssignment(IpAssignment.DHCP); 2137 config.setProxySettings(ProxySettings.NONE); 2138 2139 if (!WifiServiceImpl.isValid(config)) { 2140 if (showNetworks) { 2141 localLog("Ignoring network " + config.networkId + " because configuration " 2142 + "loaded from wpa_supplicant.conf is not valid."); 2143 } 2144 continue; 2145 } 2146 2147 // The configKey is explicitly stored in wpa_supplicant.conf, because config does 2148 // not contain sufficient information to compute it at this point. 2149 String configKey = extras.get(ID_STRING_KEY_CONFIG_KEY); 2150 if (configKey == null) { 2151 // Handle the legacy case where the configKey is not stored in 2152 // wpa_supplicant.conf but can be computed straight away. 2153 configKey = config.configKey(); 2154 } 2155 2156 final WifiConfiguration duplicateConfig = configs.put(configKey, config); 2157 if (duplicateConfig != null) { 2158 // The network is already known. Overwrite the duplicate entry. 2159 if (showNetworks) { 2160 localLog("Replacing duplicate network " + duplicateConfig.networkId 2161 + " with " + config.networkId + "."); 2162 } 2163 // This can happen after the user manually connected to an AP and tried to use 2164 // WPS to connect the AP later. In this case, the supplicant will create a new 2165 // network for the AP although there is an existing network already. 2166 mWifiNative.removeNetwork(duplicateConfig.networkId); 2167 } 2168 } 2169 2170 done = (lines.length == 1); 2171 } 2172 2173 readNetworkHistory(configs); 2174 readPasspointConfig(configs, networkExtras); 2175 2176 // We are only now updating mConfiguredNetworks for two reasons: 2177 // 1) The information required to compute configKeys is spread across wpa_supplicant.conf 2178 // and networkHistory.txt. Thus, we had to load both files first. 2179 // 2) mConfiguredNetworks caches a Passpoint network's FQDN the moment the network is added. 2180 // Thus, we had to load the FQDNs first. 2181 mConfiguredNetworks.clear(); 2182 for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) { 2183 final String configKey = entry.getKey(); 2184 final WifiConfiguration config = entry.getValue(); 2185 if (!configKey.equals(config.configKey())) { 2186 if (showNetworks) { 2187 log("Ignoring network " + config.networkId + " because the configKey loaded " 2188 + "from wpa_supplicant.conf is not valid."); 2189 } 2190 mWifiNative.removeNetwork(config.networkId); 2191 continue; 2192 } 2193 mConfiguredNetworks.put(config); 2194 } 2195 2196 readIpAndProxyConfigurations(); 2197 2198 sendConfiguredNetworksChangedBroadcast(); 2199 2200 if (showNetworks) { 2201 localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.sizeForAllUsers() 2202 + " networks (for all users)"); 2203 } 2204 2205 if (mConfiguredNetworks.sizeForAllUsers() == 0) { 2206 // no networks? Lets log if the file contents 2207 logKernelTime(); 2208 logContents(SUPPLICANT_CONFIG_FILE); 2209 logContents(SUPPLICANT_CONFIG_FILE_BACKUP); 2210 logContents(mWifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE); 2211 } 2212 } 2213 2214 private void logContents(String file) { 2215 localLogAndLogcat("--- Begin " + file + " ---"); 2216 BufferedReader reader = null; 2217 try { 2218 reader = new BufferedReader(new FileReader(file)); 2219 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 2220 localLogAndLogcat(line); 2221 } 2222 } catch (FileNotFoundException e) { 2223 localLog("Could not open " + file + ", " + e); 2224 Log.w(TAG, "Could not open " + file + ", " + e); 2225 } catch (IOException e) { 2226 localLog("Could not read " + file + ", " + e); 2227 Log.w(TAG, "Could not read " + file + ", " + e); 2228 } finally { 2229 try { 2230 if (reader != null) { 2231 reader.close(); 2232 } 2233 } catch (IOException e) { 2234 // Just ignore the fact that we couldn't close 2235 } 2236 } 2237 localLogAndLogcat("--- End " + file + " Contents ---"); 2238 } 2239 2240 private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) { 2241 // TODO(b/26733972): This method assumes that the SSID is a unique identifier for network 2242 // configurations. That is wrong. There may be any number of networks with the same SSID. 2243 // There may also be any number of network configurations for the same network. The correct 2244 // unique identifier is the configKey. This method should be switched from SSID to configKey 2245 // (which is either stored in wpa_supplicant.conf directly or can be computed from the 2246 // information found in that file). 2247 Map<String, String> result = new HashMap<>(); 2248 BufferedReader reader = null; 2249 if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key); 2250 2251 try { 2252 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 2253 boolean found = false; 2254 String networkSsid = null; 2255 String value = null; 2256 2257 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 2258 2259 if (line.matches("[ \\t]*network=\\{")) { 2260 found = true; 2261 networkSsid = null; 2262 value = null; 2263 } else if (line.matches("[ \\t]*\\}")) { 2264 found = false; 2265 networkSsid = null; 2266 value = null; 2267 } 2268 2269 if (found) { 2270 String trimmedLine = line.trim(); 2271 if (trimmedLine.startsWith("ssid=")) { 2272 networkSsid = trimmedLine.substring(5); 2273 } else if (trimmedLine.startsWith(key + "=")) { 2274 value = trimmedLine.substring(key.length() + 1); 2275 } 2276 2277 if (networkSsid != null && value != null) { 2278 result.put(networkSsid, value); 2279 } 2280 } 2281 } 2282 } catch (FileNotFoundException e) { 2283 if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 2284 } catch (IOException e) { 2285 if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 2286 } finally { 2287 try { 2288 if (reader != null) { 2289 reader.close(); 2290 } 2291 } catch (IOException e) { 2292 // Just ignore the fact that we couldn't close 2293 } 2294 } 2295 2296 return result; 2297 } 2298 2299 private String readNetworkVariableFromSupplicantFile(String ssid, String key) { 2300 long start = SystemClock.elapsedRealtimeNanos(); 2301 Map<String, String> data = readNetworkVariablesFromSupplicantFile(key); 2302 long end = SystemClock.elapsedRealtimeNanos(); 2303 2304 if (VDBG) { 2305 loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key 2306 + " duration=" + (long)(end - start)); 2307 } 2308 return data.get(ssid); 2309 } 2310 2311 /* Mark all networks except specified netId as disabled */ 2312 private void markAllNetworksDisabledExcept(int netId) { 2313 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 2314 if(config != null && config.networkId != netId) { 2315 if (config.status != Status.DISABLED) { 2316 config.status = Status.DISABLED; 2317 } 2318 } 2319 } 2320 } 2321 2322 private void markAllNetworksDisabled() { 2323 markAllNetworksDisabledExcept(INVALID_NETWORK_ID); 2324 } 2325 2326 boolean needsUnlockedKeyStore() { 2327 2328 // Any network using certificates to authenticate access requires 2329 // unlocked key store; unless the certificates can be stored with 2330 // hardware encryption 2331 2332 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 2333 2334 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) 2335 && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 2336 2337 if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 2338 return true; 2339 } 2340 } 2341 } 2342 2343 return false; 2344 } 2345 2346 void readPasspointConfig(Map<String, WifiConfiguration> configs, 2347 SparseArray<Map<String, String>> networkExtras) { 2348 List<HomeSP> homeSPs; 2349 try { 2350 homeSPs = mMOManager.loadAllSPs(); 2351 } catch (IOException e) { 2352 loge("Could not read " + PPS_FILE + " : " + e); 2353 return; 2354 } 2355 2356 int matchedConfigs = 0; 2357 for (HomeSP homeSp : homeSPs) { 2358 String fqdn = homeSp.getFQDN(); 2359 Log.d(TAG, "Looking for " + fqdn); 2360 for (WifiConfiguration config : configs.values()) { 2361 Log.d(TAG, "Testing " + config.SSID); 2362 2363 if (config.enterpriseConfig == null) { 2364 continue; 2365 } 2366 final String configFqdn = 2367 networkExtras.get(config.networkId).get(ID_STRING_KEY_FQDN); 2368 if (configFqdn != null && configFqdn.equals(fqdn)) { 2369 Log.d(TAG, "Matched " + configFqdn + " with " + config.networkId); 2370 ++matchedConfigs; 2371 config.FQDN = fqdn; 2372 config.providerFriendlyName = homeSp.getFriendlyName(); 2373 2374 HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums(); 2375 config.roamingConsortiumIds = new long[roamingConsortiumIds.size()]; 2376 int i = 0; 2377 for (long id : roamingConsortiumIds) { 2378 config.roamingConsortiumIds[i] = id; 2379 i++; 2380 } 2381 IMSIParameter imsiParameter = homeSp.getCredential().getImsi(); 2382 config.enterpriseConfig.setPlmn( 2383 imsiParameter != null ? imsiParameter.toString() : null); 2384 config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm()); 2385 } 2386 } 2387 } 2388 2389 Log.d(TAG, "loaded " + matchedConfigs + " passpoint configs"); 2390 } 2391 2392 public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) { 2393 mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() { 2394 @Override 2395 public void onWriteCalled(DataOutputStream out) throws IOException { 2396 try { 2397 if (homeSP != null) { 2398 mMOManager.addSP(homeSP); 2399 } else { 2400 mMOManager.removeSP(fqdn); 2401 } 2402 } catch (IOException e) { 2403 loge("Could not write " + PPS_FILE + " : " + e); 2404 } 2405 } 2406 }, false); 2407 } 2408 2409 /** 2410 * Write network history, WifiConfigurations and mScanDetailCaches to file. 2411 */ 2412 private void readNetworkHistory(Map<String, WifiConfiguration> configs) { 2413 mWifiNetworkHistory.readNetworkHistory(configs, 2414 mScanDetailCaches, 2415 mDeletedSSIDs, 2416 mDeletedEphemeralSSIDs); 2417 } 2418 2419 /** 2420 * Read Network history from file, merge it into mConfiguredNetowrks and mScanDetailCaches 2421 */ 2422 public void writeKnownNetworkHistory() { 2423 final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 2424 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 2425 networks.add(new WifiConfiguration(config)); 2426 } 2427 mWifiNetworkHistory.writeKnownNetworkHistory(networks, 2428 mScanDetailCaches, 2429 mDeletedSSIDs, 2430 mDeletedEphemeralSSIDs); 2431 } 2432 2433 public void setAndEnableLastSelectedConfiguration(int netId) { 2434 if (VDBG) { 2435 loge("setLastSelectedConfiguration " + Integer.toString(netId)); 2436 } 2437 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 2438 lastSelectedConfiguration = null; 2439 mLastSelectedTimeStamp = -1; 2440 } else { 2441 WifiConfiguration selected = getWifiConfiguration(netId); 2442 if (selected == null) { 2443 lastSelectedConfiguration = null; 2444 mLastSelectedTimeStamp = -1; 2445 } else { 2446 lastSelectedConfiguration = selected.configKey(); 2447 mLastSelectedTimeStamp = System.currentTimeMillis(); 2448 updateNetworkSelectionStatus(netId, 2449 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 2450 if (VDBG) { 2451 loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration); 2452 } 2453 } 2454 } 2455 } 2456 2457 public void setLatestUserSelectedConfiguration(WifiConfiguration network) { 2458 if (network != null) { 2459 lastSelectedConfiguration = network.configKey(); 2460 mLastSelectedTimeStamp = System.currentTimeMillis(); 2461 } 2462 } 2463 2464 public String getLastSelectedConfiguration() { 2465 return lastSelectedConfiguration; 2466 } 2467 2468 public long getLastSelectedTimeStamp() { 2469 return mLastSelectedTimeStamp; 2470 } 2471 2472 public boolean isLastSelectedConfiguration(WifiConfiguration config) { 2473 return (lastSelectedConfiguration != null 2474 && config != null 2475 && lastSelectedConfiguration.equals(config.configKey())); 2476 } 2477 2478 private void writeIpAndProxyConfigurations() { 2479 final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 2480 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 2481 if (!config.ephemeral) { 2482 networks.put(configKey(config), config.getIpConfiguration()); 2483 } 2484 } 2485 2486 mIpconfigStore.writeIpAndProxyConfigurations(ipConfigFile, networks); 2487 } 2488 2489 private void readIpAndProxyConfigurations() { 2490 SparseArray<IpConfiguration> networks = 2491 mIpconfigStore.readIpAndProxyConfigurations(ipConfigFile); 2492 2493 if (networks == null || networks.size() == 0) { 2494 // IpConfigStore.readIpAndProxyConfigurations has already logged an error. 2495 return; 2496 } 2497 2498 for (int i = 0; i < networks.size(); i++) { 2499 int id = networks.keyAt(i); 2500 WifiConfiguration config = mConfiguredNetworks.getByConfigKeyIDForAllUsers(id); 2501 // This is the only place the map is looked up through a (dangerous) hash-value! 2502 2503 if (config == null || config.ephemeral) { 2504 loge("configuration found for missing network, nid=" + id 2505 +", ignored, networks.size=" + Integer.toString(networks.size())); 2506 } else { 2507 config.setIpConfiguration(networks.valueAt(i)); 2508 } 2509 } 2510 } 2511 2512 /* 2513 * Convert string to Hexadecimal before passing to wifi native layer 2514 * In native function "doCommand()" have trouble in converting Unicode character string to UTF8 2515 * conversion to hex is required because SSIDs can have space characters in them; 2516 * and that can confuses the supplicant because it uses space charaters as delimiters 2517 */ 2518 2519 public static String encodeSSID(String str){ 2520 return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8)); 2521 } 2522 2523 private boolean saveConfigToSupplicant(WifiConfiguration config, int netId) { 2524 if (config.SSID != null && !mWifiNative.setNetworkVariable( 2525 netId, 2526 WifiConfiguration.ssidVarName, 2527 encodeSSID(config.SSID))) { 2528 loge("failed to set SSID: " + config.SSID); 2529 return false; 2530 } 2531 2532 final Map<String, String> metadata = new HashMap<String, String>(); 2533 if (config.isPasspoint()) { 2534 metadata.put(ID_STRING_KEY_FQDN, config.FQDN); 2535 } 2536 metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey()); 2537 metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid)); 2538 if (!mWifiNative.setNetworkExtra(netId, ID_STRING_VAR_NAME, metadata)) { 2539 loge("failed to set id_str: " + metadata.toString()); 2540 return false; 2541 } 2542 2543 //set selected BSSID to supplicant 2544 if (config.getNetworkSelectionStatus().getNetworkSelectionBSSID() != null) { 2545 String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID(); 2546 if (!mWifiNative.setNetworkVariable(netId, WifiConfiguration.bssidVarName, bssid)) { 2547 loge("failed to set BSSID: " + bssid); 2548 return false; 2549 } 2550 } 2551 2552 String allowedKeyManagementString = 2553 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 2554 if (config.allowedKeyManagement.cardinality() != 0 && !mWifiNative.setNetworkVariable( 2555 netId, 2556 WifiConfiguration.KeyMgmt.varName, 2557 allowedKeyManagementString)) { 2558 loge("failed to set key_mgmt: " + allowedKeyManagementString); 2559 return false; 2560 } 2561 2562 String allowedProtocolsString = 2563 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 2564 if (config.allowedProtocols.cardinality() != 0 && !mWifiNative.setNetworkVariable( 2565 netId, 2566 WifiConfiguration.Protocol.varName, 2567 allowedProtocolsString)) { 2568 loge("failed to set proto: " + allowedProtocolsString); 2569 return false; 2570 } 2571 2572 String allowedAuthAlgorithmsString = 2573 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 2574 if (config.allowedAuthAlgorithms.cardinality() != 0 && !mWifiNative.setNetworkVariable( 2575 netId, 2576 WifiConfiguration.AuthAlgorithm.varName, 2577 allowedAuthAlgorithmsString)) { 2578 loge("failed to set auth_alg: " + allowedAuthAlgorithmsString); 2579 return false; 2580 } 2581 2582 String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers, 2583 WifiConfiguration.PairwiseCipher.strings); 2584 if (config.allowedPairwiseCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable( 2585 netId, 2586 WifiConfiguration.PairwiseCipher.varName, 2587 allowedPairwiseCiphersString)) { 2588 loge("failed to set pairwise: " + allowedPairwiseCiphersString); 2589 return false; 2590 } 2591 2592 String allowedGroupCiphersString = 2593 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 2594 if (config.allowedGroupCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable( 2595 netId, 2596 WifiConfiguration.GroupCipher.varName, 2597 allowedGroupCiphersString)) { 2598 loge("failed to set group: " + allowedGroupCiphersString); 2599 return false; 2600 } 2601 2602 // Prevent client screw-up by passing in a WifiConfiguration we gave it 2603 // by preventing "*" as a key. 2604 if (config.preSharedKey != null && !config.preSharedKey.equals("*") 2605 && !mWifiNative.setNetworkVariable( 2606 netId, 2607 WifiConfiguration.pskVarName, 2608 config.preSharedKey)) { 2609 loge("failed to set psk"); 2610 return false; 2611 } 2612 2613 boolean hasSetKey = false; 2614 if (config.wepKeys != null) { 2615 for (int i = 0; i < config.wepKeys.length; i++) { 2616 // Prevent client screw-up by passing in a WifiConfiguration we gave it 2617 // by preventing "*" as a key. 2618 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 2619 if (!mWifiNative.setNetworkVariable( 2620 netId, 2621 WifiConfiguration.wepKeyVarNames[i], 2622 config.wepKeys[i])) { 2623 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 2624 return false; 2625 } 2626 hasSetKey = true; 2627 } 2628 } 2629 } 2630 2631 if (hasSetKey) { 2632 if (!mWifiNative.setNetworkVariable( 2633 netId, 2634 WifiConfiguration.wepTxKeyIdxVarName, 2635 Integer.toString(config.wepTxKeyIndex))) { 2636 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 2637 return false; 2638 } 2639 } 2640 2641 if (!mWifiNative.setNetworkVariable( 2642 netId, 2643 WifiConfiguration.priorityVarName, 2644 Integer.toString(config.priority))) { 2645 loge(config.SSID + ": failed to set priority: " + config.priority); 2646 return false; 2647 } 2648 2649 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 2650 netId, 2651 WifiConfiguration.hiddenSSIDVarName, 2652 Integer.toString(config.hiddenSSID ? 1 : 0))) { 2653 loge(config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID); 2654 return false; 2655 } 2656 2657 if (config.requirePMF && !mWifiNative.setNetworkVariable( 2658 netId, 2659 WifiConfiguration.pmfVarName, 2660 "2")) { 2661 loge(config.SSID + ": failed to set requirePMF: " + config.requirePMF); 2662 return false; 2663 } 2664 2665 if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable( 2666 netId, 2667 WifiConfiguration.updateIdentiferVarName, 2668 config.updateIdentifier)) { 2669 loge(config.SSID + ": failed to set updateIdentifier: " + config.updateIdentifier); 2670 return false; 2671 } 2672 2673 if (config.enterpriseConfig != null 2674 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 2675 2676 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 2677 2678 if (needsKeyStore(enterpriseConfig)) { 2679 try { 2680 /* config passed may include only fields being updated. 2681 * In order to generate the key id, fetch uninitialized 2682 * fields from the currently tracked configuration 2683 */ 2684 WifiConfiguration currentConfig = mConfiguredNetworks.getForCurrentUser(netId); 2685 String keyId = config.getKeyIdForCredentials(currentConfig); 2686 2687 if (!installKeys(currentConfig != null 2688 ? currentConfig.enterpriseConfig : null, enterpriseConfig, keyId)) { 2689 loge(config.SSID + ": failed to install keys"); 2690 return false; 2691 } 2692 } catch (IllegalStateException e) { 2693 loge(config.SSID + " invalid config for key installation"); 2694 return false; 2695 } 2696 } 2697 2698 if (!enterpriseConfig.saveToSupplicant(new SupplicantSaver(netId, config.SSID))) { 2699 removeKeys(enterpriseConfig); 2700 return false; 2701 } 2702 } 2703 2704 return true; 2705 } 2706 2707 private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) { 2708 /* 2709 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 2710 * network configuration. Otherwise, the networkId should 2711 * refer to an existing configuration. 2712 */ 2713 2714 if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); 2715 if (config.isPasspoint() && !mMOManager.isEnabled()) { 2716 Log.e(TAG, "Passpoint is not enabled"); 2717 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2718 } 2719 2720 int netId = config.networkId; 2721 boolean newNetwork = false; 2722 boolean existingMO = false; 2723 // networkId of INVALID_NETWORK_ID means we want to create a new network 2724 if (netId == INVALID_NETWORK_ID) { 2725 WifiConfiguration savedConfig = 2726 mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()); 2727 if (savedConfig != null) { 2728 netId = savedConfig.networkId; 2729 } else { 2730 if (mMOManager.getHomeSP(config.FQDN) != null) { 2731 loge("addOrUpdateNetworkNative passpoint " + config.FQDN 2732 + " was found, but no network Id"); 2733 existingMO = true; 2734 } 2735 newNetwork = true; 2736 netId = mWifiNative.addNetwork(); 2737 if (netId < 0) { 2738 loge("Failed to add a network!"); 2739 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2740 } else { 2741 loge("addOrUpdateNetworkNative created netId=" + netId); 2742 } 2743 } 2744 } 2745 2746 if (!saveConfigToSupplicant(config, netId)) { 2747 if (newNetwork) { 2748 mWifiNative.removeNetwork(netId); 2749 loge("Failed to set a network variable, removed network: " + netId); 2750 } 2751 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2752 } 2753 2754 /* An update of the network variables requires reading them 2755 * back from the supplicant to update mConfiguredNetworks. 2756 * This is because some of the variables (SSID, wep keys & 2757 * passphrases) reflect different values when read back than 2758 * when written. For example, wep key is stored as * irrespective 2759 * of the value sent to the supplicant 2760 */ 2761 WifiConfiguration currentConfig = mConfiguredNetworks.getForCurrentUser(netId); 2762 if (currentConfig == null) { 2763 currentConfig = new WifiConfiguration(); 2764 currentConfig.setIpAssignment(IpAssignment.DHCP); 2765 currentConfig.setProxySettings(ProxySettings.NONE); 2766 currentConfig.networkId = netId; 2767 if (config != null) { 2768 // Carry over the creation parameters 2769 currentConfig.selfAdded = config.selfAdded; 2770 currentConfig.didSelfAdd = config.didSelfAdd; 2771 currentConfig.ephemeral = config.ephemeral; 2772 currentConfig.lastConnectUid = config.lastConnectUid; 2773 currentConfig.lastUpdateUid = config.lastUpdateUid; 2774 currentConfig.creatorUid = config.creatorUid; 2775 currentConfig.creatorName = config.creatorName; 2776 currentConfig.lastUpdateName = config.lastUpdateName; 2777 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration; 2778 currentConfig.FQDN = config.FQDN; 2779 currentConfig.providerFriendlyName = config.providerFriendlyName; 2780 currentConfig.roamingConsortiumIds = config.roamingConsortiumIds; 2781 currentConfig.validatedInternetAccess = config.validatedInternetAccess; 2782 currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports; 2783 currentConfig.updateTime = config.updateTime; 2784 currentConfig.creationTime = config.creationTime; 2785 currentConfig.shared = config.shared; 2786 } 2787 if (DBG) { 2788 log("created new config netId=" + Integer.toString(netId) 2789 + " uid=" + Integer.toString(currentConfig.creatorUid) 2790 + " name=" + currentConfig.creatorName); 2791 } 2792 } 2793 2794 /* save HomeSP object for passpoint networks */ 2795 HomeSP homeSP = null; 2796 2797 if (!existingMO && config.isPasspoint()) { 2798 try { 2799 if (config.updateIdentifier == null) { // Only create an MO for r1 networks 2800 Credential credential = 2801 new Credential(config.enterpriseConfig, mKeyStore, !newNetwork); 2802 HashSet<Long> roamingConsortiumIds = new HashSet<Long>(); 2803 for (Long roamingConsortiumId : config.roamingConsortiumIds) { 2804 roamingConsortiumIds.add(roamingConsortiumId); 2805 } 2806 2807 homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN, 2808 roamingConsortiumIds, Collections.<String>emptySet(), 2809 Collections.<Long>emptySet(), Collections.<Long>emptyList(), 2810 config.providerFriendlyName, null, credential); 2811 2812 log("created a homeSP object for " + config.networkId + ":" + config.SSID); 2813 } 2814 2815 /* fix enterprise config properties for passpoint */ 2816 currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm()); 2817 currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn()); 2818 } 2819 catch (IOException ioe) { 2820 Log.e(TAG, "Failed to create Passpoint config: " + ioe); 2821 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2822 } 2823 } 2824 2825 if (uid != WifiConfiguration.UNKNOWN_UID) { 2826 if (newNetwork) { 2827 currentConfig.creatorUid = uid; 2828 } else { 2829 currentConfig.lastUpdateUid = uid; 2830 } 2831 } 2832 2833 // For debug, record the time the configuration was modified 2834 StringBuilder sb = new StringBuilder(); 2835 sb.append("time="); 2836 Calendar c = Calendar.getInstance(); 2837 c.setTimeInMillis(System.currentTimeMillis()); 2838 sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); 2839 2840 if (newNetwork) { 2841 currentConfig.creationTime = sb.toString(); 2842 } else { 2843 currentConfig.updateTime = sb.toString(); 2844 } 2845 2846 if (currentConfig.status == WifiConfiguration.Status.ENABLED) { 2847 // Make sure autojoin remain in sync with user modifying the configuration 2848 updateNetworkSelectionStatus(currentConfig.networkId, 2849 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 2850 } 2851 2852 if (currentConfig.configKey().equals(getLastSelectedConfiguration()) && 2853 currentConfig.ephemeral) { 2854 // Make the config non-ephemeral since the user just explicitly clicked it. 2855 currentConfig.ephemeral = false; 2856 if (DBG) log("remove ephemeral status netId=" + Integer.toString(netId) 2857 + " " + currentConfig.configKey()); 2858 } 2859 2860 if (VDBG) log("will read network variables netId=" + Integer.toString(netId)); 2861 2862 readNetworkVariables(currentConfig); 2863 2864 // Persist configuration paramaters that are not saved by supplicant. 2865 if (config.lastUpdateName != null) { 2866 currentConfig.lastUpdateName = config.lastUpdateName; 2867 } 2868 if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) { 2869 currentConfig.lastUpdateUid = config.lastUpdateUid; 2870 } 2871 2872 mConfiguredNetworks.put(currentConfig); 2873 2874 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config); 2875 result.setIsNewNetwork(newNetwork); 2876 result.setNetworkId(netId); 2877 2878 if (homeSP != null) { 2879 writePasspointConfigs(null, homeSP); 2880 } 2881 2882 writeKnownNetworkHistory(); 2883 2884 return result; 2885 } 2886 2887 public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) { 2888 WifiConfiguration config = mConfiguredNetworks.getByFQDNForCurrentUser(homeSP.getFQDN()); 2889 if (config == null) { 2890 Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN()); 2891 } 2892 return config; 2893 } 2894 2895 public HomeSP getHomeSPForConfig(WifiConfiguration config) { 2896 WifiConfiguration storedConfig = mConfiguredNetworks.getForCurrentUser(config.networkId); 2897 return storedConfig != null && storedConfig.isPasspoint() ? 2898 mMOManager.getHomeSP(storedConfig.FQDN) : null; 2899 } 2900 2901 public ScanDetailCache getScanDetailCache(WifiConfiguration config) { 2902 if (config == null) return null; 2903 ScanDetailCache cache = mScanDetailCaches.get(config.networkId); 2904 if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { 2905 cache = new ScanDetailCache(config); 2906 mScanDetailCaches.put(config.networkId, cache); 2907 } 2908 return cache; 2909 } 2910 2911 /** 2912 * This function run thru the Saved WifiConfigurations and check if some should be linked. 2913 * @param config 2914 */ 2915 public void linkConfiguration(WifiConfiguration config) { 2916 if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, 2917 mWifiStateMachine.getCurrentUserProfiles())) { 2918 loge("linkConfiguration: Attempting to link config " + config.configKey() 2919 + " that is not visible to the current user."); 2920 return; 2921 } 2922 2923 if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) { 2924 // Ignore configurations with large number of BSSIDs 2925 return; 2926 } 2927 if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 2928 // Only link WPA_PSK config 2929 return; 2930 } 2931 for (WifiConfiguration link : mConfiguredNetworks.valuesForCurrentUser()) { 2932 boolean doLink = false; 2933 2934 if (link.configKey().equals(config.configKey())) { 2935 continue; 2936 } 2937 2938 if (link.ephemeral) { 2939 continue; 2940 } 2941 2942 // Autojoin will be allowed to dynamically jump from a linked configuration 2943 // to another, hence only link configurations that have equivalent level of security 2944 if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) { 2945 continue; 2946 } 2947 2948 ScanDetailCache linkedScanDetailCache = getScanDetailCache(link); 2949 if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) { 2950 // Ignore configurations with large number of BSSIDs 2951 continue; 2952 } 2953 2954 if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) { 2955 // If both default GW are known, link only if they are equal 2956 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) { 2957 if (VDBG) { 2958 loge("linkConfiguration link due to same gw " + link.SSID + 2959 " and " + config.SSID + " GW " + config.defaultGwMacAddress); 2960 } 2961 doLink = true; 2962 } 2963 } else { 2964 // We do not know BOTH default gateways hence we will try to link 2965 // hoping that WifiConfigurations are indeed behind the same gateway. 2966 // once both WifiConfiguration have been tried and thus once both efault gateways 2967 // are known we will revisit the choice of linking them 2968 if ((getScanDetailCache(config) != null) 2969 && (getScanDetailCache(config).size() <= 6)) { 2970 2971 for (String abssid : getScanDetailCache(config).keySet()) { 2972 for (String bbssid : linkedScanDetailCache.keySet()) { 2973 if (VVDBG) { 2974 loge("linkConfiguration try to link due to DBDC BSSID match " 2975 + link.SSID + 2976 " and " + config.SSID + " bssida " + abssid 2977 + " bssidb " + bbssid); 2978 } 2979 if (abssid.regionMatches(true, 0, bbssid, 0, 16)) { 2980 // If first 16 ascii characters of BSSID matches, 2981 // we assume this is a DBDC 2982 doLink = true; 2983 } 2984 } 2985 } 2986 } 2987 } 2988 2989 if (doLink == true && onlyLinkSameCredentialConfigurations) { 2990 String apsk = readNetworkVariableFromSupplicantFile(link.SSID, "psk"); 2991 String bpsk = readNetworkVariableFromSupplicantFile(config.SSID, "psk"); 2992 if (apsk == null || bpsk == null 2993 || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk) 2994 || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK) 2995 || !apsk.equals(bpsk)) { 2996 doLink = false; 2997 } 2998 } 2999 3000 if (doLink) { 3001 if (VDBG) { 3002 loge("linkConfiguration: will link " + link.configKey() 3003 + " and " + config.configKey()); 3004 } 3005 if (link.linkedConfigurations == null) { 3006 link.linkedConfigurations = new HashMap<String, Integer>(); 3007 } 3008 if (config.linkedConfigurations == null) { 3009 config.linkedConfigurations = new HashMap<String, Integer>(); 3010 } 3011 if (link.linkedConfigurations.get(config.configKey()) == null) { 3012 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 3013 } 3014 if (config.linkedConfigurations.get(link.configKey()) == null) { 3015 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 3016 } 3017 } else { 3018 if (link.linkedConfigurations != null 3019 && (link.linkedConfigurations.get(config.configKey()) != null)) { 3020 if (VDBG) { 3021 loge("linkConfiguration: un-link " + config.configKey() 3022 + " from " + link.configKey()); 3023 } 3024 link.linkedConfigurations.remove(config.configKey()); 3025 } 3026 if (config.linkedConfigurations != null 3027 && (config.linkedConfigurations.get(link.configKey()) != null)) { 3028 if (VDBG) { 3029 loge("linkConfiguration: un-link " + link.configKey() 3030 + " from " + config.configKey()); 3031 } 3032 config.linkedConfigurations.remove(link.configKey()); 3033 } 3034 } 3035 } 3036 } 3037 3038 public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) { 3039 if (config == null) 3040 return null; 3041 long now_ms = System.currentTimeMillis(); 3042 3043 HashSet<Integer> channels = new HashSet<Integer>(); 3044 3045 //get channels for this configuration, if there are at least 2 BSSIDs 3046 if (getScanDetailCache(config) == null && config.linkedConfigurations == null) { 3047 return null; 3048 } 3049 3050 if (VDBG) { 3051 StringBuilder dbg = new StringBuilder(); 3052 dbg.append("makeChannelList age=" + Integer.toString(age) 3053 + " for " + config.configKey() 3054 + " max=" + maxNumActiveChannelsForPartialScans); 3055 if (getScanDetailCache(config) != null) { 3056 dbg.append(" bssids=" + getScanDetailCache(config).size()); 3057 } 3058 if (config.linkedConfigurations != null) { 3059 dbg.append(" linked=" + config.linkedConfigurations.size()); 3060 } 3061 loge(dbg.toString()); 3062 } 3063 3064 int numChannels = 0; 3065 if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) { 3066 for (ScanDetail scanDetail : getScanDetailCache(config).values()) { 3067 ScanResult result = scanDetail.getScanResult(); 3068 //TODO : cout active and passive channels separately 3069 if (numChannels > maxNumActiveChannelsForPartialScans.get()) { 3070 break; 3071 } 3072 if (VDBG) { 3073 boolean test = (now_ms - result.seen) < age; 3074 loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency) 3075 + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test); 3076 } 3077 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 3078 channels.add(result.frequency); 3079 numChannels++; 3080 } 3081 } 3082 } 3083 3084 //get channels for linked configurations 3085 if (config.linkedConfigurations != null) { 3086 for (String key : config.linkedConfigurations.keySet()) { 3087 WifiConfiguration linked = getWifiConfiguration(key); 3088 if (linked == null) 3089 continue; 3090 if (getScanDetailCache(linked) == null) { 3091 continue; 3092 } 3093 for (ScanDetail scanDetail : getScanDetailCache(linked).values()) { 3094 ScanResult result = scanDetail.getScanResult(); 3095 if (VDBG) { 3096 loge("has link: " + result.BSSID 3097 + " freq=" + Integer.toString(result.frequency) 3098 + " age=" + Long.toString(now_ms - result.seen)); 3099 } 3100 if (numChannels > maxNumActiveChannelsForPartialScans.get()) { 3101 break; 3102 } 3103 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 3104 channels.add(result.frequency); 3105 numChannels++; 3106 } 3107 } 3108 } 3109 } 3110 return channels; 3111 } 3112 3113 private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) { 3114 if (!mMOManager.isConfigured()) { 3115 if (mEnableOsuQueries) { 3116 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 3117 List<Constants.ANQPElementType> querySet = 3118 ANQPFactory.buildQueryList(networkDetail, false, true); 3119 3120 if (networkDetail.queriable(querySet)) { 3121 querySet = mAnqpCache.initiate(networkDetail, querySet); 3122 if (querySet != null) { 3123 mSupplicantBridge.startANQP(scanDetail, querySet); 3124 } 3125 updateAnqpCache(scanDetail, networkDetail.getANQPElements()); 3126 } 3127 } 3128 return null; 3129 } 3130 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 3131 if (!networkDetail.hasInterworking()) { 3132 return null; 3133 } 3134 updateAnqpCache(scanDetail, networkDetail.getANQPElements()); 3135 3136 Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true); 3137 Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() + 3138 " pass 1 matches: " + toMatchString(matches)); 3139 return matches; 3140 } 3141 3142 private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) { 3143 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 3144 3145 ANQPData anqpData = mAnqpCache.getEntry(networkDetail); 3146 3147 Map<Constants.ANQPElementType, ANQPElement> anqpElements = 3148 anqpData != null ? anqpData.getANQPElements() : null; 3149 3150 boolean queried = !query; 3151 Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values(); 3152 Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size()); 3153 Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString() + 3154 ", anqp " + ( anqpData != null ? "present" : "missing" ) + 3155 ", query " + query + ", home sps: " + homeSPs.size()); 3156 3157 for (HomeSP homeSP : homeSPs) { 3158 PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor); 3159 3160 Log.d(Utils.hs2LogTag(getClass()), " -- " + 3161 homeSP.getFQDN() + ": match " + match + ", queried " + queried); 3162 3163 if ((match == PasspointMatch.Incomplete || mEnableOsuQueries) && !queried) { 3164 boolean matchSet = match == PasspointMatch.Incomplete; 3165 boolean osu = mEnableOsuQueries; 3166 List<Constants.ANQPElementType> querySet = 3167 ANQPFactory.buildQueryList(networkDetail, matchSet, osu); 3168 if (networkDetail.queriable(querySet)) { 3169 querySet = mAnqpCache.initiate(networkDetail, querySet); 3170 if (querySet != null) { 3171 mSupplicantBridge.startANQP(scanDetail, querySet); 3172 } 3173 } 3174 queried = true; 3175 } 3176 matches.put(homeSP, match); 3177 } 3178 return matches; 3179 } 3180 3181 public Map<Constants.ANQPElementType, ANQPElement> getANQPData(NetworkDetail network) { 3182 ANQPData data = mAnqpCache.getEntry(network); 3183 return data != null ? data.getANQPElements() : null; 3184 } 3185 3186 public SIMAccessor getSIMAccessor() { 3187 return mSIMAccessor; 3188 } 3189 3190 public void notifyANQPDone(Long bssid, boolean success) { 3191 mSupplicantBridge.notifyANQPDone(bssid, success); 3192 } 3193 3194 public void notifyIconReceived(IconEvent iconEvent) { 3195 Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION); 3196 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3197 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, iconEvent.getBSSID()); 3198 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_FILE, iconEvent.getFileName()); 3199 try { 3200 intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_DATA, 3201 mSupplicantBridge.retrieveIcon(iconEvent)); 3202 } catch (IOException ioe) { 3203 /* Simply omit the icon data as a failure indication */ 3204 } 3205 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 3206 3207 } 3208 3209 public void wnmFrameReceived(WnmData event) { 3210 // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url 3211 // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url 3212 3213 Intent intent = new Intent(WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION); 3214 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3215 3216 intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_BSSID, event.getBssid()); 3217 intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_URL, event.getUrl()); 3218 3219 if (event.isDeauthEvent()) { 3220 intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_ESS, event.isEss()); 3221 intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_DELAY, event.getDelay()); 3222 } else { 3223 intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_METHOD, event.getMethod()); 3224 WifiConfiguration config = mWifiStateMachine.getCurrentWifiConfiguration(); 3225 if (config != null && config.FQDN != null) { 3226 intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH, 3227 matchProviderWithCurrentNetwork(config.FQDN)); 3228 } 3229 } 3230 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 3231 } 3232 3233 private void updateAnqpCache(ScanDetail scanDetail, 3234 Map<Constants.ANQPElementType,ANQPElement> anqpElements) 3235 { 3236 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 3237 3238 if (anqpElements == null) { 3239 // Try to pull cached data if query failed. 3240 ANQPData data = mAnqpCache.getEntry(networkDetail); 3241 if (data != null) { 3242 scanDetail.propagateANQPInfo(data.getANQPElements()); 3243 } 3244 return; 3245 } 3246 3247 mAnqpCache.update(networkDetail, anqpElements); 3248 } 3249 3250 private static String toMatchString(Map<HomeSP, PasspointMatch> matches) { 3251 StringBuilder sb = new StringBuilder(); 3252 for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) { 3253 sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue()); 3254 } 3255 return sb.toString(); 3256 } 3257 3258 private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail, 3259 Map<HomeSP, PasspointMatch> matches, 3260 List<WifiConfiguration> associatedWifiConfigurations) { 3261 3262 for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) { 3263 PasspointMatch match = entry.getValue(); 3264 if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) { 3265 WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey()); 3266 if (config != null) { 3267 cacheScanResultForConfig(config, scanDetail, entry.getValue()); 3268 if (associatedWifiConfigurations != null) { 3269 associatedWifiConfigurations.add(config); 3270 } 3271 } else { 3272 Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '" + 3273 entry.getKey().getFQDN() + "'"); 3274 /* perhaps the configuration was deleted?? */ 3275 } 3276 } 3277 } 3278 } 3279 3280 private void cacheScanResultForConfig( 3281 WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) { 3282 3283 ScanResult scanResult = scanDetail.getScanResult(); 3284 3285 ScanDetailCache scanDetailCache = getScanDetailCache(config); 3286 if (scanDetailCache == null) { 3287 Log.w(TAG, "Could not allocate scan cache for " + config.SSID); 3288 return; 3289 } 3290 3291 // Adding a new BSSID 3292 ScanResult result = scanDetailCache.get(scanResult.BSSID); 3293 if (result != null) { 3294 // transfer the black list status 3295 scanResult.blackListTimestamp = result.blackListTimestamp; 3296 scanResult.numIpConfigFailures = result.numIpConfigFailures; 3297 scanResult.numConnection = result.numConnection; 3298 scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate; 3299 } 3300 3301 if (config.ephemeral) { 3302 // For an ephemeral Wi-Fi config, the ScanResult should be considered 3303 // untrusted. 3304 scanResult.untrusted = true; 3305 } 3306 3307 if (scanDetailCache.size() > (maxNumScanCacheEntries + 64)) { 3308 long now_dbg = 0; 3309 if (VVDBG) { 3310 loge(" Will trim config " + config.configKey() 3311 + " size " + scanDetailCache.size()); 3312 3313 for (ScanDetail sd : scanDetailCache.values()) { 3314 loge(" " + sd.getBSSIDString() + " " + sd.getSeen()); 3315 } 3316 now_dbg = SystemClock.elapsedRealtimeNanos(); 3317 } 3318 // Trim the scan result cache to maxNumScanCacheEntries entries max 3319 // Since this operation is expensive, make sure it is not performed 3320 // until the cache has grown significantly above the trim treshold 3321 scanDetailCache.trim(maxNumScanCacheEntries); 3322 if (VVDBG) { 3323 long diff = SystemClock.elapsedRealtimeNanos() - now_dbg; 3324 loge(" Finished trimming config, time(ns) " + diff); 3325 for (ScanDetail sd : scanDetailCache.values()) { 3326 loge(" " + sd.getBSSIDString() + " " + sd.getSeen()); 3327 } 3328 } 3329 } 3330 3331 // Add the scan result to this WifiConfiguration 3332 if (passpointMatch != null) 3333 scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config)); 3334 else 3335 scanDetailCache.put(scanDetail); 3336 3337 // Since we added a scan result to this configuration, re-attempt linking 3338 linkConfiguration(config); 3339 } 3340 3341 private boolean isEncryptionWep(String encryption) { 3342 return encryption.contains("WEP"); 3343 } 3344 3345 private boolean isEncryptionPsk(String encryption) { 3346 return encryption.contains("PSK"); 3347 } 3348 3349 private boolean isEncryptionEap(String encryption) { 3350 return encryption.contains("EAP"); 3351 } 3352 3353 public boolean isOpenNetwork(String encryption) { 3354 if (!isEncryptionWep(encryption) && !isEncryptionPsk(encryption) 3355 && !isEncryptionEap(encryption)) { 3356 return true; 3357 } 3358 return false; 3359 } 3360 3361 public boolean isOpenNetwork(ScanResult scan) { 3362 String scanResultEncrypt = scan.capabilities; 3363 return isOpenNetwork(scanResultEncrypt); 3364 } 3365 3366 public boolean isOpenNetwork(WifiConfiguration config) { 3367 String configEncrypt = config.configKey(); 3368 return isOpenNetwork(configEncrypt); 3369 } 3370 3371 /** 3372 * create a mapping between the scandetail and the Wificonfiguration it associated with 3373 * because Passpoint, one BSSID can associated with multiple SSIDs 3374 * @param scanDetail input a scanDetail from the scan result 3375 * @return List<WifiConfiguration> a list of WifiConfigurations associated to this scanDetail 3376 */ 3377 public List<WifiConfiguration> updateSavedNetworkWithNewScanDetail(ScanDetail scanDetail) { 3378 3379 ScanResult scanResult = scanDetail.getScanResult(); 3380 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 3381 List<WifiConfiguration> associatedWifiConfigurations = new ArrayList<WifiConfiguration>(); 3382 3383 if (scanResult == null) 3384 return null; 3385 3386 String SSID = "\"" + scanResult.SSID + "\""; 3387 3388 if (networkDetail.hasInterworking()) { 3389 Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail); 3390 if (matches != null) { 3391 cacheScanResultForPasspointConfigs(scanDetail, matches, 3392 associatedWifiConfigurations); 3393 //Do not return here. A BSSID can belong to both passpoint network and non-passpoint 3394 //Network 3395 } 3396 } 3397 3398 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 3399 boolean found = false; 3400 if (config.SSID == null || !config.SSID.equals(SSID)) { 3401 continue; 3402 } 3403 if (DBG) { 3404 localLog("updateSavedNetworkWithNewScanDetail(): try " + config.configKey() 3405 + " SSID=" + config.SSID + " " + scanResult.SSID + " " 3406 + scanResult.capabilities); 3407 } 3408 3409 String scanResultEncrypt = scanResult.capabilities; 3410 String configEncrypt = config.configKey(); 3411 if (isEncryptionWep(scanResultEncrypt) && isEncryptionWep(configEncrypt) 3412 || (isEncryptionPsk(scanResultEncrypt) && isEncryptionPsk(configEncrypt)) 3413 || (isEncryptionEap(scanResultEncrypt) && isEncryptionEap(configEncrypt)) 3414 || (isOpenNetwork(scanResultEncrypt) && isOpenNetwork(configEncrypt))) { 3415 found = true; 3416 } 3417 3418 if (found) { 3419 cacheScanResultForConfig(config, scanDetail, null); 3420 associatedWifiConfigurations.add(config); 3421 } 3422 } 3423 3424 if (associatedWifiConfigurations.size() == 0) { 3425 return null; 3426 } else { 3427 return associatedWifiConfigurations; 3428 } 3429 } 3430 3431 /** 3432 * Handles the switch to a different foreground user: 3433 * - Removes all ephemeral networks 3434 * - Disables private network configurations belonging to the previous foreground user 3435 * - Enables private network configurations belonging to the new foreground user 3436 * 3437 * TODO(b/26785736): Terminate background users if the new foreground user has one or more 3438 * private network configurations. 3439 */ 3440 public void handleUserSwitch() { 3441 Set<WifiConfiguration> ephemeralConfigs = new HashSet<>(); 3442 for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) { 3443 if (config.ephemeral) { 3444 ephemeralConfigs.add(config); 3445 } 3446 } 3447 if (!ephemeralConfigs.isEmpty()) { 3448 for (WifiConfiguration config : ephemeralConfigs) { 3449 if (config.configKey().equals(lastSelectedConfiguration)) { 3450 lastSelectedConfiguration = null; 3451 } 3452 if (config.enterpriseConfig != null) { 3453 removeKeys(config.enterpriseConfig); 3454 } 3455 mConfiguredNetworks.remove(config.networkId); 3456 mScanDetailCaches.remove(config.networkId); 3457 mWifiNative.removeNetwork(config.networkId); 3458 } 3459 mWifiNative.saveConfig(); 3460 3461 writeKnownNetworkHistory(); 3462 } 3463 3464 final List<WifiConfiguration> hiddenConfigurations = 3465 mConfiguredNetworks.handleUserSwitch(mWifiStateMachine.getCurrentUserId()); 3466 for (WifiConfiguration network : hiddenConfigurations) { 3467 disableNetworkNative(network); 3468 } 3469 enableAllNetworks(); 3470 3471 // TODO(b/26785746): This broadcast is unnecessary if either of the following is true: 3472 // * The user switch did not change the list of visible networks 3473 // * The user switch revealed additional networks that were temporarily disabled and got 3474 // re-enabled now (because enableAllNetworks() sent the same broadcast already). 3475 sendConfiguredNetworksChangedBroadcast(); 3476 } 3477 3478 /* Compare current and new configuration and write to file on change */ 3479 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 3480 WifiConfiguration currentConfig, 3481 WifiConfiguration newConfig) { 3482 boolean ipChanged = false; 3483 boolean proxyChanged = false; 3484 3485 if (VDBG) { 3486 loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " + 3487 newConfig.SSID + " path: " + ipConfigFile); 3488 } 3489 3490 3491 switch (newConfig.getIpAssignment()) { 3492 case STATIC: 3493 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 3494 ipChanged = true; 3495 } else { 3496 ipChanged = !Objects.equals( 3497 currentConfig.getStaticIpConfiguration(), 3498 newConfig.getStaticIpConfiguration()); 3499 } 3500 break; 3501 case DHCP: 3502 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 3503 ipChanged = true; 3504 } 3505 break; 3506 case UNASSIGNED: 3507 /* Ignore */ 3508 break; 3509 default: 3510 loge("Ignore invalid ip assignment during write"); 3511 break; 3512 } 3513 3514 switch (newConfig.getProxySettings()) { 3515 case STATIC: 3516 case PAC: 3517 ProxyInfo newHttpProxy = newConfig.getHttpProxy(); 3518 ProxyInfo currentHttpProxy = currentConfig.getHttpProxy(); 3519 3520 if (newHttpProxy != null) { 3521 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 3522 } else { 3523 proxyChanged = (currentHttpProxy != null); 3524 } 3525 break; 3526 case NONE: 3527 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) { 3528 proxyChanged = true; 3529 } 3530 break; 3531 case UNASSIGNED: 3532 /* Ignore */ 3533 break; 3534 default: 3535 loge("Ignore invalid proxy configuration during write"); 3536 break; 3537 } 3538 3539 if (ipChanged) { 3540 currentConfig.setIpAssignment(newConfig.getIpAssignment()); 3541 currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration()); 3542 log("IP config changed SSID = " + currentConfig.SSID); 3543 if (currentConfig.getStaticIpConfiguration() != null) { 3544 log(" static configuration: " + 3545 currentConfig.getStaticIpConfiguration().toString()); 3546 } 3547 } 3548 3549 if (proxyChanged) { 3550 currentConfig.setProxySettings(newConfig.getProxySettings()); 3551 currentConfig.setHttpProxy(newConfig.getHttpProxy()); 3552 log("proxy changed SSID = " + currentConfig.SSID); 3553 if (currentConfig.getHttpProxy() != null) { 3554 log(" proxyProperties: " + currentConfig.getHttpProxy().toString()); 3555 } 3556 } 3557 3558 if (ipChanged || proxyChanged) { 3559 writeIpAndProxyConfigurations(); 3560 sendConfiguredNetworksChangedBroadcast(currentConfig, 3561 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 3562 } 3563 return new NetworkUpdateResult(ipChanged, proxyChanged); 3564 } 3565 3566 /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */ 3567 private boolean enterpriseConfigKeyShouldBeQuoted(String key) { 3568 switch (key) { 3569 case WifiEnterpriseConfig.EAP_KEY: 3570 case WifiEnterpriseConfig.ENGINE_KEY: 3571 return false; 3572 default: 3573 return true; 3574 } 3575 } 3576 3577 /** 3578 * Read the variables from the supplicant daemon that are needed to 3579 * fill in the WifiConfiguration object. 3580 * 3581 * @param config the {@link WifiConfiguration} object to be filled in. 3582 */ 3583 private void readNetworkVariables(WifiConfiguration config) { 3584 3585 int netId = config.networkId; 3586 if (netId < 0) 3587 return; 3588 3589 /* 3590 * TODO: maybe should have a native method that takes an array of 3591 * variable names and returns an array of values. But we'd still 3592 * be doing a round trip to the supplicant daemon for each variable. 3593 */ 3594 String value; 3595 3596 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 3597 if (!TextUtils.isEmpty(value)) { 3598 if (value.charAt(0) != '"') { 3599 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 3600 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 3601 //supplicant string 3602 } else { 3603 config.SSID = value; 3604 } 3605 } else { 3606 config.SSID = null; 3607 } 3608 3609 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 3610 if (!TextUtils.isEmpty(value)) { 3611 config.getNetworkSelectionStatus().setNetworkSelectionBSSID(value); 3612 } else { 3613 config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null); 3614 } 3615 3616 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 3617 config.priority = -1; 3618 if (!TextUtils.isEmpty(value)) { 3619 try { 3620 config.priority = Integer.parseInt(value); 3621 } catch (NumberFormatException ignore) { 3622 } 3623 } 3624 3625 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 3626 config.hiddenSSID = false; 3627 if (!TextUtils.isEmpty(value)) { 3628 try { 3629 config.hiddenSSID = Integer.parseInt(value) != 0; 3630 } catch (NumberFormatException ignore) { 3631 } 3632 } 3633 3634 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 3635 config.wepTxKeyIndex = -1; 3636 if (!TextUtils.isEmpty(value)) { 3637 try { 3638 config.wepTxKeyIndex = Integer.parseInt(value); 3639 } catch (NumberFormatException ignore) { 3640 } 3641 } 3642 3643 for (int i = 0; i < 4; i++) { 3644 value = mWifiNative.getNetworkVariable(netId, 3645 WifiConfiguration.wepKeyVarNames[i]); 3646 if (!TextUtils.isEmpty(value)) { 3647 config.wepKeys[i] = value; 3648 } else { 3649 config.wepKeys[i] = null; 3650 } 3651 } 3652 3653 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 3654 if (!TextUtils.isEmpty(value)) { 3655 config.preSharedKey = value; 3656 } else { 3657 config.preSharedKey = null; 3658 } 3659 3660 readNetworkBitsetVariable(config.networkId, config.allowedProtocols, 3661 WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings); 3662 3663 readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement, 3664 WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings); 3665 3666 readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms, 3667 WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings); 3668 3669 readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers, 3670 WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings); 3671 3672 readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers, 3673 WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings); 3674 3675 if (config.enterpriseConfig == null) { 3676 config.enterpriseConfig = new WifiEnterpriseConfig(); 3677 } 3678 config.enterpriseConfig.loadFromSupplicant(new SupplicantLoader(netId)); 3679 3680 if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) { 3681 saveConfig(); 3682 } 3683 3684 migrateCerts(config.enterpriseConfig); 3685 // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore); 3686 } 3687 3688 private static String removeDoubleQuotes(String string) { 3689 int length = string.length(); 3690 if ((length > 1) && (string.charAt(0) == '"') 3691 && (string.charAt(length - 1) == '"')) { 3692 return string.substring(1, length - 1); 3693 } 3694 return string; 3695 } 3696 3697 private static String makeString(BitSet set, String[] strings) { 3698 StringBuffer buf = new StringBuffer(); 3699 int nextSetBit = -1; 3700 3701 /* Make sure all set bits are in [0, strings.length) to avoid 3702 * going out of bounds on strings. (Shouldn't happen, but...) */ 3703 set = set.get(0, strings.length); 3704 3705 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 3706 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 3707 } 3708 3709 // remove trailing space 3710 if (set.cardinality() > 0) { 3711 buf.setLength(buf.length() - 1); 3712 } 3713 3714 return buf.toString(); 3715 } 3716 3717 private int lookupString(String string, String[] strings) { 3718 int size = strings.length; 3719 3720 string = string.replace('-', '_'); 3721 3722 for (int i = 0; i < size; i++) 3723 if (string.equals(strings[i])) 3724 return i; 3725 3726 // if we ever get here, we should probably add the 3727 // value to WifiConfiguration to reflect that it's 3728 // supported by the WPA supplicant 3729 loge("Failed to look-up a string: " + string); 3730 3731 return -1; 3732 } 3733 3734 /* return the allowed key management based on a scan result */ 3735 3736 public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 3737 3738 WifiConfiguration config = new WifiConfiguration(); 3739 3740 config.SSID = "\"" + result.SSID + "\""; 3741 3742 if (VDBG) { 3743 loge("WifiConfiguration from scan results " + 3744 config.SSID + " cap " + result.capabilities); 3745 } 3746 if (result.capabilities.contains("WEP")) { 3747 config.allowedKeyManagement.set(KeyMgmt.NONE); 3748 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //? 3749 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 3750 } 3751 3752 if (result.capabilities.contains("PSK")) { 3753 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 3754 } 3755 3756 if (result.capabilities.contains("EAP")) { 3757 //this is probably wrong, as we don't have a way to enter the enterprise config 3758 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 3759 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 3760 } 3761 3762 /* getScanDetailCache(config).put(scanDetail); */ 3763 3764 return config; 3765 } 3766 3767 public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) { 3768 ScanResult result = scanDetail.getScanResult(); 3769 return wifiConfigurationFromScanResult(result); 3770 } 3771 3772 /* Returns a unique for a given configuration */ 3773 private static int configKey(WifiConfiguration config) { 3774 String key = config.configKey(); 3775 return key.hashCode(); 3776 } 3777 3778 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3779 pw.println("Dump of WifiConfigManager"); 3780 pw.println("mLastPriority " + mLastPriority); 3781 pw.println("Configured networks"); 3782 for (WifiConfiguration conf : getAllConfiguredNetworks()) { 3783 pw.println(conf); 3784 } 3785 pw.println(); 3786 if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) { 3787 pw.println("LostConfigs: "); 3788 for (String s : mLostConfigsDbg) { 3789 pw.println(s); 3790 } 3791 } 3792 if (mLocalLog != null) { 3793 pw.println("WifiConfigManager - Log Begin ----"); 3794 mLocalLog.dump(fd, pw, args); 3795 pw.println("WifiConfigManager - Log End ----"); 3796 } 3797 if (mMOManager.isConfigured()) { 3798 pw.println("Begin dump of ANQP Cache"); 3799 mAnqpCache.dump(pw); 3800 pw.println("End dump of ANQP Cache"); 3801 } 3802 } 3803 3804 public String getConfigFile() { 3805 return ipConfigFile; 3806 } 3807 3808 protected void logd(String s) { 3809 Log.d(TAG, s); 3810 } 3811 3812 protected void loge(String s) { 3813 loge(s, false); 3814 } 3815 3816 protected void loge(String s, boolean stack) { 3817 if (stack) { 3818 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 3819 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 3820 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 3821 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 3822 } else { 3823 Log.e(TAG, s); 3824 } 3825 } 3826 3827 private void logKernelTime() { 3828 long kernelTimeMs = System.nanoTime()/(1000*1000); 3829 StringBuilder builder = new StringBuilder(); 3830 builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append 3831 (kernelTimeMs%1000).append("\n"); 3832 localLog(builder.toString()); 3833 } 3834 3835 protected void log(String s) { 3836 Log.d(TAG, s); 3837 } 3838 3839 private void localLog(String s) { 3840 if (mLocalLog != null) { 3841 mLocalLog.log(s); 3842 } 3843 } 3844 3845 private void localLogAndLogcat(String s) { 3846 localLog(s); 3847 Log.d(TAG, s); 3848 } 3849 3850 private void localLogNetwork(String s, int netId) { 3851 if (mLocalLog == null) { 3852 return; 3853 } 3854 3855 WifiConfiguration config; 3856 synchronized(mConfiguredNetworks) { // !!! Useless synchronization 3857 config = mConfiguredNetworks.getForAllUsers(netId); 3858 } 3859 3860 if (config != null) { 3861 mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId 3862 + " status=" + config.status 3863 + " key=" + config.configKey()); 3864 } else { 3865 mLocalLog.log(s + " " + netId); 3866 } 3867 } 3868 3869 // Certificate and private key management for EnterpriseConfig 3870 static boolean needsKeyStore(WifiEnterpriseConfig config) { 3871 // Has no keys to be installed 3872 if (config.getClientCertificate() == null && config.getCaCertificate() == null) 3873 return false; 3874 return true; 3875 } 3876 3877 static boolean isHardwareBackedKey(PrivateKey key) { 3878 return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); 3879 } 3880 3881 static boolean hasHardwareBackedKey(Certificate certificate) { 3882 return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); 3883 } 3884 3885 static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 3886 String client = config.getClientCertificateAlias(); 3887 if (!TextUtils.isEmpty(client)) { 3888 // a valid client certificate is configured 3889 3890 // BUGBUG: keyStore.get() never returns certBytes; because it is not 3891 // taking WIFI_UID as a parameter. It always looks for certificate 3892 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 3893 // all certificates need software keystore until we get the get() API 3894 // fixed. 3895 3896 return true; 3897 } 3898 3899 /* 3900 try { 3901 3902 if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials 3903 .USER_CERTIFICATE + client); 3904 3905 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 3906 if (factory == null) { 3907 Slog.e(TAG, "Error getting certificate factory"); 3908 return; 3909 } 3910 3911 byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); 3912 if (certBytes != null) { 3913 Certificate cert = (X509Certificate) factory.generateCertificate( 3914 new ByteArrayInputStream(certBytes)); 3915 3916 if (cert != null) { 3917 mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); 3918 3919 if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials 3920 .USER_CERTIFICATE + client); 3921 if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : 3922 "does not need" ) + " software key store"); 3923 } else { 3924 Slog.d(TAG, "could not generate certificate"); 3925 } 3926 } else { 3927 Slog.e(TAG, "Could not load client certificate " + Credentials 3928 .USER_CERTIFICATE + client); 3929 mNeedsSoftwareKeystore = true; 3930 } 3931 3932 } catch(CertificateException e) { 3933 Slog.e(TAG, "Could not read certificates"); 3934 mCaCert = null; 3935 mClientCertificate = null; 3936 } 3937 */ 3938 3939 return false; 3940 } 3941 3942 boolean isSimConfig(WifiConfiguration config) { 3943 if (config == null) { 3944 return false; 3945 } 3946 3947 if (config.enterpriseConfig == null) { 3948 return false; 3949 } 3950 3951 int method = config.enterpriseConfig.getEapMethod(); 3952 return (method == WifiEnterpriseConfig.Eap.SIM 3953 || method == WifiEnterpriseConfig.Eap.AKA 3954 || method == WifiEnterpriseConfig.Eap.AKA_PRIME); 3955 } 3956 3957 void resetSimNetworks() { 3958 for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { 3959 if (isSimConfig(config)) { 3960 /* This configuration may have cached Pseudonym IDs; lets remove them */ 3961 mWifiNative.setNetworkVariable(config.networkId, "identity", "NULL"); 3962 mWifiNative.setNetworkVariable(config.networkId, "anonymous_identity", "NULL"); 3963 } 3964 } 3965 } 3966 3967 boolean isNetworkConfigured(WifiConfiguration config) { 3968 // Check if either we have a network Id or a WifiConfiguration 3969 // matching the one we are trying to add. 3970 3971 if(config.networkId != INVALID_NETWORK_ID) { 3972 return (mConfiguredNetworks.getForCurrentUser(config.networkId) != null); 3973 } 3974 3975 return (mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()) != null); 3976 } 3977 3978 /** 3979 * Checks if uid has access to modify the configuration corresponding to networkId. 3980 * 3981 * The conditions checked are, in descending priority order: 3982 * - Disallow modification if the the configuration is not visible to the uid. 3983 * - Allow modification if the uid represents the Device Owner app. 3984 * - Allow modification if both of the following are true: 3985 * - The uid represents the configuration's creator or an app holding OVERRIDE_CONFIG_WIFI. 3986 * - The modification is only for administrative annotation (e.g. when connecting) or the 3987 * configuration is not lockdown eligible (which currently means that it was not last 3988 * updated by the DO). 3989 * - Allow modification if configuration lockdown is explicitly disabled and the uid represents 3990 * an app holding OVERRIDE_CONFIG_WIFI. 3991 * - In all other cases, disallow modification. 3992 */ 3993 boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) { 3994 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(networkId); 3995 3996 if (config == null) { 3997 loge("canModifyNetwork: cannot find config networkId " + networkId); 3998 return false; 3999 } 4000 4001 final DevicePolicyManagerInternal dpmi = LocalServices.getService( 4002 DevicePolicyManagerInternal.class); 4003 4004 final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid, 4005 DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 4006 4007 if (isUidDeviceOwner) { 4008 return true; 4009 } 4010 4011 final boolean isCreator = (config.creatorUid == uid); 4012 4013 if (onlyAnnotate) { 4014 return isCreator || checkConfigOverridePermission(uid); 4015 } 4016 4017 // Check if device has DPM capability. If it has and dpmi is still null, then we 4018 // treat this case with suspicion and bail out. 4019 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) 4020 && dpmi == null) { 4021 return false; 4022 } 4023 4024 // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner. 4025 4026 final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy( 4027 config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); 4028 if (!isConfigEligibleForLockdown) { 4029 return isCreator || checkConfigOverridePermission(uid); 4030 } 4031 4032 final ContentResolver resolver = mContext.getContentResolver(); 4033 final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver, 4034 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0; 4035 return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid); 4036 } 4037 4038 /** 4039 * Checks if uid has access to modify config. 4040 */ 4041 boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) { 4042 if (config == null) { 4043 loge("canModifyNetowrk recieved null configuration"); 4044 return false; 4045 } 4046 4047 // Resolve the correct network id. 4048 int netid; 4049 if (config.networkId != INVALID_NETWORK_ID){ 4050 netid = config.networkId; 4051 } else { 4052 WifiConfiguration test = 4053 mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()); 4054 if (test == null) { 4055 return false; 4056 } else { 4057 netid = test.networkId; 4058 } 4059 } 4060 4061 return canModifyNetwork(uid, netid, onlyAnnotate); 4062 } 4063 4064 boolean checkConfigOverridePermission(int uid) { 4065 try { 4066 return (mFacade.checkUidPermission( 4067 android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid) 4068 == PackageManager.PERMISSION_GRANTED); 4069 } catch (RemoteException e) { 4070 return false; 4071 } 4072 } 4073 4074 /** called when CS ask WiFistateMachine to disconnect the current network 4075 * because the score is bad. 4076 */ 4077 void handleBadNetworkDisconnectReport(int netId, WifiInfo info) { 4078 /* TODO verify the bad network is current */ 4079 WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); 4080 if (config != null) { 4081 if ((info.is24GHz() && info.getRssi() 4082 <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND) 4083 || (info.is5GHz() && info.getRssi() 4084 <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND)) { 4085 // We do not block due to bad RSSI since network selection should not select bad 4086 // RSSI candidate 4087 } else { 4088 // We got disabled but RSSI is good, so disable hard 4089 updateNetworkSelectionStatus(config, 4090 WifiConfiguration.NetworkSelectionStatus.DISABLED_BAD_LINK); 4091 } 4092 } 4093 // Record last time Connectivity Service switched us away from WiFi and onto Cell 4094 lastUnwantedNetworkDisconnectTimestamp = System.currentTimeMillis(); 4095 } 4096 4097 int getMaxDhcpRetries() { 4098 return mFacade.getIntegerSetting(mContext, 4099 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 4100 DEFAULT_MAX_DHCP_RETRIES); 4101 } 4102 4103 void clearBssidBlacklist() { 4104 if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) { 4105 if(DBG) { 4106 Log.d(TAG, "No blacklist allowed without epno enabled"); 4107 } 4108 return; 4109 } 4110 mBssidBlacklist = new HashSet<String>(); 4111 mWifiNative.clearBlacklist(); 4112 mWifiNative.setBssidBlacklist(null); 4113 } 4114 4115 void blackListBssid(String BSSID) { 4116 if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) { 4117 if(DBG) { 4118 Log.d(TAG, "No blacklist allowed without epno enabled"); 4119 } 4120 return; 4121 } 4122 if (BSSID == null) 4123 return; 4124 mBssidBlacklist.add(BSSID); 4125 // Blacklist at wpa_supplicant 4126 mWifiNative.addToBlacklist(BSSID); 4127 // Blacklist at firmware 4128 String list[] = new String[mBssidBlacklist.size()]; 4129 int count = 0; 4130 for (String bssid : mBssidBlacklist) { 4131 list[count++] = bssid; 4132 } 4133 mWifiNative.setBssidBlacklist(list); 4134 } 4135 4136 public boolean isBssidBlacklisted(String bssid) { 4137 return mBssidBlacklist.contains(bssid); 4138 } 4139 4140 public boolean getEnableNewNetworkSelectionWhenAssociated() { 4141 return enableAutoJoinWhenAssociated.get(); 4142 } 4143 4144 boolean installKeys(WifiEnterpriseConfig oldConfig, WifiEnterpriseConfig config, String name) { 4145 boolean ret = true; 4146 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 4147 String userCertName = Credentials.USER_CERTIFICATE + name; 4148 if (config.getClientCertificate() != null) { 4149 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 4150 if (DBG) { 4151 if (isHardwareBackedKey(config.getClientPrivateKey())) { 4152 Log.d(TAG, "importing keys " + name + " in hardware backed store"); 4153 } else { 4154 Log.d(TAG, "importing keys " + name + " in software backed store"); 4155 } 4156 } 4157 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 4158 KeyStore.FLAG_NONE); 4159 4160 if (ret == false) { 4161 return ret; 4162 } 4163 4164 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 4165 if (ret == false) { 4166 // Remove private key installed 4167 mKeyStore.delete(privKeyName, Process.WIFI_UID); 4168 return ret; 4169 } 4170 } 4171 4172 X509Certificate[] caCertificates = config.getCaCertificates(); 4173 Set<String> oldCaCertificatesToRemove = new ArraySet<String>(); 4174 if (oldConfig != null && oldConfig.getCaCertificateAliases() != null) { 4175 oldCaCertificatesToRemove.addAll(Arrays.asList(oldConfig.getCaCertificateAliases())); 4176 } 4177 List<String> caCertificateAliases = null; 4178 if (caCertificates != null) { 4179 caCertificateAliases = new ArrayList<String>(); 4180 for (int i = 0; i < caCertificates.length; i++) { 4181 String alias = caCertificates.length == 1 ? name 4182 : String.format("%s_%d", name, i); 4183 4184 oldCaCertificatesToRemove.remove(alias); 4185 ret = putCertInKeyStore(Credentials.CA_CERTIFICATE + alias, caCertificates[i]); 4186 if (!ret) { 4187 // Remove client key+cert 4188 if (config.getClientCertificate() != null) { 4189 mKeyStore.delete(privKeyName, Process.WIFI_UID); 4190 mKeyStore.delete(userCertName, Process.WIFI_UID); 4191 } 4192 // Remove added CA certs. 4193 for (String addedAlias : caCertificateAliases) { 4194 mKeyStore.delete(Credentials.CA_CERTIFICATE + addedAlias, Process.WIFI_UID); 4195 } 4196 return ret; 4197 } else { 4198 caCertificateAliases.add(alias); 4199 } 4200 } 4201 } 4202 // Remove old CA certs. 4203 for (String oldAlias : oldCaCertificatesToRemove) { 4204 mKeyStore.delete(Credentials.CA_CERTIFICATE + oldAlias, Process.WIFI_UID); 4205 } 4206 // Set alias names 4207 if (config.getClientCertificate() != null) { 4208 config.setClientCertificateAlias(name); 4209 config.resetClientKeyEntry(); 4210 } 4211 4212 if (caCertificates != null) { 4213 config.setCaCertificateAliases( 4214 caCertificateAliases.toArray(new String[caCertificateAliases.size()])); 4215 config.resetCaCertificate(); 4216 } 4217 4218 return ret; 4219 } 4220 4221 private boolean putCertInKeyStore(String name, Certificate cert) { 4222 try { 4223 byte[] certData = Credentials.convertToPem(cert); 4224 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 4225 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 4226 4227 } catch (IOException e1) { 4228 return false; 4229 } catch (CertificateException e2) { 4230 return false; 4231 } 4232 } 4233 4234 void removeKeys(WifiEnterpriseConfig config) { 4235 String client = config.getClientCertificateAlias(); 4236 // a valid client certificate is configured 4237 if (!TextUtils.isEmpty(client)) { 4238 if (DBG) Log.d(TAG, "removing client private key and user cert"); 4239 mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 4240 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 4241 } 4242 4243 String[] aliases = config.getCaCertificateAliases(); 4244 // a valid ca certificate is configured 4245 if (aliases != null) { 4246 for (String ca: aliases) { 4247 if (!TextUtils.isEmpty(ca)) { 4248 if (DBG) Log.d(TAG, "removing CA cert: " + ca); 4249 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 4250 } 4251 } 4252 } 4253 } 4254 4255 4256 /** Migrates the old style TLS config to the new config style. This should only be used 4257 * when restoring an old wpa_supplicant.conf or upgrading from a previous 4258 * platform version. 4259 * @return true if the config was updated 4260 * @hide 4261 */ 4262 boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) { 4263 String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); 4264 /* 4265 * If the old configuration value is not present, then there is nothing 4266 * to do. 4267 */ 4268 if (TextUtils.isEmpty(oldPrivateKey)) { 4269 return false; 4270 } else { 4271 // Also ignore it if it's empty quotes. 4272 oldPrivateKey = removeDoubleQuotes(oldPrivateKey); 4273 if (TextUtils.isEmpty(oldPrivateKey)) { 4274 return false; 4275 } 4276 } 4277 4278 config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE); 4279 config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, 4280 WifiEnterpriseConfig.ENGINE_ID_KEYSTORE); 4281 4282 /* 4283 * The old key started with the keystore:// URI prefix, but we don't 4284 * need that anymore. Trim it off if it exists. 4285 */ 4286 final String keyName; 4287 if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) { 4288 keyName = new String( 4289 oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length())); 4290 } else { 4291 keyName = oldPrivateKey; 4292 } 4293 config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName); 4294 4295 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY, 4296 config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, "")); 4297 4298 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY, 4299 config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, "")); 4300 4301 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, 4302 config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, "")); 4303 4304 // Remove old private_key string so we don't run this again. 4305 mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); 4306 4307 return true; 4308 } 4309 4310 /** Migrate certs from global pool to wifi UID if not already done */ 4311 void migrateCerts(WifiEnterpriseConfig config) { 4312 String client = config.getClientCertificateAlias(); 4313 // a valid client certificate is configured 4314 if (!TextUtils.isEmpty(client)) { 4315 if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { 4316 mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, 4317 Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 4318 mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, 4319 Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 4320 } 4321 } 4322 4323 String[] aliases = config.getCaCertificateAliases(); 4324 // a valid ca certificate is configured 4325 if (aliases != null) { 4326 for (String ca : aliases) { 4327 if (!TextUtils.isEmpty(ca) 4328 && !mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { 4329 mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, 4330 Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 4331 } 4332 } 4333 } 4334 } 4335 4336 private void readNetworkBitsetVariable(int netId, BitSet variable, String varName, 4337 String[] strings) { 4338 String value = mWifiNative.getNetworkVariable(netId, varName); 4339 if (!TextUtils.isEmpty(value)) { 4340 variable.clear(); 4341 String vals[] = value.split(" "); 4342 for (String val : vals) { 4343 int index = lookupString(val, strings); 4344 if (0 <= index) { 4345 variable.set(index); 4346 } 4347 } 4348 } 4349 } 4350} 4351