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