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