WifiConfigStore.java revision c290d8dff6172d5fde7b9dfd74d3a20785dab246
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 android.content.Context; 20import android.content.Intent; 21import android.net.IpConfiguration; 22import android.net.IpConfiguration.IpAssignment; 23import android.net.IpConfiguration.ProxySettings; 24import android.net.LinkAddress; 25import android.net.LinkProperties; 26import android.net.NetworkInfo.DetailedState; 27import android.net.ProxyInfo; 28import android.net.RouteInfo; 29import android.net.wifi.WifiConfiguration; 30import android.net.wifi.WifiConfiguration.KeyMgmt; 31import android.net.wifi.WifiConfiguration.Status; 32import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 33 34import android.net.wifi.WifiEnterpriseConfig; 35import android.net.wifi.WifiManager; 36import android.net.wifi.WifiSsid; 37import android.net.wifi.WpsInfo; 38import android.net.wifi.WpsResult; 39import android.net.wifi.ScanResult; 40import android.net.wifi.WifiInfo; 41 42import android.os.Environment; 43import android.os.FileObserver; 44import android.os.Process; 45import android.os.SystemClock; 46import android.os.UserHandle; 47import android.provider.Settings; 48import android.security.Credentials; 49import android.security.KeyChain; 50import android.security.KeyStore; 51import android.text.TextUtils; 52import android.util.LocalLog; 53import android.util.Log; 54import android.util.SparseArray; 55 56import com.android.server.net.DelayedDiskWrite; 57import com.android.server.net.IpConfigStore; 58 59import java.io.BufferedReader; 60import java.io.BufferedInputStream; 61import java.io.DataInputStream; 62import java.io.DataOutputStream; 63import java.io.EOFException; 64import java.io.File; 65import java.io.FileDescriptor; 66import java.io.FileInputStream; 67import java.io.FileNotFoundException; 68import java.io.FileReader; 69import java.io.IOException; 70import java.io.PrintWriter; 71import java.math.BigInteger; 72import java.net.InetAddress; 73import java.nio.charset.Charset; 74import java.security.PrivateKey; 75import java.security.cert.Certificate; 76import java.security.cert.CertificateException; 77import java.text.SimpleDateFormat; 78import java.text.DateFormat; 79import java.util.regex.Matcher; 80import java.util.regex.Pattern; 81import java.util.*; 82 83/** 84 * This class provides the API to manage configured 85 * wifi networks. The API is not thread safe is being 86 * used only from WifiStateMachine. 87 * 88 * It deals with the following 89 * - Add/update/remove a WifiConfiguration 90 * The configuration contains two types of information. 91 * = IP and proxy configuration that is handled by WifiConfigStore and 92 * is saved to disk on any change. 93 * 94 * The format of configuration file is as follows: 95 * <version> 96 * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS> 97 * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS> 98 * .. 99 * 100 * (key, value) pairs for a given network are grouped together and can 101 * be in any order. A EOS at the end of a set of (key, value) pairs 102 * indicates that the next set of (key, value) pairs are for a new 103 * network. A network is identified by a unique ID_KEY. If there is no 104 * ID_KEY in the (key, value) pairs, the data is discarded. 105 * 106 * An invalid version on read would result in discarding the contents of 107 * the file. On the next write, the latest version is written to file. 108 * 109 * Any failures during read or write to the configuration file are ignored 110 * without reporting to the user since the likelihood of these errors are 111 * low and the impact on connectivity is low. 112 * 113 * = SSID & security details that is pushed to the supplicant. 114 * supplicant saves these details to the disk on calling 115 * saveConfigCommand(). 116 * 117 * We have two kinds of APIs exposed: 118 * > public API calls that provide fine grained control 119 * - enableNetwork, disableNetwork, addOrUpdateNetwork(), 120 * removeNetwork(). For these calls, the config is not persisted 121 * to the disk. (TODO: deprecate these calls in WifiManager) 122 * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork(). 123 * These calls persist the supplicant config to disk. 124 * 125 * - Maintain a list of configured networks for quick access 126 * 127 */ 128public class WifiConfigStore extends IpConfigStore { 129 130 private Context mContext; 131 private static final String TAG = "WifiConfigStore"; 132 private static final boolean DBG = true; 133 private static boolean VDBG = false; 134 135 private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; 136 137 /* configured networks with network id as the key */ 138 private HashMap<Integer, WifiConfiguration> mConfiguredNetworks = 139 new HashMap<Integer, WifiConfiguration>(); 140 141 /* A network id is a unique identifier for a network configured in the 142 * supplicant. Network ids are generated when the supplicant reads 143 * the configuration file at start and can thus change for networks. 144 * We store the IP configuration for networks along with a unique id 145 * that is generated from SSID and security type of the network. A mapping 146 * from the generated unique id to network id of the network is needed to 147 * map supplicant config to IP configuration. */ 148 private HashMap<Integer, Integer> mNetworkIds = 149 new HashMap<Integer, Integer>(); 150 151 /* Tracks the highest priority of configured networks */ 152 private int mLastPriority = -1; 153 154 private static final String ipConfigFile = Environment.getDataDirectory() + 155 "/misc/wifi/ipconfig.txt"; 156 157 private static final String networkHistoryConfigFile = Environment.getDataDirectory() + 158 "/misc/wifi/networkHistory.txt"; 159 160 /* Network History Keys */ 161 private static final String SSID_KEY = "SSID: "; 162 private static final String CONFIG_KEY = "CONFIG: "; 163 private static final String CHOICE_KEY = "CHOICE: "; 164 private static final String LINK_KEY = "LINK: "; 165 private static final String BSSID_KEY = "BSSID: "; 166 private static final String BSSID_KEY_END = "/BSSID: "; 167 private static final String RSSI_KEY = "RSSI: "; 168 private static final String FREQ_KEY = "FREQ: "; 169 private static final String DATE_KEY = "DATE: "; 170 private static final String MILLI_KEY = "MILLI: "; 171 private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI: "; 172 private static final String NETWORK_ID_KEY = "ID: "; 173 private static final String PRIORITY_KEY = "PRIORITY: "; 174 private static final String DEFAULT_GW_KEY = "DEFAULT_GW: "; 175 private static final String AUTH_KEY = "AUTH: "; 176 private static final String SEPARATOR_KEY = "\n"; 177 private static final String STATUS_KEY = "AUTO_JOIN_STATUS: "; 178 private static final String BSSID_STATUS_KEY = "BSSID_STATUS: "; 179 private static final String SELF_ADDED_KEY = "SELF_ADDED: "; 180 private static final String FAILURE_KEY = "FAILURE: "; 181 private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD: "; 182 private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION: "; 183 private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY: "; 184 private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY: "; 185 private static final String UPDATE_UID_KEY = "UPDATE_UID: "; 186 private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS: "; 187 private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON: "; 188 private static final String FQDN_KEY = "FQDN: "; 189 private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES: "; 190 private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE: "; 191 private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH: "; 192 private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION: "; 193 /** 194 * Regex pattern for extracting a connect choice. 195 * Matches a strings like the following: 196 * <configKey>=([0:9]+) 197 */ 198 private static Pattern mConnectChoice = 199 Pattern.compile("(.*)=([0-9]+)"); 200 201 202 /* Enterprise configuration keys */ 203 /** 204 * In old configurations, the "private_key" field was used. However, newer 205 * configurations use the key_id field with the engine_id set to "keystore". 206 * If this field is found in the configuration, the migration code is 207 * triggered. 208 */ 209 public static final String OLD_PRIVATE_KEY_NAME = "private_key"; 210 211 /** 212 * This represents an empty value of an enterprise field. 213 * NULL is used at wpa_supplicant to indicate an empty value 214 */ 215 static final String EMPTY_VALUE = "NULL"; 216 217 // Internal use only 218 private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] { 219 WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY, 220 WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY, 221 WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY, 222 WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY, 223 WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY, 224 WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY }; 225 226 227 /** 228 * The maximum number of times we will retry a connection to an access point 229 * for which we have failed in acquiring an IP address from DHCP. A value of 230 * N means that we will make N+1 connection attempts in all. 231 * <p> 232 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default 233 * value if a Settings value is not present. 234 */ 235 private static final int DEFAULT_MAX_DHCP_RETRIES = 9; 236 237 238 private final LocalLog mLocalLog; 239 private final WpaConfigFileObserver mFileObserver; 240 241 private WifiNative mWifiNative; 242 private final KeyStore mKeyStore = KeyStore.getInstance(); 243 244 /** 245 * The lastSelectedConfiguration is used to remember which network was selected last by the user. 246 * The connection to this network may not be successful, as well 247 * the selection (i.e. network priority) might not be persisted. 248 * WiFi state machine is the only object that sets this variable. 249 */ 250 private String lastSelectedConfiguration = null; 251 252 WifiConfigStore(Context c, WifiNative wn) { 253 mContext = c; 254 mWifiNative = wn; 255 256 if (VDBG) { 257 mLocalLog = mWifiNative.getLocalLog(); 258 mFileObserver = new WpaConfigFileObserver(); 259 mFileObserver.startWatching(); 260 } else { 261 mLocalLog = null; 262 mFileObserver = null; 263 } 264 } 265 266 void enableVerboseLogging(int verbose) { 267 if (verbose > 0) { 268 VDBG = true; 269 } else { 270 VDBG = false; 271 } 272 } 273 274 class WpaConfigFileObserver extends FileObserver { 275 276 public WpaConfigFileObserver() { 277 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 278 } 279 280 @Override 281 public void onEvent(int event, String path) { 282 if (event == CLOSE_WRITE) { 283 File file = new File(SUPPLICANT_CONFIG_FILE); 284 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 285 } 286 } 287 } 288 289 290 /** 291 * Fetch the list of configured networks 292 * and enable all stored networks in supplicant. 293 */ 294 void loadAndEnableAllNetworks() { 295 if (DBG) log("Loading config and enabling all networks "); 296 loadConfiguredNetworks(); 297 enableAllNetworks(); 298 } 299 300 int getConfiguredNetworksSize() { 301 return mConfiguredNetworks.size(); 302 } 303 304 private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) { 305 List<WifiConfiguration> networks = new ArrayList<>(); 306 for(WifiConfiguration config : mConfiguredNetworks.values()) { 307 WifiConfiguration newConfig = new WifiConfiguration(config); 308 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 309 //do not enumerate and return this configuration to any one, 310 //for instance WiFi Picker. 311 //instead treat it as unknown. the configuration can still be retrieved 312 //directly by the key or networkId 313 continue; 314 } 315 if (pskMap != null && config.allowedKeyManagement != null 316 && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) 317 && pskMap.containsKey(config.SSID)) { 318 newConfig.preSharedKey = pskMap.get(config.SSID); 319 } 320 networks.add(newConfig); 321 } 322 return networks; 323 } 324 325 /** 326 * Fetch the list of currently configured networks 327 * @return List of networks 328 */ 329 List<WifiConfiguration> getConfiguredNetworks() { 330 return getConfiguredNetworks(null); 331 } 332 333 /** 334 * Fetch the list of currently configured networks, filled with real preSharedKeys 335 * @return List of networks 336 */ 337 List<WifiConfiguration> getPrivilegedConfiguredNetworks() { 338 Map<String, String> pskMap = getCredentialsBySsidMap(); 339 return getConfiguredNetworks(pskMap); 340 } 341 342 /** 343 * Fetch the preSharedKeys for all networks. 344 * @return a map from Ssid to preSharedKey. 345 */ 346 private Map<String, String> getCredentialsBySsidMap() { 347 return readNetworkVariablesFromSupplicantFile("psk"); 348 } 349 350 /** 351 * Fetch the list of currently configured networks that were recently seen 352 * 353 * @return List of networks 354 */ 355 List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) { 356 List<WifiConfiguration> networks = null; 357 358 for (WifiConfiguration config : mConfiguredNetworks.values()) { 359 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 360 // Do not enumerate and return this configuration to any one, 361 // instead treat it as unknown. the configuration can still be retrieved 362 // directly by the key or networkId 363 continue; 364 } 365 366 // Calculate the RSSI for scan results that are more recent than milli 367 config.setVisibility(milli); 368 369 if (config.visibility == null) { 370 continue; 371 } 372 if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI && 373 config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) { 374 continue; 375 } 376 if (networks == null) 377 networks = new ArrayList<WifiConfiguration>(); 378 if (copy) { 379 networks.add(new WifiConfiguration(config)); 380 } else { 381 networks.add(config); 382 } 383 } 384 return networks; 385 } 386 387 /** 388 * Update the configuration and BSSID with latest RSSI value. 389 */ 390 void updateConfiguration(WifiInfo info) { 391 WifiConfiguration config = getWifiConfiguration(info.getNetworkId()); 392 if (config != null && config.scanResultCache != null) { 393 ScanResult result = config.scanResultCache.get(info.getBSSID()); 394 if (result != null) { 395 long previousSeen = result.seen; 396 int previousRssi = result.level; 397 398 // Update the scan result 399 result.seen = System.currentTimeMillis(); 400 result.level = info.getRssi(); 401 402 // Average the RSSI value 403 result.averageRssi(previousRssi, previousSeen, 404 WifiAutoJoinController.mScanResultMaximumAge); 405 if (VDBG) { 406 loge("updateConfiguration freq=" + result.frequency 407 + " BSSID=" + result.BSSID 408 + " RSSI=" + result.level 409 + " " + config.configKey()); 410 } 411 } 412 } 413 } 414 415 /** 416 * get the Wificonfiguration for this netId 417 * 418 * @return Wificonfiguration 419 */ 420 WifiConfiguration getWifiConfiguration(int netId) { 421 if (mConfiguredNetworks == null) 422 return null; 423 return mConfiguredNetworks.get(netId); 424 } 425 426 /** 427 * Get the Wificonfiguration for this key 428 * @return Wificonfiguration 429 */ 430 WifiConfiguration getWifiConfiguration(String key) { 431 if (key == null) 432 return null; 433 int hash = key.hashCode(); 434 if (mNetworkIds == null) 435 return null; 436 Integer n = mNetworkIds.get(hash); 437 if (n == null) 438 return null; 439 int netId = n.intValue(); 440 return getWifiConfiguration(netId); 441 } 442 443 /** 444 * Enable all networks and save config. This will be a no-op if the list 445 * of configured networks indicates all networks as being enabled 446 */ 447 void enableAllNetworks() { 448 boolean networkEnabledStateChanged = false; 449 for(WifiConfiguration config : mConfiguredNetworks.values()) { 450 if(config != null && config.status == Status.DISABLED 451 && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) { 452 if(mWifiNative.enableNetwork(config.networkId, false)) { 453 networkEnabledStateChanged = true; 454 config.status = Status.ENABLED; 455 } else { 456 loge("Enable network failed on " + config.networkId); 457 } 458 } 459 } 460 461 if (networkEnabledStateChanged) { 462 mWifiNative.saveConfig(); 463 sendConfiguredNetworksChangedBroadcast(); 464 } 465 } 466 467 468 /** 469 * Selects the specified network for connection. This involves 470 * updating the priority of all the networks and enabling the given 471 * network while disabling others. 472 * 473 * Selecting a network will leave the other networks disabled and 474 * a call to enableAllNetworks() needs to be issued upon a connection 475 * or a failure event from supplicant 476 * 477 * @param netId network to select for connection 478 * @return false if the network id is invalid 479 */ 480 boolean selectNetwork(int netId) { 481 if (VDBG) localLog("selectNetwork", netId); 482 if (netId == INVALID_NETWORK_ID) return false; 483 484 // Reset the priority of each network at start or if it goes too high. 485 if (mLastPriority == -1 || mLastPriority > 1000000) { 486 for(WifiConfiguration config : mConfiguredNetworks.values()) { 487 if (config.networkId != INVALID_NETWORK_ID) { 488 config.priority = 0; 489 addOrUpdateNetworkNative(config); 490 } 491 } 492 mLastPriority = 0; 493 } 494 495 // Set to the highest priority and save the configuration. 496 WifiConfiguration config = new WifiConfiguration(); 497 config.networkId = netId; 498 config.priority = ++mLastPriority; 499 500 addOrUpdateNetworkNative(config); 501 mWifiNative.saveConfig(); 502 503 /* Enable the given network while disabling all other networks */ 504 enableNetworkWithoutBroadcast(netId, true); 505 506 /* Avoid saving the config & sending a broadcast to prevent settings 507 * from displaying a disabled list of networks */ 508 return true; 509 } 510 511 /** 512 * Add/update the specified configuration and save config 513 * 514 * @param config WifiConfiguration to be saved 515 * @return network update result 516 */ 517 NetworkUpdateResult saveNetwork(WifiConfiguration config) { 518 WifiConfiguration conf; 519 520 // A new network cannot have null SSID 521 if (config == null || (config.networkId == INVALID_NETWORK_ID && 522 config.SSID == null)) { 523 return new NetworkUpdateResult(INVALID_NETWORK_ID); 524 } 525 if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId); 526 if (VDBG) { 527 loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size() 528 + " SSID=" + config.SSID 529 + " Uid=" + Integer.toString(config.creatorUid) 530 + "/" + Integer.toString(config.lastUpdateUid)); 531 } 532 boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); 533 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 534 int netId = result.getNetworkId(); 535 536 if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId); 537 538 /* enable a new network */ 539 if (newNetwork && netId != INVALID_NETWORK_ID) { 540 if (VDBG) localLog("WifiConfigStore: will enable netId=", netId); 541 542 mWifiNative.enableNetwork(netId, false); 543 conf = mConfiguredNetworks.get(netId); 544 if (conf != null) 545 conf.status = Status.ENABLED; 546 } 547 548 conf = mConfiguredNetworks.get(netId); 549 if (conf != null) { 550 if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) { 551 if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID); 552 553 // reenable autojoin, since new information has been provided 554 conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 555 enableNetworkWithoutBroadcast(conf.networkId, false); 556 } 557 if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId=" 558 + Integer.toString(netId) 559 + " uid=" + Integer.toString(config.creatorUid)); 560 } 561 562 mWifiNative.saveConfig(); 563 sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ? 564 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 565 return result; 566 } 567 568 void updateStatus(int netId, DetailedState state) { 569 if (netId != INVALID_NETWORK_ID) { 570 WifiConfiguration config = mConfiguredNetworks.get(netId); 571 if (config == null) return; 572 switch (state) { 573 case CONNECTED: 574 config.status = Status.CURRENT; 575 //we successfully connected, hence remove the blacklist 576 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 577 break; 578 case DISCONNECTED: 579 //If network is already disabled, keep the status 580 if (config.status == Status.CURRENT) { 581 config.status = Status.ENABLED; 582 } 583 break; 584 default: 585 //do nothing, retain the existing state 586 break; 587 } 588 } 589 } 590 591 /** 592 * Forget the specified network and save config 593 * 594 * @param netId network to forget 595 * @return {@code true} if it succeeds, {@code false} otherwise 596 */ 597 boolean forgetNetwork(int netId) { 598 if (VDBG) localLog("forgetNetwork", netId); 599 600 boolean remove = removeConfigAndSendBroadcastIfNeeded(netId); 601 if (!remove) { 602 //success but we dont want to remove the network from supplicant conf file 603 return true; 604 } 605 if (mWifiNative.removeNetwork(netId)) { 606 mWifiNative.saveConfig(); 607 return true; 608 } else { 609 loge("Failed to remove network " + netId); 610 return false; 611 } 612 } 613 614 /** 615 * Add/update a network. Note that there is no saveConfig operation. 616 * This function is retained for compatibility with the public 617 * API. The more powerful saveNetwork() is used by the 618 * state machine 619 * 620 * @param config wifi configuration to add/update 621 * @return network Id 622 */ 623 int addOrUpdateNetwork(WifiConfiguration config) { 624 if (VDBG) localLog("addOrUpdateNetwork id=", config.networkId); 625 //adding unconditional message to chase b/15111865 626 Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId) 627 + " uid=" + Integer.toString(config.creatorUid) 628 + "/" + Integer.toString(config.lastUpdateUid)); 629 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 630 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 631 WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId()); 632 if (conf != null) { 633 sendConfiguredNetworksChangedBroadcast(conf, 634 result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED : 635 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 636 } 637 } 638 return result.getNetworkId(); 639 } 640 641 /** 642 * Remove a network. Note that there is no saveConfig operation. 643 * This function is retained for compatibility with the public 644 * API. The more powerful forgetNetwork() is used by the 645 * state machine for network removal 646 * 647 * @param netId network to be removed 648 * @return {@code true} if it succeeds, {@code false} otherwise 649 */ 650 boolean removeNetwork(int netId) { 651 if (VDBG) localLog("removeNetwork", netId); 652 boolean ret = mWifiNative.removeNetwork(netId); 653 if (ret) { 654 removeConfigAndSendBroadcastIfNeeded(netId); 655 } 656 return ret; 657 } 658 659 private boolean removeConfigAndSendBroadcastIfNeeded(int netId) { 660 boolean remove = true; 661 WifiConfiguration config = mConfiguredNetworks.get(netId); 662 if (config != null) { 663 if (VDBG) { 664 loge("removeNetwork " + Integer.toString(netId) + " key=" + 665 config.configKey() + " config.id=" + Integer.toString(config.networkId)); 666 } 667 668 // cancel the last user choice 669 if (config.configKey().equals(lastSelectedConfiguration)) { 670 lastSelectedConfiguration = null; 671 } 672 673 // Remove any associated keys 674 if (config.enterpriseConfig != null) { 675 removeKeys(config.enterpriseConfig); 676 } 677 678 if (config.didSelfAdd) { 679 if (config.peerWifiConfiguration != null) { 680 for (WifiConfiguration peer : mConfiguredNetworks.values()) { 681 if (config.peerWifiConfiguration.equals(peer.configKey())) { 682 /* the configuration that trigger the add is still there */ 683 remove = false; 684 } 685 } 686 } 687 } 688 689 if (remove) { 690 mConfiguredNetworks.remove(netId); 691 mNetworkIds.remove(configKey(config)); 692 } else { 693 /* we can't directly remove the configuration since we added it ourselves, because 694 * that could cause the system to re-add it right away. 695 * Instead black list it. It will be unblacklisted only thru a new add. 696 */ 697 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DELETED); 698 mWifiNative.disableNetwork(config.networkId); 699 remove = false; 700 } 701 702 writeIpAndProxyConfigurations(); 703 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 704 writeKnownNetworkHistory(); 705 } 706 return remove; 707 } 708 709 /** 710 * Enable a network. Note that there is no saveConfig operation. 711 * This function is retained for compatibility with the public 712 * API. The more powerful selectNetwork()/saveNetwork() is used by the 713 * state machine for connecting to a network 714 * 715 * @param netId network to be enabled 716 * @return {@code true} if it succeeds, {@code false} otherwise 717 */ 718 boolean enableNetwork(int netId, boolean disableOthers) { 719 boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers); 720 if (disableOthers) { 721 if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId); 722 sendConfiguredNetworksChangedBroadcast(); 723 } else { 724 if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId); 725 WifiConfiguration enabledNetwork = null; 726 synchronized(mConfiguredNetworks) { 727 enabledNetwork = mConfiguredNetworks.get(netId); 728 } 729 // check just in case the network was removed by someone else. 730 if (enabledNetwork != null) { 731 sendConfiguredNetworksChangedBroadcast(enabledNetwork, 732 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 733 } 734 } 735 return ret; 736 } 737 738 boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) { 739 boolean ret = mWifiNative.enableNetwork(netId, disableOthers); 740 741 WifiConfiguration config = mConfiguredNetworks.get(netId); 742 if (config != null) config.status = Status.ENABLED; 743 744 if (disableOthers) { 745 markAllNetworksDisabledExcept(netId); 746 } 747 return ret; 748 } 749 750 void disableAllNetworks() { 751 if (VDBG) localLog("disableAllNetworks"); 752 boolean networkDisabled = false; 753 for(WifiConfiguration config : mConfiguredNetworks.values()) { 754 if(config != null && config.status != Status.DISABLED) { 755 if(mWifiNative.disableNetwork(config.networkId)) { 756 networkDisabled = true; 757 config.status = Status.DISABLED; 758 } else { 759 loge("Disable network failed on " + config.networkId); 760 } 761 } 762 } 763 764 if (networkDisabled) { 765 sendConfiguredNetworksChangedBroadcast(); 766 } 767 } 768 /** 769 * Disable a network. Note that there is no saveConfig operation. 770 * @param netId network to be disabled 771 * @return {@code true} if it succeeds, {@code false} otherwise 772 */ 773 boolean disableNetwork(int netId) { 774 return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON); 775 } 776 777 /** 778 * Disable a network. Note that there is no saveConfig operation. 779 * @param netId network to be disabled 780 * @param reason reason code network was disabled 781 * @return {@code true} if it succeeds, {@code false} otherwise 782 */ 783 boolean disableNetwork(int netId, int reason) { 784 if (VDBG) localLog("disableNetwork", netId); 785 boolean ret = mWifiNative.disableNetwork(netId); 786 WifiConfiguration network = null; 787 WifiConfiguration config = mConfiguredNetworks.get(netId); 788 789 if (VDBG) { 790 if (config != null) { 791 loge("disableNetwork netId=" + Integer.toString(netId) 792 + " SSID=" + config.SSID 793 + " disabled=" + (config.status == Status.DISABLED) 794 + " reason=" + Integer.toString(config.disableReason)); 795 } 796 } 797 /* Only change the reason if the network was not previously disabled */ 798 if (config != null && config.status != Status.DISABLED) { 799 config.status = Status.DISABLED; 800 config.disableReason = reason; 801 network = config; 802 } 803 if (network != null) { 804 sendConfiguredNetworksChangedBroadcast(network, 805 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 806 } 807 return ret; 808 } 809 810 /** 811 * Save the configured networks in supplicant to disk 812 * @return {@code true} if it succeeds, {@code false} otherwise 813 */ 814 boolean saveConfig() { 815 return mWifiNative.saveConfig(); 816 } 817 818 /** 819 * Start WPS pin method configuration with pin obtained 820 * from the access point 821 * @param config WPS configuration 822 * @return Wps result containing status and pin 823 */ 824 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) { 825 WpsResult result = new WpsResult(); 826 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 827 /* WPS leaves all networks disabled */ 828 markAllNetworksDisabled(); 829 result.status = WpsResult.Status.SUCCESS; 830 } else { 831 loge("Failed to start WPS pin method configuration"); 832 result.status = WpsResult.Status.FAILURE; 833 } 834 return result; 835 } 836 837 /** 838 * Start WPS pin method configuration with pin obtained 839 * from the device 840 * @return WpsResult indicating status and pin 841 */ 842 WpsResult startWpsWithPinFromDevice(WpsInfo config) { 843 WpsResult result = new WpsResult(); 844 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 845 /* WPS leaves all networks disabled */ 846 if (!TextUtils.isEmpty(result.pin)) { 847 markAllNetworksDisabled(); 848 result.status = WpsResult.Status.SUCCESS; 849 } else { 850 loge("Failed to start WPS pin method configuration"); 851 result.status = WpsResult.Status.FAILURE; 852 } 853 return result; 854 } 855 856 /** 857 * Start WPS push button configuration 858 * @param config WPS configuration 859 * @return WpsResult indicating status and pin 860 */ 861 WpsResult startWpsPbc(WpsInfo config) { 862 WpsResult result = new WpsResult(); 863 if (mWifiNative.startWpsPbc(config.BSSID)) { 864 /* WPS leaves all networks disabled */ 865 markAllNetworksDisabled(); 866 result.status = WpsResult.Status.SUCCESS; 867 } else { 868 loge("Failed to start WPS push button configuration"); 869 result.status = WpsResult.Status.FAILURE; 870 } 871 return result; 872 } 873 874 /** 875 * Fetch the link properties for a given network id 876 * 877 * @return LinkProperties for the given network id 878 */ 879 LinkProperties getLinkProperties(int netId) { 880 WifiConfiguration config = mConfiguredNetworks.get(netId); 881 if (config != null) return new LinkProperties(config.getLinkProperties()); 882 return null; 883 } 884 885 /** 886 * set IP configuration for a given network id 887 */ 888 void setLinkProperties(int netId, LinkProperties linkProperties) { 889 WifiConfiguration config = mConfiguredNetworks.get(netId); 890 if (config != null) { 891 // add old proxy details - TODO - is this still needed? 892 if(config.getLinkProperties() != null) { 893 linkProperties.setHttpProxy(config.getLinkProperties().getHttpProxy()); 894 } 895 config.setLinkProperties(linkProperties); 896 } 897 } 898 899 /** 900 * set default GW MAC address 901 */ 902 void setDefaultGwMacAddress(int netId, String macAddress) { 903 WifiConfiguration config = mConfiguredNetworks.get(netId); 904 if (config != null) { 905 //update defaultGwMacAddress 906 config.defaultGwMacAddress = macAddress; 907 } 908 } 909 910 911 /** 912 * clear IP configuration for a given network id 913 * @param network id 914 */ 915 void clearLinkProperties(int netId) { 916 WifiConfiguration config = mConfiguredNetworks.get(netId); 917 if (config != null && config.getLinkProperties() != null) { 918 // Clear everything except proxy 919 ProxyInfo proxy = config.getLinkProperties().getHttpProxy(); 920 config.getLinkProperties().clear(); 921 config.getLinkProperties().setHttpProxy(proxy); 922 } 923 } 924 925 926 /** 927 * Fetch the proxy properties for a given network id 928 * @param network id 929 * @return ProxyInfo for the network id 930 */ 931 ProxyInfo getProxyProperties(int netId) { 932 LinkProperties linkProperties = getLinkProperties(netId); 933 if (linkProperties != null) { 934 return new ProxyInfo(linkProperties.getHttpProxy()); 935 } 936 return null; 937 } 938 939 /** 940 * Return if the specified network is using static IP 941 * @param network id 942 * @return {@code true} if using static ip for netId 943 */ 944 boolean isUsingStaticIp(int netId) { 945 WifiConfiguration config = mConfiguredNetworks.get(netId); 946 if (config != null && config.getIpAssignment() == IpAssignment.STATIC) { 947 return true; 948 } 949 return false; 950 } 951 952 /** 953 * Should be called when a single network configuration is made. 954 * @param network The network configuration that changed. 955 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 956 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 957 */ 958 private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, 959 int reason) { 960 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 961 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 962 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 963 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); 964 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 965 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 966 } 967 968 /** 969 * Should be called when multiple network configuration changes are made. 970 */ 971 private void sendConfiguredNetworksChangedBroadcast() { 972 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 973 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 974 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 975 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 976 } 977 978 void loadConfiguredNetworks() { 979 String listStr = mWifiNative.listNetworks(); 980 mLastPriority = 0; 981 982 mConfiguredNetworks.clear(); 983 mNetworkIds.clear(); 984 985 if (listStr == null) 986 return; 987 988 String[] lines = listStr.split("\n"); 989 990 if (VDBG) { 991 loge("loadConfiguredNetworks: found " + Integer.toString(lines.length) 992 + " networks", true); 993 } 994 995 // Skip the first line, which is a header 996 for (int i = 1; i < lines.length; i++) { 997 String[] result = lines[i].split("\t"); 998 // network-id | ssid | bssid | flags 999 WifiConfiguration config = new WifiConfiguration(); 1000 try { 1001 config.networkId = Integer.parseInt(result[0]); 1002 } catch(NumberFormatException e) { 1003 loge("Failed to read network-id '" + result[0] + "'"); 1004 continue; 1005 } 1006 if (result.length > 3) { 1007 if (result[3].indexOf("[CURRENT]") != -1) 1008 config.status = WifiConfiguration.Status.CURRENT; 1009 else if (result[3].indexOf("[DISABLED]") != -1) 1010 config.status = WifiConfiguration.Status.DISABLED; 1011 else 1012 config.status = WifiConfiguration.Status.ENABLED; 1013 } else { 1014 config.status = WifiConfiguration.Status.ENABLED; 1015 } 1016 readNetworkVariables(config); 1017 if (config.priority > mLastPriority) { 1018 mLastPriority = config.priority; 1019 } 1020 1021 config.setIpAssignment(IpAssignment.DHCP); 1022 config.setProxySettings(ProxySettings.NONE); 1023 1024 if (mNetworkIds.containsKey(configKey(config))) { 1025 // That SSID is already known, just ignore this duplicate entry 1026 if (VDBG) localLog("discarded duplicate network ", config.networkId); 1027 } else if(config.isValid()){ 1028 mConfiguredNetworks.put(config.networkId, config); 1029 mNetworkIds.put(configKey(config), config.networkId); 1030 if (VDBG) localLog("loaded configured network", config.networkId); 1031 } else { 1032 if (DBG) log("Ignoring loaded configured for network " + config.networkId 1033 + " because config are not valid"); 1034 } 1035 } 1036 1037 readIpAndProxyConfigurations(); 1038 readNetworkHistory(); 1039 1040 sendConfiguredNetworksChangedBroadcast(); 1041 1042 if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks"); 1043 1044 if (mNetworkIds.size() == 0) { 1045 // no networks? Lets log if the wpa_supplicant.conf file contents 1046 BufferedReader reader = null; 1047 try { 1048 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 1049 if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---"); 1050 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1051 if (VDBG) localLog(line); 1052 } 1053 if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---"); 1054 } catch (FileNotFoundException e) { 1055 if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 1056 } catch (IOException e) { 1057 if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 1058 } finally { 1059 try { 1060 if (reader != null) { 1061 reader.close(); 1062 } 1063 } catch (IOException e) { 1064 // Just ignore the fact that we couldn't close 1065 } 1066 } 1067 } 1068 } 1069 1070 private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) { 1071 Map<String, String> result = new HashMap<>(); 1072 BufferedReader reader = null; 1073 if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key); 1074 1075 try { 1076 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 1077 boolean found = false; 1078 String networkSsid = null; 1079 String value = null; 1080 1081 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1082 if (VDBG) loge(line); 1083 1084 if (line.matches("[ \\t]*network=\\{")) { 1085 found = true; 1086 networkSsid = null; 1087 value = null; 1088 } else if (line.matches("[ \\t]*\\{")) { 1089 found = false; 1090 networkSsid = null; 1091 value = null; 1092 } 1093 1094 if (found) { 1095 int index; 1096 if ((index = line.indexOf("ssid=")) >= 0) { 1097 networkSsid = line.substring(index + 5); 1098 } else if ((index = line.indexOf(key + "=")) >= 0) { 1099 value = line.substring(index + key.length() + 1); 1100 } 1101 1102 if (networkSsid != null && value != null) { 1103 result.put(networkSsid, value); 1104 } 1105 } 1106 } 1107 } catch (FileNotFoundException e) { 1108 if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 1109 } catch (IOException e) { 1110 if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 1111 } finally { 1112 try { 1113 if (reader != null) { 1114 reader.close(); 1115 } 1116 } catch (IOException e) { 1117 // Just ignore the fact that we couldn't close 1118 } 1119 } 1120 1121 return result; 1122 } 1123 1124 private String readNetworkVariableFromSupplicantFile(String ssid, String key) { 1125 Map<String, String> data = readNetworkVariablesFromSupplicantFile(key); 1126 if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key); 1127 return data.get(ssid); 1128 } 1129 1130 /* Mark all networks except specified netId as disabled */ 1131 private void markAllNetworksDisabledExcept(int netId) { 1132 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1133 if(config != null && config.networkId != netId) { 1134 if (config.status != Status.DISABLED) { 1135 config.status = Status.DISABLED; 1136 config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON; 1137 } 1138 } 1139 } 1140 } 1141 1142 private void markAllNetworksDisabled() { 1143 markAllNetworksDisabledExcept(INVALID_NETWORK_ID); 1144 } 1145 1146 boolean needsUnlockedKeyStore() { 1147 1148 // Any network using certificates to authenticate access requires 1149 // unlocked key store; unless the certificates can be stored with 1150 // hardware encryption 1151 1152 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1153 1154 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) 1155 && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1156 1157 if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 1158 return true; 1159 } 1160 } 1161 } 1162 1163 return false; 1164 } 1165 1166 public void writeKnownNetworkHistory() { 1167 if (VDBG) { 1168 loge(" writeKnownNetworkHistory() num networks:" + 1169 Integer.toString(mConfiguredNetworks.size()), true); 1170 } 1171 1172 /* Make a copy */ 1173 final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 1174 for (WifiConfiguration config : mConfiguredNetworks.values()) { 1175 networks.add(new WifiConfiguration(config)); 1176 } 1177 1178 mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() { 1179 public void onWriteCalled(DataOutputStream out) throws IOException { 1180 for (WifiConfiguration config : networks) { 1181 //loge("onWriteCalled write SSID: " + config.SSID); 1182 /* if (config.getLinkProperties() != null) 1183 loge(" lp " + config.getLinkProperties().toString()); 1184 else 1185 loge("attempt config w/o lp"); 1186 */ 1187 1188 if (VDBG) { 1189 int num = 0; 1190 if (config.connectChoices != null) { 1191 num = config.connectChoices.size(); 1192 } 1193 loge("saving network history: " + config.configKey() + " gw: " + 1194 config.defaultGwMacAddress + " autojoin status: " + 1195 config.autoJoinStatus + " ephemeral=" + config.ephemeral 1196 + " choices:" + Integer.toString(num)); 1197 } 1198 if (config.ephemeral == true) 1199 continue; 1200 1201 if (config.isValid() == false) 1202 continue; 1203 1204 if (config.SSID == null) { 1205 if (VDBG) { 1206 loge("writeKnownNetworkHistory trying to write config with null SSID"); 1207 } 1208 continue; 1209 } 1210 1211 out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY); 1212 1213 out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY); 1214 out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY); 1215 1216 out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY); 1217 out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus) 1218 + SEPARATOR_KEY); 1219 out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status) 1220 + SEPARATOR_KEY); 1221 out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY 1222 + Integer.toString(config.disableReason) 1223 + SEPARATOR_KEY); 1224 out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId) 1225 + SEPARATOR_KEY); 1226 out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded) 1227 + SEPARATOR_KEY); 1228 out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd) 1229 + SEPARATOR_KEY); 1230 if (config.peerWifiConfiguration != null) { 1231 out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration 1232 + SEPARATOR_KEY); 1233 } 1234 out.writeUTF(NUM_CONNECTION_FAILURES_KEY 1235 + Integer.toString(config.numConnectionFailures) 1236 + SEPARATOR_KEY); 1237 out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride) 1238 + SEPARATOR_KEY); 1239 out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY 1240 + Integer.toString(config.numScorerOverrideAndSwitchedNetwork) 1241 + SEPARATOR_KEY); 1242 out.writeUTF(NUM_ASSOCIATION_KEY 1243 + Integer.toString(config.numAssociation) 1244 + SEPARATOR_KEY); 1245 out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp) 1246 + SEPARATOR_KEY); 1247 out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid) 1248 + SEPARATOR_KEY); 1249 out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid) 1250 + SEPARATOR_KEY); 1251 out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid) 1252 + SEPARATOR_KEY); 1253 String allowedKeyManagementString = 1254 makeString(config.allowedKeyManagement, 1255 WifiConfiguration.KeyMgmt.strings); 1256 out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY); 1257 1258 if (config.connectChoices != null) { 1259 for (String key : config.connectChoices.keySet()) { 1260 Integer choice = config.connectChoices.get(key); 1261 out.writeUTF(CHOICE_KEY + key + "=" 1262 + choice.toString() + SEPARATOR_KEY); 1263 } 1264 } 1265 if (config.linkedConfigurations != null) { 1266 for (String key : config.linkedConfigurations.keySet()) { 1267 out.writeUTF(LINK_KEY + key + SEPARATOR_KEY); 1268 } 1269 } 1270 1271 if (config.getLinkProperties() != null) { 1272 String macAddress = config.defaultGwMacAddress; 1273 if (macAddress != null) { 1274 out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY); 1275 } 1276 } 1277 1278 if (config.scanResultCache != null) { 1279 for (ScanResult result : config.scanResultCache.values()) { 1280 out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY); 1281 1282 out.writeUTF(FREQ_KEY + Integer.toString(result.frequency) 1283 + SEPARATOR_KEY); 1284 1285 out.writeUTF(RSSI_KEY + Integer.toString(result.level) 1286 + SEPARATOR_KEY); 1287 1288 out.writeUTF(BSSID_STATUS_KEY + Integer.toString(result.status) 1289 + SEPARATOR_KEY); 1290 1291 if (result.seen != 0) { 1292 out.writeUTF(MILLI_KEY + Long.toString(result.seen) 1293 + SEPARATOR_KEY); 1294 } 1295 out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY); 1296 } 1297 } 1298 if (config.lastFailure != null) { 1299 out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY); 1300 } 1301 out.writeUTF(SEPARATOR_KEY); 1302 } 1303 } 1304 1305 }); 1306 } 1307 1308 public void setLastSelectedConfiguration(int netId) { 1309 if (DBG) { 1310 loge("setLastSelectedConfiguration " + Integer.toString(netId)); 1311 } 1312 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 1313 lastSelectedConfiguration = null; 1314 } else { 1315 WifiConfiguration selected = getWifiConfiguration(netId); 1316 if (selected == null) { 1317 lastSelectedConfiguration = null; 1318 } else { 1319 lastSelectedConfiguration = selected.configKey(); 1320 if (VDBG) { 1321 loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration); 1322 } 1323 } 1324 } 1325 } 1326 1327 public String getLastSelectedConfiguration() { 1328 return lastSelectedConfiguration; 1329 } 1330 1331 private void readNetworkHistory() { 1332 if (VDBG) { 1333 loge("readNetworkHistory path:" + networkHistoryConfigFile, true); 1334 } 1335 DataInputStream in = null; 1336 try { 1337 in = new DataInputStream(new BufferedInputStream(new FileInputStream( 1338 networkHistoryConfigFile))); 1339 WifiConfiguration config = null; 1340 while (true) { 1341 int id = -1; 1342 String key = in.readUTF(); 1343 String bssid = null; 1344 String ssid = null; 1345 1346 int freq = 0; 1347 int status = 0; 1348 long seen = 0; 1349 int rssi = WifiConfiguration.INVALID_RSSI; 1350 String caps = null; 1351 if (key.startsWith(CONFIG_KEY)) { 1352 1353 if (config != null) { 1354 config = null; 1355 } 1356 String configKey = key.replace(CONFIG_KEY, ""); 1357 configKey = configKey.replace(SEPARATOR_KEY, ""); 1358 // get the networkId for that config Key 1359 Integer n = mNetworkIds.get(configKey.hashCode()); 1360 // skip reading that configuration data 1361 // since we don't have a corresponding network ID 1362 if (n == null) { 1363 loge("readNetworkHistory didnt find netid for hash=" 1364 + Integer.toString(configKey.hashCode()) 1365 + " key: " + configKey); 1366 continue; 1367 } 1368 config = mConfiguredNetworks.get(n); 1369 if (config == null) { 1370 loge("readNetworkHistory didnt find config for netid=" 1371 + n.toString() 1372 + " key: " + configKey); 1373 } 1374 status = 0; 1375 ssid = null; 1376 bssid = null; 1377 freq = 0; 1378 seen = 0; 1379 rssi = WifiConfiguration.INVALID_RSSI; 1380 caps = null; 1381 1382 } else if (config != null) { 1383 if (key.startsWith(SSID_KEY)) { 1384 ssid = key.replace(SSID_KEY, ""); 1385 ssid = ssid.replace(SEPARATOR_KEY, ""); 1386 if (config.SSID != null && !config.SSID.equals(ssid)) { 1387 loge("Error parsing network history file, mismatched SSIDs"); 1388 config = null; //error 1389 ssid = null; 1390 } else { 1391 config.SSID = ssid; 1392 } 1393 } 1394 1395 if (key.startsWith(FQDN_KEY)) { 1396 String fqdn = key.replace(FQDN_KEY, ""); 1397 fqdn = fqdn.replace(SEPARATOR_KEY, ""); 1398 config.FQDN = fqdn; 1399 } 1400 1401 if (key.startsWith(DEFAULT_GW_KEY)) { 1402 String gateway = key.replace(DEFAULT_GW_KEY, ""); 1403 gateway = gateway.replace(SEPARATOR_KEY, ""); 1404 config.defaultGwMacAddress = gateway; 1405 } 1406 1407 if (key.startsWith(STATUS_KEY)) { 1408 String st = key.replace(STATUS_KEY, ""); 1409 st = st.replace(SEPARATOR_KEY, ""); 1410 config.autoJoinStatus = Integer.parseInt(st); 1411 } 1412 1413 /* 1414 if (key.startsWith(SUPPLICANT_STATUS_KEY)) { 1415 String status = key.replace(SUPPLICANT_STATUS_KEY, ""); 1416 status = status.replace(SEPARATOR_KEY, ""); 1417 config.status = Integer.parseInt(status); 1418 } 1419 1420 if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) { 1421 String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, ""); 1422 reason = reason.replace(SEPARATOR_KEY, ""); 1423 config.disableReason = Integer.parseInt(reason); 1424 }*/ 1425 1426 if (key.startsWith(SELF_ADDED_KEY)) { 1427 String selfAdded = key.replace(SELF_ADDED_KEY, ""); 1428 selfAdded = selfAdded.replace(SEPARATOR_KEY, ""); 1429 config.selfAdded = Boolean.parseBoolean(selfAdded); 1430 } 1431 1432 if (key.startsWith(DID_SELF_ADD_KEY)) { 1433 String didSelfAdd = key.replace(DID_SELF_ADD_KEY, ""); 1434 didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, ""); 1435 config.didSelfAdd = Boolean.parseBoolean(didSelfAdd); 1436 } 1437 1438 if (key.startsWith(CREATOR_UID_KEY)) { 1439 String uid = key.replace(CREATOR_UID_KEY, ""); 1440 uid = uid.replace(SEPARATOR_KEY, ""); 1441 config.creatorUid = Integer.parseInt(uid); 1442 } 1443 1444 if (key.startsWith(BLACKLIST_MILLI_KEY)) { 1445 String milli = key.replace(BLACKLIST_MILLI_KEY, ""); 1446 milli = milli.replace(SEPARATOR_KEY, ""); 1447 config.blackListTimestamp = Long.parseLong(milli); 1448 } 1449 1450 if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) { 1451 String num = key.replace(NUM_CONNECTION_FAILURES_KEY, ""); 1452 num = num.replace(SEPARATOR_KEY, ""); 1453 config.numConnectionFailures = Integer.parseInt(num); 1454 } 1455 1456 if (key.startsWith(SCORER_OVERRIDE_KEY)) { 1457 String num = key.replace(SCORER_OVERRIDE_KEY, ""); 1458 num = num.replace(SEPARATOR_KEY, ""); 1459 config.numScorerOverride = Integer.parseInt(num); 1460 } 1461 1462 if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) { 1463 String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, ""); 1464 num = num.replace(SEPARATOR_KEY, ""); 1465 config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num); 1466 } 1467 1468 if (key.startsWith(NUM_ASSOCIATION_KEY)) { 1469 String num = key.replace(NUM_ASSOCIATION_KEY, ""); 1470 num = num.replace(SEPARATOR_KEY, ""); 1471 config.numAssociation = Integer.parseInt(num); 1472 } 1473 1474 if (key.startsWith(CONNECT_UID_KEY)) { 1475 String uid = key.replace(CONNECT_UID_KEY, ""); 1476 uid = uid.replace(SEPARATOR_KEY, ""); 1477 config.lastConnectUid = Integer.parseInt(uid); 1478 } 1479 1480 if (key.startsWith(UPDATE_UID_KEY)) { 1481 String uid = key.replace(UPDATE_UID_KEY, ""); 1482 uid = uid.replace(SEPARATOR_KEY, ""); 1483 config.lastUpdateUid = Integer.parseInt(uid); 1484 } 1485 1486 if (key.startsWith(FAILURE_KEY)) { 1487 config.lastFailure = key.replace(FAILURE_KEY, ""); 1488 config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, ""); 1489 } 1490 1491 if (key.startsWith(PEER_CONFIGURATION_KEY)) { 1492 config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, ""); 1493 config.peerWifiConfiguration = 1494 config.peerWifiConfiguration.replace(SEPARATOR_KEY, ""); 1495 } 1496 1497 if (key.startsWith(CHOICE_KEY)) { 1498 String choiceStr = key.replace(CHOICE_KEY, ""); 1499 choiceStr = choiceStr.replace(SEPARATOR_KEY, ""); 1500 String configKey = ""; 1501 int choice = 0; 1502 Matcher match = mConnectChoice.matcher(choiceStr); 1503 if (!match.find()) { 1504 if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " + 1505 " Couldnt match pattern : " + choiceStr); 1506 } else { 1507 configKey = match.group(1); 1508 try { 1509 choice = Integer.parseInt(match.group(2)); 1510 } catch (NumberFormatException e) { 1511 choice = 0; 1512 } 1513 if (choice > 0) { 1514 if (config.connectChoices == null) { 1515 config.connectChoices = new HashMap<String, Integer>(); 1516 } 1517 config.connectChoices.put(configKey, choice); 1518 } 1519 } 1520 } 1521 1522 if (key.startsWith(LINK_KEY)) { 1523 String configKey = key.replace(LINK_KEY, ""); 1524 configKey = configKey.replace(SEPARATOR_KEY, ""); 1525 if (config.linkedConfigurations == null) { 1526 config.linkedConfigurations = new HashMap<String, Integer>(); 1527 } 1528 if (config.linkedConfigurations != null) { 1529 config.linkedConfigurations.put(configKey, -1); 1530 } 1531 } 1532 1533 if (key.startsWith(BSSID_KEY)) { 1534 if (key.startsWith(BSSID_KEY)) { 1535 bssid = key.replace(BSSID_KEY, ""); 1536 bssid = bssid.replace(SEPARATOR_KEY, ""); 1537 freq = 0; 1538 seen = 0; 1539 rssi = WifiConfiguration.INVALID_RSSI; 1540 caps = ""; 1541 status = 0; 1542 } 1543 1544 if (key.startsWith(RSSI_KEY)) { 1545 String lvl = key.replace(RSSI_KEY, ""); 1546 lvl = lvl.replace(SEPARATOR_KEY, ""); 1547 rssi = Integer.parseInt(lvl); 1548 } 1549 1550 if (key.startsWith(BSSID_STATUS_KEY)) { 1551 String st = key.replace(BSSID_STATUS_KEY, ""); 1552 st = st.replace(SEPARATOR_KEY, ""); 1553 status = Integer.parseInt(st); 1554 } 1555 1556 if (key.startsWith(FREQ_KEY)) { 1557 String channel = key.replace(FREQ_KEY, ""); 1558 channel = channel.replace(SEPARATOR_KEY, ""); 1559 freq = Integer.parseInt(channel); 1560 } 1561 1562 if (key.startsWith(DATE_KEY)) { 1563 /* 1564 * when reading the configuration from file we don't update the date 1565 * so as to avoid reading back stale or non-sensical data that would 1566 * depend on network time. 1567 * The date of a WifiConfiguration should only come from actual scan result. 1568 * 1569 String s = key.replace(FREQ_KEY, ""); 1570 seen = Integer.getInteger(s); 1571 */ 1572 } 1573 1574 if (key.startsWith(BSSID_KEY_END)) { 1575 1576 if ((bssid != null) && (ssid != null)) { 1577 1578 if (config.scanResultCache == null) { 1579 config.scanResultCache = new HashMap<String, ScanResult>(); 1580 } 1581 WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid); 1582 ScanResult result = new ScanResult(wssid, bssid, 1583 caps, rssi, freq, (long) 0); 1584 result.seen = seen; 1585 config.scanResultCache.put(bssid, result); 1586 result.status = status; 1587 } 1588 } 1589 } 1590 } 1591 } 1592 } catch (EOFException ignore) { 1593 if (in != null) { 1594 try { 1595 in.close(); 1596 } catch (Exception e) { 1597 loge("readNetworkHistory: Error reading file" + e); 1598 } 1599 } 1600 } catch (IOException e) { 1601 loge("readNetworkHistory: Error parsing configuration" + e); 1602 } 1603 1604 if(in!=null) { 1605 try { 1606 in.close(); 1607 } catch (Exception e) { 1608 loge("readNetworkHistory: Error closing file" + e); 1609 } 1610 } 1611 } 1612 1613 private void writeIpAndProxyConfigurations() { 1614 final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 1615 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1616 if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) { 1617 networks.put(configKey(config), config.getIpConfiguration()); 1618 } 1619 } 1620 1621 super.writeIpAndProxyConfigurations(ipConfigFile, networks); 1622 } 1623 1624 private void readIpAndProxyConfigurations() { 1625 SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile); 1626 1627 if (networks.size() == 0) { 1628 // IpConfigStore.readIpAndProxyConfigurations has already logged an error. 1629 return; 1630 } 1631 1632 for (int i = 0; i < networks.size(); i++) { 1633 Integer id = (Integer) i; 1634 WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id)); 1635 1636 1637 if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 1638 loge("configuration found for missing network, nid="+Integer.toString(id) 1639 +", ignored, networks.size=" + Integer.toString(networks.size())); 1640 } else { 1641 config.setIpConfiguration(networks.valueAt(i)); 1642 } 1643 } 1644 } 1645 1646 /* 1647 * Convert string to Hexadecimal before passing to wifi native layer 1648 * In native function "doCommand()" have trouble in converting Unicode character string to UTF8 1649 * conversion to hex is required because SSIDs can have space characters in them; 1650 * and that can confuses the supplicant because it uses space charaters as delimiters 1651 */ 1652 1653 private String encodeSSID(String str){ 1654 String tmp = removeDoubleQuotes(str); 1655 return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8")))); 1656 } 1657 1658 private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) { 1659 /* 1660 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1661 * network configuration. Otherwise, the networkId should 1662 * refer to an existing configuration. 1663 */ 1664 1665 if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); 1666 1667 int netId = config.networkId; 1668 boolean newNetwork = false; 1669 // networkId of INVALID_NETWORK_ID means we want to create a new network 1670 if (netId == INVALID_NETWORK_ID) { 1671 Integer savedNetId = mNetworkIds.get(configKey(config)); 1672 //paranoia: check if either we have a network Id or a WifiConfiguration 1673 //matching the one we are trying to add. 1674 if (savedNetId == null) { 1675 for (WifiConfiguration test : mConfiguredNetworks.values()) { 1676 if (test.configKey().equals(config.configKey())) { 1677 savedNetId = test.networkId; 1678 loge("addOrUpdateNetworkNative " + config.configKey() 1679 + " was found, but no network Id"); 1680 break; 1681 } 1682 } 1683 } 1684 if (savedNetId != null) { 1685 netId = savedNetId; 1686 } else { 1687 newNetwork = true; 1688 netId = mWifiNative.addNetwork(); 1689 if (netId < 0) { 1690 loge("Failed to add a network!"); 1691 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1692 } else { 1693 loge("addOrUpdateNetworkNative created netId=" + netId); 1694 } 1695 } 1696 } 1697 1698 boolean updateFailed = true; 1699 1700 setVariables: { 1701 1702 if (config.SSID != null && 1703 !mWifiNative.setNetworkVariable( 1704 netId, 1705 WifiConfiguration.ssidVarName, 1706 encodeSSID(config.SSID))) { 1707 loge("failed to set SSID: "+config.SSID); 1708 break setVariables; 1709 } 1710 1711 if (config.BSSID != null) { 1712 loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID); 1713 if (!mWifiNative.setNetworkVariable( 1714 netId, 1715 WifiConfiguration.bssidVarName, 1716 config.BSSID)) { 1717 loge("failed to set BSSID: " + config.BSSID); 1718 break setVariables; 1719 } 1720 } 1721 1722 String allowedKeyManagementString = 1723 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 1724 if (config.allowedKeyManagement.cardinality() != 0 && 1725 !mWifiNative.setNetworkVariable( 1726 netId, 1727 WifiConfiguration.KeyMgmt.varName, 1728 allowedKeyManagementString)) { 1729 loge("failed to set key_mgmt: "+ 1730 allowedKeyManagementString); 1731 break setVariables; 1732 } 1733 1734 String allowedProtocolsString = 1735 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 1736 if (config.allowedProtocols.cardinality() != 0 && 1737 !mWifiNative.setNetworkVariable( 1738 netId, 1739 WifiConfiguration.Protocol.varName, 1740 allowedProtocolsString)) { 1741 loge("failed to set proto: "+ 1742 allowedProtocolsString); 1743 break setVariables; 1744 } 1745 1746 String allowedAuthAlgorithmsString = 1747 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 1748 if (config.allowedAuthAlgorithms.cardinality() != 0 && 1749 !mWifiNative.setNetworkVariable( 1750 netId, 1751 WifiConfiguration.AuthAlgorithm.varName, 1752 allowedAuthAlgorithmsString)) { 1753 loge("failed to set auth_alg: "+ 1754 allowedAuthAlgorithmsString); 1755 break setVariables; 1756 } 1757 1758 String allowedPairwiseCiphersString = 1759 makeString(config.allowedPairwiseCiphers, 1760 WifiConfiguration.PairwiseCipher.strings); 1761 if (config.allowedPairwiseCiphers.cardinality() != 0 && 1762 !mWifiNative.setNetworkVariable( 1763 netId, 1764 WifiConfiguration.PairwiseCipher.varName, 1765 allowedPairwiseCiphersString)) { 1766 loge("failed to set pairwise: "+ 1767 allowedPairwiseCiphersString); 1768 break setVariables; 1769 } 1770 1771 String allowedGroupCiphersString = 1772 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 1773 if (config.allowedGroupCiphers.cardinality() != 0 && 1774 !mWifiNative.setNetworkVariable( 1775 netId, 1776 WifiConfiguration.GroupCipher.varName, 1777 allowedGroupCiphersString)) { 1778 loge("failed to set group: "+ 1779 allowedGroupCiphersString); 1780 break setVariables; 1781 } 1782 1783 // Prevent client screw-up by passing in a WifiConfiguration we gave it 1784 // by preventing "*" as a key. 1785 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 1786 !mWifiNative.setNetworkVariable( 1787 netId, 1788 WifiConfiguration.pskVarName, 1789 config.preSharedKey)) { 1790 loge("failed to set psk"); 1791 break setVariables; 1792 } 1793 1794 boolean hasSetKey = false; 1795 if (config.wepKeys != null) { 1796 for (int i = 0; i < config.wepKeys.length; i++) { 1797 // Prevent client screw-up by passing in a WifiConfiguration we gave it 1798 // by preventing "*" as a key. 1799 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 1800 if (!mWifiNative.setNetworkVariable( 1801 netId, 1802 WifiConfiguration.wepKeyVarNames[i], 1803 config.wepKeys[i])) { 1804 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 1805 break setVariables; 1806 } 1807 hasSetKey = true; 1808 } 1809 } 1810 } 1811 1812 if (hasSetKey) { 1813 if (!mWifiNative.setNetworkVariable( 1814 netId, 1815 WifiConfiguration.wepTxKeyIdxVarName, 1816 Integer.toString(config.wepTxKeyIndex))) { 1817 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 1818 break setVariables; 1819 } 1820 } 1821 1822 if (!mWifiNative.setNetworkVariable( 1823 netId, 1824 WifiConfiguration.priorityVarName, 1825 Integer.toString(config.priority))) { 1826 loge(config.SSID + ": failed to set priority: " 1827 +config.priority); 1828 break setVariables; 1829 } 1830 1831 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 1832 netId, 1833 WifiConfiguration.hiddenSSIDVarName, 1834 Integer.toString(config.hiddenSSID ? 1 : 0))) { 1835 loge(config.SSID + ": failed to set hiddenSSID: "+ 1836 config.hiddenSSID); 1837 break setVariables; 1838 } 1839 1840 if (config.requirePMF && !mWifiNative.setNetworkVariable( 1841 netId, 1842 WifiConfiguration.pmfVarName, 1843 "2")) { 1844 loge(config.SSID + ": failed to set requirePMF: "+ 1845 config.requirePMF); 1846 break setVariables; 1847 } 1848 1849 if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable( 1850 netId, 1851 WifiConfiguration.updateIdentiferVarName, 1852 config.updateIdentifier)) { 1853 loge(config.SSID + ": failed to set updateIdentifier: "+ 1854 config.updateIdentifier); 1855 break setVariables; 1856 } 1857 1858 if (config.enterpriseConfig != null && 1859 config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 1860 1861 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 1862 1863 if (needsKeyStore(enterpriseConfig)) { 1864 /** 1865 * Keyguard settings may eventually be controlled by device policy. 1866 * We check here if keystore is unlocked before installing 1867 * credentials. 1868 * TODO: Do we need a dialog here ? 1869 */ 1870 if (mKeyStore.state() != KeyStore.State.UNLOCKED) { 1871 loge(config.SSID + ": key store is locked"); 1872 break setVariables; 1873 } 1874 1875 try { 1876 /* config passed may include only fields being updated. 1877 * In order to generate the key id, fetch uninitialized 1878 * fields from the currently tracked configuration 1879 */ 1880 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1881 String keyId = config.getKeyIdForCredentials(currentConfig); 1882 1883 if (!installKeys(enterpriseConfig, keyId)) { 1884 loge(config.SSID + ": failed to install keys"); 1885 break setVariables; 1886 } 1887 } catch (IllegalStateException e) { 1888 loge(config.SSID + " invalid config for key installation"); 1889 break setVariables; 1890 } 1891 } 1892 1893 HashMap<String, String> enterpriseFields = enterpriseConfig.getFields(); 1894 for (String key : enterpriseFields.keySet()) { 1895 String value = enterpriseFields.get(key); 1896 if (key.equals("password") && value != null && value.equals("*")) { 1897 //no need to try to set an obfuscated password, which will fail 1898 continue; 1899 } 1900 if (!mWifiNative.setNetworkVariable( 1901 netId, 1902 key, 1903 value)) { 1904 removeKeys(enterpriseConfig); 1905 loge(config.SSID + ": failed to set " + key + 1906 ": " + value); 1907 break setVariables; 1908 } 1909 } 1910 } 1911 updateFailed = false; 1912 } //end of setVariables 1913 1914 if (updateFailed) { 1915 if (newNetwork) { 1916 mWifiNative.removeNetwork(netId); 1917 loge("Failed to set a network variable, removed network: " + netId); 1918 } 1919 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1920 } 1921 1922 /* An update of the network variables requires reading them 1923 * back from the supplicant to update mConfiguredNetworks. 1924 * This is because some of the variables (SSID, wep keys & 1925 * passphrases) reflect different values when read back than 1926 * when written. For example, wep key is stored as * irrespective 1927 * of the value sent to the supplicant 1928 */ 1929 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1930 if (currentConfig == null) { 1931 currentConfig = new WifiConfiguration(); 1932 currentConfig.setIpAssignment(IpAssignment.DHCP); 1933 currentConfig.setProxySettings(ProxySettings.NONE); 1934 currentConfig.networkId = netId; 1935 if (config != null) { 1936 //carry over the creation parameters 1937 currentConfig.selfAdded = config.selfAdded; 1938 currentConfig.didSelfAdd = config.didSelfAdd; 1939 currentConfig.lastConnectUid = config.lastConnectUid; 1940 currentConfig.lastUpdateUid = config.lastUpdateUid; 1941 currentConfig.creatorUid = config.creatorUid; 1942 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration; 1943 } 1944 if (DBG) { 1945 loge("created new config netId=" + Integer.toString(netId) 1946 + " uid=" + Integer.toString(currentConfig.creatorUid)); 1947 } 1948 } 1949 1950 if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 1951 //make sure the configuration is not deleted anymore since we just 1952 //added or modified it. 1953 currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 1954 currentConfig.selfAdded = false; 1955 currentConfig.didSelfAdd = false; 1956 } 1957 1958 if (DBG) loge("will read network variables netId=" + Integer.toString(netId)); 1959 1960 readNetworkVariables(currentConfig); 1961 1962 mConfiguredNetworks.put(netId, currentConfig); 1963 mNetworkIds.put(configKey(currentConfig), netId); 1964 1965 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config); 1966 result.setIsNewNetwork(newNetwork); 1967 result.setNetworkId(netId); 1968 return result; 1969 } 1970 1971 1972 public void linkConfiguration(WifiConfiguration config) { 1973 for (WifiConfiguration link : mConfiguredNetworks.values()) { 1974 boolean doLink = false; 1975 1976 if (link.configKey().equals(config.configKey())) { 1977 continue; 1978 } 1979 1980 if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 1981 continue; 1982 } 1983 1984 //autojoin will be allowed to dynamically jump from a linked configuration 1985 //to another, hence only link configurations that have equivalent level of security 1986 if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) { 1987 continue; 1988 } 1989 1990 if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) { 1991 //if both default GW are known, compare based on RSSI only if the GW is equal 1992 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) { 1993 1994 if (VDBG) { 1995 loge("linkConfiguration link due to same gw" + link.SSID + 1996 " and " + config.SSID + " GW " + config.defaultGwMacAddress); 1997 } 1998 doLink = true; 1999 } 2000 } else { 2001 // we do not know BOTH default gateways hence we will try to link 2002 // hoping that WifiConfigurations are indeed behind the same gateway 2003 // once both WifiConfiguration will have been tried we will know 2004 // the default gateway and revisit the choice of linking them 2005 if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6) 2006 && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) { 2007 String abssid = ""; 2008 String bbssid = ""; 2009 for (String key : config.scanResultCache.keySet()) { 2010 abssid = key; 2011 } 2012 for (String key : link.scanResultCache.keySet()) { 2013 bbssid = key; 2014 } 2015 if (VDBG) { 2016 loge("linkConfiguration try to link due to DBDC BSSID match " + link.SSID + 2017 " and " + config.SSID + " bssida " + abssid + " bssidb " + bbssid); 2018 } 2019 if (abssid.regionMatches(true, 0, bbssid, 0, 16)) { 2020 //if first 16 ascii characters of BSSID matches, we assume this is a DBDC 2021 doLink = true; 2022 } 2023 } 2024 } 2025 2026 if (doLink) { 2027 if (VDBG) { 2028 loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID); 2029 } 2030 if (link.linkedConfigurations == null) { 2031 link.linkedConfigurations = new HashMap<String, Integer>(); 2032 } 2033 if (config.linkedConfigurations == null) { 2034 config.linkedConfigurations = new HashMap<String, Integer>(); 2035 } 2036 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 2037 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 2038 } else { 2039 //todo if they are linked, break the link 2040 } 2041 } 2042 } 2043 2044 /* 2045 * We try to link a scan result with a WifiConfiguration for which SSID and ket management dont match, 2046 * for instance, we try identify the 5GHz SSID of a DBDC AP, even though we know only of the 2.4GHz 2047 * 2048 * Obviously, this function is not optimal since it is used to compare every scan 2049 * result with every Saved WifiConfiguration, with a string.equals operation. 2050 * As a speed up, might be better to implement the mConfiguredNetworks store as a 2051 * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object 2052 * so as to speed this up. Also to prevent the tiny probability of hash collision. 2053 * 2054 */ 2055 public WifiConfiguration associateWithConfiguration(ScanResult result) { 2056 String configKey = WifiConfiguration.configKey(result); 2057 if (configKey == null) { 2058 if (DBG) loge("associateWithConfiguration(): no config key " ); 2059 return null; 2060 } 2061 2062 //need to compare with quoted string 2063 String SSID = "\"" + result.SSID + "\""; 2064 2065 WifiConfiguration config = null; 2066 for (WifiConfiguration link : mConfiguredNetworks.values()) { 2067 boolean doLink = false; 2068 2069 if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) { 2070 //make sure we dont associate the scan result to a deleted config 2071 continue; 2072 } 2073 2074 if (configKey.equals(link.configKey())) { 2075 if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey ); 2076 return link; //found it exactly 2077 } 2078 2079 if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) { 2080 String bssid = ""; 2081 for (String key : link.scanResultCache.keySet()) { 2082 bssid = key; 2083 } 2084 2085 if (result.BSSID.regionMatches(true, 0, bssid, 0, 16) 2086 && SSID.regionMatches(false, 0, link.SSID, 0, 3)) { 2087 // if first 16 ascii characters of BSSID matches, and first 3 2088 // characters of SSID match, we assume this is a home setup 2089 // and thus we will try to transfer the password from the known 2090 // BSSID/SSID to the recently found BSSID/SSID 2091 2092 //if (VDBG) 2093 // loge("associateWithConfiguration OK " ); 2094 doLink = true; 2095 } 2096 } 2097 2098 if (doLink) { 2099 //try to make a non verified WifiConfiguration, but only if the original 2100 //configuration was not self already added 2101 if (VDBG) { 2102 loge("associateWithConfiguration: will create " + 2103 result.SSID + " and associate it with: " + link.SSID); 2104 } 2105 config = wifiConfigurationFromScanResult(result); 2106 if (config != null) { 2107 config.selfAdded = true; 2108 config.didSelfAdd = true; 2109 config.peerWifiConfiguration = link.configKey(); 2110 if (config.allowedKeyManagement.equals(link.allowedKeyManagement) && 2111 config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 2112 //transfer the credentials from the configuration we are linking from 2113 String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk"); 2114 if (psk != null) { 2115 config.preSharedKey = psk; 2116 if (VDBG) { 2117 if (config.preSharedKey != null) 2118 loge(" transfer PSK : " + config.preSharedKey); 2119 } 2120 2121 //link configurations 2122 if (link.linkedConfigurations == null) { 2123 link.linkedConfigurations = new HashMap<String, Integer>(); 2124 } 2125 if (config.linkedConfigurations == null) { 2126 config.linkedConfigurations = new HashMap<String, Integer>(); 2127 } 2128 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 2129 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 2130 } else { 2131 config = null; 2132 } 2133 } else { 2134 config = null; 2135 } 2136 } 2137 } else { 2138 //todo if they are linked, break the link 2139 } 2140 } 2141 return config; 2142 } 2143 2144 public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) { 2145 if (config == null) 2146 return null; 2147 long now_ms = System.currentTimeMillis(); 2148 2149 HashSet<Integer> channels = new HashSet<Integer>(); 2150 2151 //get channels for this configuration, if there are at least 2 BSSIDs 2152 if (config.scanResultCache == null && config.linkedConfigurations == null) { 2153 return null; 2154 } 2155 2156 if (VDBG) { 2157 StringBuilder dbg = new StringBuilder(); 2158 dbg.append("makeChannelList age=" + Integer.toString(age) + " for " + config.configKey()); 2159 if (config.scanResultCache != null) { 2160 dbg.append(" bssids=" + config.scanResultCache.size()); 2161 } 2162 if (config.linkedConfigurations != null) { 2163 dbg.append(" linked=" + config.linkedConfigurations.size()); 2164 } 2165 loge(dbg.toString()); 2166 } 2167 2168 if (config.scanResultCache != null && config.scanResultCache.size() > 0) { 2169 for (ScanResult result : config.scanResultCache.values()) { 2170 if (VDBG) { 2171 boolean test = (now_ms - result.seen) < age; 2172 loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency) 2173 + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test); 2174 } 2175 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2176 channels.add(result.frequency); 2177 } 2178 } 2179 } 2180 2181 //get channels for linked configurations 2182 if (config.linkedConfigurations != null) { 2183 for (String key : config.linkedConfigurations.keySet()) { 2184 WifiConfiguration linked = getWifiConfiguration(key); 2185 if (linked == null) 2186 continue; 2187 if (linked.scanResultCache == null) { 2188 continue; 2189 } 2190 for (ScanResult result : linked.scanResultCache.values()) { 2191 if (VDBG) { 2192 loge("has link: " + result.BSSID 2193 + " freq=" + Integer.toString(result.frequency) 2194 + " age=" + Long.toString(now_ms - result.seen)); 2195 } 2196 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2197 channels.add(result.frequency); 2198 } 2199 } 2200 } 2201 } 2202 return channels; 2203 } 2204 2205 public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) { 2206 WifiConfiguration found = null; 2207 if (scanResult == null) 2208 return found; 2209 2210 //first step, look for this scan Result by SSID + Key Management 2211 String key = WifiConfiguration.configKey(scanResult); 2212 int hash = key.hashCode(); 2213 2214 Integer netId = mNetworkIds.get(hash); 2215 if (netId == null) return null; 2216 WifiConfiguration config = mConfiguredNetworks.get(netId); 2217 if (config != null) { 2218 if (config.scanResultCache == null) { 2219 config.scanResultCache = new HashMap<String, ScanResult>(); 2220 } 2221 if (config.scanResultCache == null) { 2222 return null; 2223 } 2224 //add the scan result to this WifiConfiguration 2225 config.scanResultCache.put(scanResult.BSSID, scanResult); 2226 mConfiguredNetworks.put(netId, config); 2227 linkConfiguration(config); 2228 found = config; 2229 } 2230 2231 if (VDBG) { 2232 config = mConfiguredNetworks.get(netId); 2233 if (config != null) { 2234 if (config.scanResultCache != null) { 2235 String status = ""; 2236 if (scanResult.status > 0) { 2237 status = " status=" + Integer.toString(scanResult.status); 2238 } 2239 loge(" got known scan result " + 2240 scanResult.BSSID + " key : " + key + " num: " + 2241 Integer.toString(config.scanResultCache.size()) 2242 + " rssi=" + Integer.toString(scanResult.level) 2243 + " freq=" + Integer.toString(scanResult.frequency) 2244 + status); 2245 } else { 2246 loge(" got known scan result and no cache" + 2247 scanResult.BSSID + " key : " + key); 2248 } 2249 } 2250 } 2251 return found; 2252 2253 } 2254 2255 /* Compare current and new configuration and write to file on change */ 2256 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 2257 WifiConfiguration currentConfig, 2258 WifiConfiguration newConfig) { 2259 boolean ipChanged = false; 2260 boolean proxyChanged = false; 2261 LinkProperties linkProperties = null; 2262 2263 if (VDBG) { 2264 loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " + 2265 newConfig.SSID + " path: " + ipConfigFile); 2266 } 2267 2268 2269 switch (newConfig.getIpAssignment()) { 2270 case STATIC: 2271 Collection<LinkAddress> currentLinkAddresses = currentConfig.getLinkProperties() 2272 .getLinkAddresses(); 2273 Collection<LinkAddress> newLinkAddresses = newConfig.getLinkProperties() 2274 .getLinkAddresses(); 2275 Collection<InetAddress> currentDnses = 2276 currentConfig.getLinkProperties().getDnsServers(); 2277 Collection<InetAddress> newDnses = newConfig.getLinkProperties().getDnsServers(); 2278 Collection<RouteInfo> currentRoutes = currentConfig.getLinkProperties().getRoutes(); 2279 Collection<RouteInfo> newRoutes = newConfig.getLinkProperties().getRoutes(); 2280 2281 boolean linkAddressesDiffer = 2282 (currentLinkAddresses.size() != newLinkAddresses.size()) || 2283 !currentLinkAddresses.containsAll(newLinkAddresses); 2284 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || 2285 !currentDnses.containsAll(newDnses); 2286 boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) || 2287 !currentRoutes.containsAll(newRoutes); 2288 2289 if ((currentConfig.getIpAssignment() != newConfig.getIpAssignment()) || 2290 linkAddressesDiffer || 2291 dnsesDiffer || 2292 routesDiffer) { 2293 ipChanged = true; 2294 } 2295 break; 2296 case DHCP: 2297 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 2298 ipChanged = true; 2299 } 2300 break; 2301 case UNASSIGNED: 2302 /* Ignore */ 2303 break; 2304 default: 2305 loge("Ignore invalid ip assignment during write"); 2306 break; 2307 } 2308 2309 switch (newConfig.getProxySettings()) { 2310 case STATIC: 2311 case PAC: 2312 ProxyInfo newHttpProxy = newConfig.getLinkProperties().getHttpProxy(); 2313 ProxyInfo currentHttpProxy = currentConfig.getLinkProperties().getHttpProxy(); 2314 2315 if (newHttpProxy != null) { 2316 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 2317 } else { 2318 proxyChanged = (currentHttpProxy != null); 2319 } 2320 break; 2321 case NONE: 2322 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) { 2323 proxyChanged = true; 2324 } 2325 break; 2326 case UNASSIGNED: 2327 /* Ignore */ 2328 break; 2329 default: 2330 loge("Ignore invalid proxy configuration during write"); 2331 break; 2332 } 2333 2334 if (!ipChanged) { 2335 linkProperties = copyIpSettingsFromConfig(currentConfig); 2336 } else { 2337 currentConfig.setIpAssignment(newConfig.getIpAssignment()); 2338 linkProperties = copyIpSettingsFromConfig(newConfig); 2339 log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " + 2340 linkProperties.toString()); 2341 } 2342 2343 2344 if (!proxyChanged) { 2345 linkProperties.setHttpProxy(currentConfig.getLinkProperties().getHttpProxy()); 2346 } else { 2347 currentConfig.setProxySettings(newConfig.getProxySettings()); 2348 linkProperties.setHttpProxy(newConfig.getLinkProperties().getHttpProxy()); 2349 log("proxy changed SSID = " + currentConfig.SSID); 2350 if (linkProperties.getHttpProxy() != null) { 2351 log(" proxyProperties: " + linkProperties.getHttpProxy().toString()); 2352 } 2353 } 2354 2355 if (ipChanged || proxyChanged) { 2356 currentConfig.setLinkProperties(linkProperties); 2357 writeIpAndProxyConfigurations(); 2358 sendConfiguredNetworksChangedBroadcast(currentConfig, 2359 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 2360 } 2361 return new NetworkUpdateResult(ipChanged, proxyChanged); 2362 } 2363 2364 private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) { 2365 LinkProperties linkProperties = new LinkProperties(); 2366 linkProperties.setInterfaceName(config.getLinkProperties().getInterfaceName()); 2367 for (LinkAddress linkAddr : config.getLinkProperties().getLinkAddresses()) { 2368 linkProperties.addLinkAddress(linkAddr); 2369 } 2370 for (RouteInfo route : config.getLinkProperties().getRoutes()) { 2371 linkProperties.addRoute(route); 2372 } 2373 for (InetAddress dns : config.getLinkProperties().getDnsServers()) { 2374 linkProperties.addDnsServer(dns); 2375 } 2376 return linkProperties; 2377 } 2378 2379 /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */ 2380 private boolean enterpriseConfigKeyShouldBeQuoted(String key) { 2381 switch (key) { 2382 case WifiEnterpriseConfig.EAP_KEY: 2383 case WifiEnterpriseConfig.ENGINE_KEY: 2384 return false; 2385 default: 2386 return true; 2387 } 2388 } 2389 2390 /** 2391 * Read the variables from the supplicant daemon that are needed to 2392 * fill in the WifiConfiguration object. 2393 * 2394 * @param config the {@link WifiConfiguration} object to be filled in. 2395 */ 2396 private void readNetworkVariables(WifiConfiguration config) { 2397 2398 int netId = config.networkId; 2399 if (netId < 0) 2400 return; 2401 2402 /* 2403 * TODO: maybe should have a native method that takes an array of 2404 * variable names and returns an array of values. But we'd still 2405 * be doing a round trip to the supplicant daemon for each variable. 2406 */ 2407 String value; 2408 2409 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 2410 if (!TextUtils.isEmpty(value)) { 2411 if (value.charAt(0) != '"') { 2412 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 2413 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 2414 //supplicant string 2415 } else { 2416 config.SSID = value; 2417 } 2418 } else { 2419 config.SSID = null; 2420 } 2421 2422 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 2423 if (!TextUtils.isEmpty(value)) { 2424 config.BSSID = value; 2425 } else { 2426 config.BSSID = null; 2427 } 2428 2429 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 2430 config.priority = -1; 2431 if (!TextUtils.isEmpty(value)) { 2432 try { 2433 config.priority = Integer.parseInt(value); 2434 } catch (NumberFormatException ignore) { 2435 } 2436 } 2437 2438 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 2439 config.hiddenSSID = false; 2440 if (!TextUtils.isEmpty(value)) { 2441 try { 2442 config.hiddenSSID = Integer.parseInt(value) != 0; 2443 } catch (NumberFormatException ignore) { 2444 } 2445 } 2446 2447 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 2448 config.wepTxKeyIndex = -1; 2449 if (!TextUtils.isEmpty(value)) { 2450 try { 2451 config.wepTxKeyIndex = Integer.parseInt(value); 2452 } catch (NumberFormatException ignore) { 2453 } 2454 } 2455 2456 for (int i = 0; i < 4; i++) { 2457 value = mWifiNative.getNetworkVariable(netId, 2458 WifiConfiguration.wepKeyVarNames[i]); 2459 if (!TextUtils.isEmpty(value)) { 2460 config.wepKeys[i] = value; 2461 } else { 2462 config.wepKeys[i] = null; 2463 } 2464 } 2465 2466 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 2467 if (!TextUtils.isEmpty(value)) { 2468 config.preSharedKey = value; 2469 } else { 2470 config.preSharedKey = null; 2471 } 2472 2473 value = mWifiNative.getNetworkVariable(config.networkId, 2474 WifiConfiguration.Protocol.varName); 2475 if (!TextUtils.isEmpty(value)) { 2476 String vals[] = value.split(" "); 2477 for (String val : vals) { 2478 int index = 2479 lookupString(val, WifiConfiguration.Protocol.strings); 2480 if (0 <= index) { 2481 config.allowedProtocols.set(index); 2482 } 2483 } 2484 } 2485 2486 value = mWifiNative.getNetworkVariable(config.networkId, 2487 WifiConfiguration.KeyMgmt.varName); 2488 if (!TextUtils.isEmpty(value)) { 2489 String vals[] = value.split(" "); 2490 for (String val : vals) { 2491 int index = 2492 lookupString(val, WifiConfiguration.KeyMgmt.strings); 2493 if (0 <= index) { 2494 config.allowedKeyManagement.set(index); 2495 } 2496 } 2497 } 2498 2499 value = mWifiNative.getNetworkVariable(config.networkId, 2500 WifiConfiguration.AuthAlgorithm.varName); 2501 if (!TextUtils.isEmpty(value)) { 2502 String vals[] = value.split(" "); 2503 for (String val : vals) { 2504 int index = 2505 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 2506 if (0 <= index) { 2507 config.allowedAuthAlgorithms.set(index); 2508 } 2509 } 2510 } 2511 2512 value = mWifiNative.getNetworkVariable(config.networkId, 2513 WifiConfiguration.PairwiseCipher.varName); 2514 if (!TextUtils.isEmpty(value)) { 2515 String vals[] = value.split(" "); 2516 for (String val : vals) { 2517 int index = 2518 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 2519 if (0 <= index) { 2520 config.allowedPairwiseCiphers.set(index); 2521 } 2522 } 2523 } 2524 2525 value = mWifiNative.getNetworkVariable(config.networkId, 2526 WifiConfiguration.GroupCipher.varName); 2527 if (!TextUtils.isEmpty(value)) { 2528 String vals[] = value.split(" "); 2529 for (String val : vals) { 2530 int index = 2531 lookupString(val, WifiConfiguration.GroupCipher.strings); 2532 if (0 <= index) { 2533 config.allowedGroupCiphers.set(index); 2534 } 2535 } 2536 } 2537 2538 if (config.enterpriseConfig == null) { 2539 config.enterpriseConfig = new WifiEnterpriseConfig(); 2540 } 2541 HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields(); 2542 for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) { 2543 value = mWifiNative.getNetworkVariable(netId, key); 2544 if (!TextUtils.isEmpty(value)) { 2545 if (!enterpriseConfigKeyShouldBeQuoted(key)) { 2546 value = removeDoubleQuotes(value); 2547 } 2548 enterpriseFields.put(key, value); 2549 } else { 2550 enterpriseFields.put(key, EMPTY_VALUE); 2551 } 2552 } 2553 2554 if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) { 2555 saveConfig(); 2556 } 2557 2558 migrateCerts(config.enterpriseConfig); 2559 // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore); 2560 } 2561 2562 private static String removeDoubleQuotes(String string) { 2563 int length = string.length(); 2564 if ((length > 1) && (string.charAt(0) == '"') 2565 && (string.charAt(length - 1) == '"')) { 2566 return string.substring(1, length - 1); 2567 } 2568 return string; 2569 } 2570 2571 private static String makeString(BitSet set, String[] strings) { 2572 StringBuffer buf = new StringBuffer(); 2573 int nextSetBit = -1; 2574 2575 /* Make sure all set bits are in [0, strings.length) to avoid 2576 * going out of bounds on strings. (Shouldn't happen, but...) */ 2577 set = set.get(0, strings.length); 2578 2579 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 2580 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 2581 } 2582 2583 // remove trailing space 2584 if (set.cardinality() > 0) { 2585 buf.setLength(buf.length() - 1); 2586 } 2587 2588 return buf.toString(); 2589 } 2590 2591 private int lookupString(String string, String[] strings) { 2592 int size = strings.length; 2593 2594 string = string.replace('-', '_'); 2595 2596 for (int i = 0; i < size; i++) 2597 if (string.equals(strings[i])) 2598 return i; 2599 2600 // if we ever get here, we should probably add the 2601 // value to WifiConfiguration to reflect that it's 2602 // supported by the WPA supplicant 2603 loge("Failed to look-up a string: " + string); 2604 2605 return -1; 2606 } 2607 2608 /* return the allowed key management based on a scan result */ 2609 2610 public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 2611 WifiConfiguration config = new WifiConfiguration(); 2612 2613 config.SSID = "\"" + result.SSID + "\""; 2614 2615 if (VDBG) { 2616 loge("WifiConfiguration from scan results " + 2617 config.SSID + " cap " + result.capabilities); 2618 } 2619 if (result.capabilities.contains("WEP")) { 2620 config.allowedKeyManagement.set(KeyMgmt.NONE); 2621 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //? 2622 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 2623 } 2624 2625 if (result.capabilities.contains("PSK")) { 2626 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 2627 } 2628 2629 if (result.capabilities.contains("EAP")) { 2630 //this is probably wrong, as we don't have a way to enter the enterprise config 2631 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 2632 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 2633 } 2634 2635 config.scanResultCache = new HashMap<String, ScanResult>(); 2636 if (config.scanResultCache == null) 2637 return null; 2638 config.scanResultCache.put(result.BSSID, result); 2639 2640 return config; 2641 } 2642 2643 2644 /* Returns a unique for a given configuration */ 2645 private static int configKey(WifiConfiguration config) { 2646 String key = config.configKey(); 2647 return key.hashCode(); 2648 } 2649 2650 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2651 pw.println("WifiConfigStore"); 2652 pw.println("mLastPriority " + mLastPriority); 2653 pw.println("Configured networks"); 2654 for (WifiConfiguration conf : getConfiguredNetworks()) { 2655 pw.println(conf); 2656 } 2657 pw.println(); 2658 2659 if (mLocalLog != null) { 2660 pw.println("WifiConfigStore - Log Begin ----"); 2661 mLocalLog.dump(fd, pw, args); 2662 pw.println("WifiConfigStore - Log End ----"); 2663 } 2664 } 2665 2666 public String getConfigFile() { 2667 return ipConfigFile; 2668 } 2669 2670 protected void loge(String s) { 2671 loge(s, false); 2672 } 2673 2674 protected void loge(String s, boolean stack) { 2675 if (stack) { 2676 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2677 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 2678 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 2679 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 2680 } else { 2681 Log.e(TAG, s); 2682 } 2683 } 2684 2685 protected void log(String s) { 2686 Log.d(TAG, s); 2687 } 2688 2689 private void localLog(String s) { 2690 if (mLocalLog != null) { 2691 mLocalLog.log(s); 2692 } 2693 } 2694 2695 private void localLog(String s, int netId) { 2696 if (mLocalLog == null) { 2697 return; 2698 } 2699 2700 WifiConfiguration config; 2701 synchronized(mConfiguredNetworks) { 2702 config = mConfiguredNetworks.get(netId); 2703 } 2704 2705 if (config != null) { 2706 mLocalLog.log(s + " " + config.getPrintableSsid()); 2707 } else { 2708 mLocalLog.log(s + " " + netId); 2709 } 2710 } 2711 2712 // Certificate and private key management for EnterpriseConfig 2713 static boolean needsKeyStore(WifiEnterpriseConfig config) { 2714 // Has no keys to be installed 2715 if (config.getClientCertificate() == null && config.getCaCertificate() == null) 2716 return false; 2717 return true; 2718 } 2719 2720 static boolean isHardwareBackedKey(PrivateKey key) { 2721 return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); 2722 } 2723 2724 static boolean hasHardwareBackedKey(Certificate certificate) { 2725 return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); 2726 } 2727 2728 static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 2729 String client = config.getClientCertificateAlias(); 2730 if (!TextUtils.isEmpty(client)) { 2731 // a valid client certificate is configured 2732 2733 // BUGBUG: keyStore.get() never returns certBytes; because it is not 2734 // taking WIFI_UID as a parameter. It always looks for certificate 2735 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 2736 // all certificates need software keystore until we get the get() API 2737 // fixed. 2738 2739 return true; 2740 } 2741 2742 /* 2743 try { 2744 2745 if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials 2746 .USER_CERTIFICATE + client); 2747 2748 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 2749 if (factory == null) { 2750 Slog.e(TAG, "Error getting certificate factory"); 2751 return; 2752 } 2753 2754 byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); 2755 if (certBytes != null) { 2756 Certificate cert = (X509Certificate) factory.generateCertificate( 2757 new ByteArrayInputStream(certBytes)); 2758 2759 if (cert != null) { 2760 mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); 2761 2762 if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials 2763 .USER_CERTIFICATE + client); 2764 if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : 2765 "does not need" ) + " software key store"); 2766 } else { 2767 Slog.d(TAG, "could not generate certificate"); 2768 } 2769 } else { 2770 Slog.e(TAG, "Could not load client certificate " + Credentials 2771 .USER_CERTIFICATE + client); 2772 mNeedsSoftwareKeystore = true; 2773 } 2774 2775 } catch(CertificateException e) { 2776 Slog.e(TAG, "Could not read certificates"); 2777 mCaCert = null; 2778 mClientCertificate = null; 2779 } 2780 */ 2781 2782 return false; 2783 } 2784 2785 /** called when CS ask WiFistateMachine to disconnect the current network 2786 * because the score is bad. 2787 */ 2788 void handleBadNetworkDisconnectReport(int netId, WifiInfo info) { 2789 /* TODO verify the bad network is current */ 2790 WifiConfiguration config = mConfiguredNetworks.get(netId); 2791 if (config != null) { 2792 if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24 2793 && info.is24GHz()) || (info.getRssi() < 2794 WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) { 2795 //we got disconnected and RSSI was bad, so disable light 2796 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED 2797 + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP); 2798 loge("handleBadNetworkDisconnectReport (+4) " 2799 + Integer.toString(netId) + " " + info); 2800 } else { 2801 //we got disabled but RSSI is good, so disable hard 2802 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED 2803 + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP); 2804 loge("handleBadNetworkDisconnectReport (+8) " 2805 + Integer.toString(netId) + " " + info); 2806 } 2807 } 2808 } 2809 2810 boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) { 2811 boolean found = false; 2812 if (BSSID == null) 2813 return found; 2814 2815 // Look for the BSSID in our config store 2816 for (WifiConfiguration config : mConfiguredNetworks.values()) { 2817 if (config.scanResultCache != null) { 2818 for (ScanResult result: config.scanResultCache.values()) { 2819 if (result.BSSID.equals(BSSID)) { 2820 if (enable) { 2821 result.status = ScanResult.ENABLED; 2822 } else { 2823 // Black list the BSSID we we were trying to join 2824 // so as the Roam state machine 2825 // doesn't pick it up over and over 2826 result.status = ScanResult.AUTO_ROAM_DISABLED; 2827 found = true; 2828 } 2829 } 2830 } 2831 } 2832 } 2833 return found; 2834 } 2835 2836 int getMaxDhcpRetries() { 2837 return Settings.Global.getInt(mContext.getContentResolver(), 2838 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 2839 DEFAULT_MAX_DHCP_RETRIES); 2840 } 2841 2842 void handleSSIDStateChange(int netId, boolean enabled, String message) { 2843 WifiConfiguration config = mConfiguredNetworks.get(netId); 2844 if (config != null) { 2845 if (enabled) { 2846 loge("SSID re-enabled for " + config.configKey() + 2847 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 2848 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 2849 //TODO: http://b/16381983 Fix Wifi Network Blacklisting 2850 //TODO: really I don't know if re-enabling is right but we 2851 //TODO: should err on the side of trying to connect 2852 //TODO: even if the attempt will fail 2853 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 2854 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 2855 } 2856 } else { 2857 loge("SSID temp disabled for " + config.configKey() + 2858 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 2859 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 2860 if (message != null) { 2861 loge(" message=" + message); 2862 } 2863 if (config.selfAdded && config.lastConnected == 0) { 2864 // This is a network we self added, and we never succeeded, 2865 // the user did not create this network and never entered its credentials, 2866 // so we want to be very aggressive in disabling it completely. 2867 disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE); 2868 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 2869 config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE; 2870 } else { 2871 if (message != null) { 2872 if (message.contains("WRONG_KEY") 2873 || message.contains("AUTH_FAILED")) { 2874 // This configuration has received an auth failure, so disable it 2875 // temporarily because we don't want auto-join to try it out. 2876 // this network may be re-enabled by the "usual" 2877 // enableAllNetwork function 2878 //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE: 2879 //TODO: if we could count on the wrong_ley or auth_failure 2880 //TODO: message to be correct 2881 //TODO: then we could just mark the configuration as 2882 //TODO: DISABLED_ON_AUTH_FAILURE 2883 //TODO: and the configuration will stay there until 2884 //TODO: user enter new credentials 2885 //TODO: It is not the case however, so instead of disabling, let's 2886 //TODO: start blacklisting hard 2887 //TODO: http://b/16381983 Fix Wifi Network Blacklisting 2888 if (config.autoJoinStatus <= 2889 WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 2890 // 4 auth failure will reach 128 and disable permanently 2891 // autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128 2892 config.setAutoJoinStatus(4 + config.autoJoinStatus * 4); 2893 if (config.autoJoinStatus > 2894 WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) 2895 config.setAutoJoinStatus 2896 (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 2897 } 2898 if (DBG) { 2899 loge("blacklisted " + config.configKey() + " to " 2900 + Integer.toString(config.autoJoinStatus)); 2901 } 2902 } else if (message.contains("DHCP FAILURE")) { 2903 config.numConnectionFailures++; 2904 config.lastConnectionFailure = System.currentTimeMillis(); 2905 int maxRetries = getMaxDhcpRetries(); 2906 // maxRetries == 0 means keep trying forever 2907 if (maxRetries > 0 && config.numConnectionFailures > maxRetries) { 2908 /** 2909 * If we've exceeded the maximum number of retries for DHCP 2910 * to a given network, disable the network 2911 */ 2912 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 2913 disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE); 2914 } 2915 if (DBG) { 2916 loge("blacklisted " + config.configKey() + " to " 2917 + config.autoJoinStatus 2918 + " due to DHCP failure, count=" 2919 + config.numConnectionFailures); 2920 } 2921 } 2922 message.replace("\n", ""); 2923 message.replace("\r", ""); 2924 config.lastFailure = message; 2925 } 2926 } 2927 } 2928 } 2929 } 2930 2931 boolean installKeys(WifiEnterpriseConfig config, String name) { 2932 boolean ret = true; 2933 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 2934 String userCertName = Credentials.USER_CERTIFICATE + name; 2935 String caCertName = Credentials.CA_CERTIFICATE + name; 2936 if (config.getClientCertificate() != null) { 2937 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 2938 if (isHardwareBackedKey(config.getClientPrivateKey())) { 2939 // Hardware backed key store is secure enough to store keys un-encrypted, this 2940 // removes the need for user to punch a PIN to get access to these keys 2941 if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store"); 2942 ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID, 2943 KeyStore.FLAG_NONE); 2944 } else { 2945 // Software backed key store is NOT secure enough to store keys un-encrypted. 2946 // Save keys encrypted so they are protected with user's PIN. User will 2947 // have to unlock phone before being able to use these keys and connect to 2948 // networks. 2949 if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store"); 2950 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 2951 KeyStore.FLAG_ENCRYPTED); 2952 } 2953 if (ret == false) { 2954 return ret; 2955 } 2956 2957 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 2958 if (ret == false) { 2959 // Remove private key installed 2960 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 2961 return ret; 2962 } 2963 } 2964 2965 if (config.getCaCertificate() != null) { 2966 ret = putCertInKeyStore(caCertName, config.getCaCertificate()); 2967 if (ret == false) { 2968 if (config.getClientCertificate() != null) { 2969 // Remove client key+cert 2970 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 2971 mKeyStore.delete(userCertName, Process.WIFI_UID); 2972 } 2973 return ret; 2974 } 2975 } 2976 2977 // Set alias names 2978 if (config.getClientCertificate() != null) { 2979 config.setClientCertificateAlias(name); 2980 config.resetClientKeyEntry(); 2981 } 2982 2983 if (config.getCaCertificate() != null) { 2984 config.setCaCertificateAlias(name); 2985 config.resetCaCertificate(); 2986 } 2987 2988 return ret; 2989 } 2990 2991 private boolean putCertInKeyStore(String name, Certificate cert) { 2992 try { 2993 byte[] certData = Credentials.convertToPem(cert); 2994 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 2995 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 2996 2997 } catch (IOException e1) { 2998 return false; 2999 } catch (CertificateException e2) { 3000 return false; 3001 } 3002 } 3003 3004 void removeKeys(WifiEnterpriseConfig config) { 3005 String client = config.getClientCertificateAlias(); 3006 // a valid client certificate is configured 3007 if (!TextUtils.isEmpty(client)) { 3008 if (DBG) Log.d(TAG, "removing client private key and user cert"); 3009 mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 3010 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 3011 } 3012 3013 String ca = config.getCaCertificateAlias(); 3014 // a valid ca certificate is configured 3015 if (!TextUtils.isEmpty(ca)) { 3016 if (DBG) Log.d(TAG, "removing CA cert"); 3017 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 3018 } 3019 } 3020 3021 3022 /** Migrates the old style TLS config to the new config style. This should only be used 3023 * when restoring an old wpa_supplicant.conf or upgrading from a previous 3024 * platform version. 3025 * @return true if the config was updated 3026 * @hide 3027 */ 3028 boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) { 3029 String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); 3030 /* 3031 * If the old configuration value is not present, then there is nothing 3032 * to do. 3033 */ 3034 if (TextUtils.isEmpty(oldPrivateKey)) { 3035 return false; 3036 } else { 3037 // Also ignore it if it's empty quotes. 3038 oldPrivateKey = removeDoubleQuotes(oldPrivateKey); 3039 if (TextUtils.isEmpty(oldPrivateKey)) { 3040 return false; 3041 } 3042 } 3043 3044 config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE); 3045 config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, 3046 WifiEnterpriseConfig.ENGINE_ID_KEYSTORE); 3047 3048 /* 3049 * The old key started with the keystore:// URI prefix, but we don't 3050 * need that anymore. Trim it off if it exists. 3051 */ 3052 final String keyName; 3053 if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) { 3054 keyName = new String( 3055 oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length())); 3056 } else { 3057 keyName = oldPrivateKey; 3058 } 3059 config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName); 3060 3061 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY, 3062 config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, "")); 3063 3064 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY, 3065 config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, "")); 3066 3067 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, 3068 config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, "")); 3069 3070 // Remove old private_key string so we don't run this again. 3071 mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); 3072 3073 return true; 3074 } 3075 3076 /** Migrate certs from global pool to wifi UID if not already done */ 3077 void migrateCerts(WifiEnterpriseConfig config) { 3078 String client = config.getClientCertificateAlias(); 3079 // a valid client certificate is configured 3080 if (!TextUtils.isEmpty(client)) { 3081 if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { 3082 mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, 3083 Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 3084 mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, 3085 Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 3086 } 3087 } 3088 3089 String ca = config.getCaCertificateAlias(); 3090 // a valid ca certificate is configured 3091 if (!TextUtils.isEmpty(ca)) { 3092 if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { 3093 mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, 3094 Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 3095 } 3096 } 3097 } 3098 3099} 3100