WifiConfigStore.java revision c6f06c628ee3583b60ff31a7da442e0ac7b26d97
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.enterpriseConfig != null && 1677 config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 1678 1679 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 1680 1681 if (needsKeyStore(enterpriseConfig)) { 1682 /** 1683 * Keyguard settings may eventually be controlled by device policy. 1684 * We check here if keystore is unlocked before installing 1685 * credentials. 1686 * TODO: Do we need a dialog here ? 1687 */ 1688 if (mKeyStore.state() != KeyStore.State.UNLOCKED) { 1689 loge(config.SSID + ": key store is locked"); 1690 break setVariables; 1691 } 1692 1693 try { 1694 /* config passed may include only fields being updated. 1695 * In order to generate the key id, fetch uninitialized 1696 * fields from the currently tracked configuration 1697 */ 1698 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1699 String keyId = config.getKeyIdForCredentials(currentConfig); 1700 1701 if (!installKeys(enterpriseConfig, keyId)) { 1702 loge(config.SSID + ": failed to install keys"); 1703 break setVariables; 1704 } 1705 } catch (IllegalStateException e) { 1706 loge(config.SSID + " invalid config for key installation"); 1707 break setVariables; 1708 } 1709 } 1710 1711 HashMap<String, String> enterpriseFields = enterpriseConfig.getFields(); 1712 for (String key : enterpriseFields.keySet()) { 1713 String value = enterpriseFields.get(key); 1714 if (!mWifiNative.setNetworkVariable( 1715 netId, 1716 key, 1717 value)) { 1718 removeKeys(enterpriseConfig); 1719 loge(config.SSID + ": failed to set " + key + 1720 ": " + value); 1721 break setVariables; 1722 } 1723 } 1724 } 1725 updateFailed = false; 1726 } //end of setVariables 1727 1728 if (updateFailed) { 1729 if (newNetwork) { 1730 mWifiNative.removeNetwork(netId); 1731 loge("Failed to set a network variable, removed network: " + netId); 1732 } 1733 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1734 } 1735 1736 /* An update of the network variables requires reading them 1737 * back from the supplicant to update mConfiguredNetworks. 1738 * This is because some of the variables (SSID, wep keys & 1739 * passphrases) reflect different values when read back than 1740 * when written. For example, wep key is stored as * irrespective 1741 * of the value sent to the supplicant 1742 */ 1743 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1744 if (currentConfig == null) { 1745 currentConfig = new WifiConfiguration(); 1746 currentConfig.setIpAssignment(IpAssignment.DHCP); 1747 currentConfig.setProxySettings(ProxySettings.NONE); 1748 currentConfig.networkId = netId; 1749 if (config != null) { 1750 //carry over the creation parameters 1751 currentConfig.selfAdded = config.selfAdded; 1752 currentConfig.didSelfAdd = config.didSelfAdd; 1753 currentConfig.lastConnectUid = config.lastConnectUid; 1754 currentConfig.lastUpdateUid = config.lastUpdateUid; 1755 currentConfig.creatorUid = config.creatorUid; 1756 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration; 1757 } 1758 if (DBG) { 1759 loge("created new config netId=" + Integer.toString(netId) 1760 + " uid=" + Integer.toString(currentConfig.creatorUid)); 1761 } 1762 } 1763 1764 if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 1765 //make sure the configuration is not deleted anymore since we just 1766 //added or modified it. 1767 currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 1768 currentConfig.selfAdded = false; 1769 currentConfig.didSelfAdd = false; 1770 } 1771 1772 if (DBG) loge("will read network variables netId=" + Integer.toString(netId)); 1773 1774 readNetworkVariables(currentConfig); 1775 1776 mConfiguredNetworks.put(netId, currentConfig); 1777 mNetworkIds.put(configKey(currentConfig), netId); 1778 1779 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config); 1780 result.setIsNewNetwork(newNetwork); 1781 result.setNetworkId(netId); 1782 return result; 1783 } 1784 1785 1786 public void linkConfiguration(WifiConfiguration config) { 1787 for (WifiConfiguration link : mConfiguredNetworks.values()) { 1788 boolean doLink = false; 1789 1790 if (link.configKey().equals(config.configKey())) { 1791 continue; 1792 } 1793 1794 if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 1795 continue; 1796 } 1797 1798 //autojoin will be allowed to dynamically jump from a linked configuration 1799 //to another, hence only link configurations that have equivalent level of security 1800 if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) { 1801 continue; 1802 } 1803 1804 if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) { 1805 //if both default GW are known, compare based on RSSI only if the GW is equal 1806 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) { 1807 1808 if (VDBG) { 1809 loge("linkConfiguration link due to same gw" + link.SSID + 1810 " and " + config.SSID + " GW " + config.defaultGwMacAddress); 1811 } 1812 doLink = true; 1813 } 1814 } else { 1815 // we do not know BOTH default gateways hence we will try to link 1816 // hoping that WifiConfigurations are indeed behind the same gateway 1817 // once both WifiConfiguration will have been tried we will know 1818 // the default gateway and revisit the choice of linking them 1819 if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 5) 1820 && (link.scanResultCache != null) && (link.scanResultCache.size() <= 5)) { 1821 String abssid = ""; 1822 String bbssid = ""; 1823 for (String key : config.scanResultCache.keySet()) { 1824 abssid = key; 1825 } 1826 for (String key : link.scanResultCache.keySet()) { 1827 bbssid = key; 1828 } 1829 if (VDBG) { 1830 loge("linkConfiguration link due to DBDC BSSID match " + link.SSID + 1831 " and " + config.SSID + " bssida " + abssid + " bssidb " + bbssid); 1832 } 1833 if (abssid.regionMatches(true, 0, bbssid, 0, 16)) { 1834 //if first 16 ascii characters of BSSID matches, we assume this is a DBDC 1835 doLink = true; 1836 } 1837 } 1838 } 1839 1840 if (doLink) { 1841 if (VDBG) { 1842 loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID); 1843 } 1844 if (link.linkedConfigurations == null) { 1845 link.linkedConfigurations = new HashMap<String, Integer>(); 1846 } 1847 if (config.linkedConfigurations == null) { 1848 config.linkedConfigurations = new HashMap<String, Integer>(); 1849 } 1850 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 1851 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 1852 } else { 1853 //todo if they are linked, break the link 1854 } 1855 } 1856 } 1857 1858 /* 1859 * We try to link a scan result with a WifiConfiguration for which SSID and ket management dont match, 1860 * for instance, we try identify the 5GHz SSID of a DBDC AP, even though we know only of the 2.4GHz 1861 * 1862 * Obviously, this function is not optimal since it is used to compare every scan 1863 * result with every Saved WifiConfiguration, with a string.equals operation. 1864 * As a speed up, might be better to implement the mConfiguredNetworks store as a 1865 * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object 1866 * so as to speed this up. Also to prevent the tiny probability of hash collision. 1867 * 1868 */ 1869 public WifiConfiguration associateWithConfiguration(ScanResult result) { 1870 String configKey = WifiConfiguration.configKey(result); 1871 if (configKey == null) { 1872 if (DBG) loge("associateWithConfiguration(): no config key " ); 1873 return null; 1874 } 1875 1876 //need to compare with quoted string 1877 String SSID = "\"" + result.SSID + "\""; 1878 1879 WifiConfiguration config = null; 1880 for (WifiConfiguration link : mConfiguredNetworks.values()) { 1881 boolean doLink = false; 1882 1883 if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) { 1884 //make sure we dont associate the scan result to a deleted config 1885 continue; 1886 } 1887 1888 if (configKey.equals(link.configKey())) { 1889 if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey ); 1890 return link; //found it exactly 1891 } 1892 1893 if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) { 1894 String bssid = ""; 1895 for (String key : link.scanResultCache.keySet()) { 1896 bssid = key; 1897 } 1898 1899 if (result.BSSID.regionMatches(true, 0, bssid, 0, 16) 1900 && SSID.regionMatches(false, 0, link.SSID, 0, 3)) { 1901 // if first 16 ascii characters of BSSID matches, and first 3 1902 // characters of SSID match, we assume this is a home setup 1903 // and thus we will try to transfer the password from the known 1904 // BSSID/SSID to the recently found BSSID/SSID 1905 1906 //if (VDBG) 1907 // loge("associateWithConfiguration OK " ); 1908 doLink = true; 1909 } 1910 } 1911 1912 if (doLink) { 1913 //try to make a non verified WifiConfiguration, but only if the original 1914 //configuration was not self already added 1915 if (VDBG) { 1916 loge("associateWithConfiguration: will create " + 1917 result.SSID + " and associate it with: " + link.SSID); 1918 } 1919 config = wifiConfigurationFromScanResult(result); 1920 if (config != null) { 1921 config.selfAdded = true; 1922 config.didSelfAdd = true; 1923 config.peerWifiConfiguration = link.configKey(); 1924 if (config.allowedKeyManagement.equals(link.allowedKeyManagement) && 1925 config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1926 //transfer the credentials from the configuration we are linking from 1927 String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk"); 1928 if (psk != null) { 1929 config.preSharedKey = psk; 1930 if (VDBG) { 1931 if (config.preSharedKey != null) 1932 loge(" transfer PSK : " + config.preSharedKey); 1933 } 1934 1935 //link configurations 1936 if (link.linkedConfigurations == null) { 1937 link.linkedConfigurations = new HashMap<String, Integer>(); 1938 } 1939 if (config.linkedConfigurations == null) { 1940 config.linkedConfigurations = new HashMap<String, Integer>(); 1941 } 1942 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 1943 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 1944 } else { 1945 config = null; 1946 } 1947 } else { 1948 config = null; 1949 } 1950 } 1951 } else { 1952 //todo if they are linked, break the link 1953 } 1954 } 1955 return config; 1956 } 1957 1958 public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) { 1959 if (config == null) 1960 return null; 1961 long now_ms = System.currentTimeMillis(); 1962 1963 HashSet<Integer> channels = new HashSet<Integer>(); 1964 1965 //get channels for this configuration, if there are at least 2 BSSIDs 1966 if (config.scanResultCache == null && config.linkedConfigurations == null) { 1967 return null; 1968 } 1969 1970 if (VDBG) { 1971 StringBuilder dbg = new StringBuilder(); 1972 dbg.append("makeChannelList age=" + Integer.toString(age) + " for " + config.configKey()); 1973 if (config.scanResultCache != null) { 1974 dbg.append(" bssids=" + config.scanResultCache.size()); 1975 } 1976 if (config.linkedConfigurations != null) { 1977 dbg.append(" linked=" + config.linkedConfigurations.size()); 1978 } 1979 loge(dbg.toString()); 1980 } 1981 1982 if (config.scanResultCache != null && config.scanResultCache.size() > 0) { 1983 for (ScanResult result : config.scanResultCache.values()) { 1984 if (VDBG) { 1985 boolean test = (now_ms - result.seen) < age; 1986 loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency) 1987 + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test); 1988 } 1989 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 1990 channels.add(result.frequency); 1991 } 1992 } 1993 } 1994 1995 //get channels for linked configurations 1996 if (config.linkedConfigurations != null) { 1997 for (String key : config.linkedConfigurations.keySet()) { 1998 WifiConfiguration linked = getWifiConfiguration(key); 1999 if (linked == null) 2000 continue; 2001 if (linked.scanResultCache == null) { 2002 continue; 2003 } 2004 for (ScanResult result : linked.scanResultCache.values()) { 2005 if (VDBG) { 2006 loge("has link: " + result.BSSID 2007 + " freq=" + Integer.toString(result.frequency) 2008 + " age=" + Long.toString(now_ms - result.seen)); 2009 } 2010 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2011 channels.add(result.frequency); 2012 } 2013 } 2014 } 2015 } 2016 return channels; 2017 } 2018 2019 public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) { 2020 WifiConfiguration found = null; 2021 if (scanResult == null) 2022 return found; 2023 2024 //first step, look for this scan Result by SSID + Key Management 2025 String key = WifiConfiguration.configKey(scanResult); 2026 int hash = key.hashCode(); 2027 2028 Integer netId = mNetworkIds.get(hash); 2029 if (netId == null) return null; 2030 WifiConfiguration config = mConfiguredNetworks.get(netId); 2031 if (config != null) { 2032 if (config.scanResultCache == null) { 2033 config.scanResultCache = new HashMap<String, ScanResult>(); 2034 } 2035 if (config.scanResultCache == null) { 2036 return null; 2037 } 2038 //add the scan result to this WifiConfiguration 2039 config.scanResultCache.put(scanResult.BSSID, scanResult); 2040 mConfiguredNetworks.put(netId, config); 2041 linkConfiguration(config); 2042 found = config; 2043 } 2044 2045 if (VDBG) { 2046 config = mConfiguredNetworks.get(netId); 2047 if (config != null) { 2048 if (config.scanResultCache != null) { 2049 loge(" tested " + scanResult.SSID + " " + 2050 scanResult.BSSID + " key : " + key + " num: " + 2051 Integer.toString(config.scanResultCache.size())); 2052 } else { 2053 loge(" tested " + scanResult.SSID + " " + 2054 scanResult.BSSID + " key : " + key); 2055 } 2056 } 2057 } 2058 return found; 2059 2060 } 2061 2062 /* Compare current and new configuration and write to file on change */ 2063 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 2064 WifiConfiguration currentConfig, 2065 WifiConfiguration newConfig) { 2066 boolean ipChanged = false; 2067 boolean proxyChanged = false; 2068 LinkProperties linkProperties = null; 2069 2070 if (VDBG) { 2071 loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " + 2072 newConfig.SSID + " path: " + ipConfigFile); 2073 } 2074 2075 2076 switch (newConfig.getIpAssignment()) { 2077 case STATIC: 2078 Collection<LinkAddress> currentLinkAddresses = currentConfig.getLinkProperties() 2079 .getLinkAddresses(); 2080 Collection<LinkAddress> newLinkAddresses = newConfig.getLinkProperties() 2081 .getLinkAddresses(); 2082 Collection<InetAddress> currentDnses = 2083 currentConfig.getLinkProperties().getDnsServers(); 2084 Collection<InetAddress> newDnses = newConfig.getLinkProperties().getDnsServers(); 2085 Collection<RouteInfo> currentRoutes = currentConfig.getLinkProperties().getRoutes(); 2086 Collection<RouteInfo> newRoutes = newConfig.getLinkProperties().getRoutes(); 2087 2088 boolean linkAddressesDiffer = 2089 (currentLinkAddresses.size() != newLinkAddresses.size()) || 2090 !currentLinkAddresses.containsAll(newLinkAddresses); 2091 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || 2092 !currentDnses.containsAll(newDnses); 2093 boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) || 2094 !currentRoutes.containsAll(newRoutes); 2095 2096 if ((currentConfig.getIpAssignment() != newConfig.getIpAssignment()) || 2097 linkAddressesDiffer || 2098 dnsesDiffer || 2099 routesDiffer) { 2100 ipChanged = true; 2101 } 2102 break; 2103 case DHCP: 2104 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 2105 ipChanged = true; 2106 } 2107 break; 2108 case UNASSIGNED: 2109 /* Ignore */ 2110 break; 2111 default: 2112 loge("Ignore invalid ip assignment during write"); 2113 break; 2114 } 2115 2116 switch (newConfig.getProxySettings()) { 2117 case STATIC: 2118 case PAC: 2119 ProxyInfo newHttpProxy = newConfig.getLinkProperties().getHttpProxy(); 2120 ProxyInfo currentHttpProxy = currentConfig.getLinkProperties().getHttpProxy(); 2121 2122 if (newHttpProxy != null) { 2123 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 2124 } else { 2125 proxyChanged = (currentHttpProxy != null); 2126 } 2127 break; 2128 case NONE: 2129 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) { 2130 proxyChanged = true; 2131 } 2132 break; 2133 case UNASSIGNED: 2134 /* Ignore */ 2135 break; 2136 default: 2137 loge("Ignore invalid proxy configuration during write"); 2138 break; 2139 } 2140 2141 if (!ipChanged) { 2142 linkProperties = copyIpSettingsFromConfig(currentConfig); 2143 } else { 2144 currentConfig.setIpAssignment(newConfig.getIpAssignment()); 2145 linkProperties = copyIpSettingsFromConfig(newConfig); 2146 log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " + 2147 linkProperties.toString()); 2148 } 2149 2150 2151 if (!proxyChanged) { 2152 linkProperties.setHttpProxy(currentConfig.getLinkProperties().getHttpProxy()); 2153 } else { 2154 currentConfig.setProxySettings(newConfig.getProxySettings()); 2155 linkProperties.setHttpProxy(newConfig.getLinkProperties().getHttpProxy()); 2156 log("proxy changed SSID = " + currentConfig.SSID); 2157 if (linkProperties.getHttpProxy() != null) { 2158 log(" proxyProperties: " + linkProperties.getHttpProxy().toString()); 2159 } 2160 } 2161 2162 if (ipChanged || proxyChanged) { 2163 currentConfig.setLinkProperties(linkProperties); 2164 writeIpAndProxyConfigurations(); 2165 sendConfiguredNetworksChangedBroadcast(currentConfig, 2166 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 2167 } 2168 return new NetworkUpdateResult(ipChanged, proxyChanged); 2169 } 2170 2171 private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) { 2172 LinkProperties linkProperties = new LinkProperties(); 2173 linkProperties.setInterfaceName(config.getLinkProperties().getInterfaceName()); 2174 for (LinkAddress linkAddr : config.getLinkProperties().getLinkAddresses()) { 2175 linkProperties.addLinkAddress(linkAddr); 2176 } 2177 for (RouteInfo route : config.getLinkProperties().getRoutes()) { 2178 linkProperties.addRoute(route); 2179 } 2180 for (InetAddress dns : config.getLinkProperties().getDnsServers()) { 2181 linkProperties.addDnsServer(dns); 2182 } 2183 return linkProperties; 2184 } 2185 2186 /** 2187 * Read the variables from the supplicant daemon that are needed to 2188 * fill in the WifiConfiguration object. 2189 * 2190 * @param config the {@link WifiConfiguration} object to be filled in. 2191 */ 2192 private void readNetworkVariables(WifiConfiguration config) { 2193 2194 int netId = config.networkId; 2195 if (netId < 0) 2196 return; 2197 2198 /* 2199 * TODO: maybe should have a native method that takes an array of 2200 * variable names and returns an array of values. But we'd still 2201 * be doing a round trip to the supplicant daemon for each variable. 2202 */ 2203 String value; 2204 2205 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 2206 if (!TextUtils.isEmpty(value)) { 2207 if (value.charAt(0) != '"') { 2208 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 2209 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 2210 //supplicant string 2211 } else { 2212 config.SSID = value; 2213 } 2214 } else { 2215 config.SSID = null; 2216 } 2217 2218 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 2219 if (!TextUtils.isEmpty(value)) { 2220 config.BSSID = value; 2221 } else { 2222 config.BSSID = null; 2223 } 2224 2225 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 2226 config.priority = -1; 2227 if (!TextUtils.isEmpty(value)) { 2228 try { 2229 config.priority = Integer.parseInt(value); 2230 } catch (NumberFormatException ignore) { 2231 } 2232 } 2233 2234 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 2235 config.hiddenSSID = false; 2236 if (!TextUtils.isEmpty(value)) { 2237 try { 2238 config.hiddenSSID = Integer.parseInt(value) != 0; 2239 } catch (NumberFormatException ignore) { 2240 } 2241 } 2242 2243 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 2244 config.wepTxKeyIndex = -1; 2245 if (!TextUtils.isEmpty(value)) { 2246 try { 2247 config.wepTxKeyIndex = Integer.parseInt(value); 2248 } catch (NumberFormatException ignore) { 2249 } 2250 } 2251 2252 for (int i = 0; i < 4; i++) { 2253 value = mWifiNative.getNetworkVariable(netId, 2254 WifiConfiguration.wepKeyVarNames[i]); 2255 if (!TextUtils.isEmpty(value)) { 2256 config.wepKeys[i] = value; 2257 } else { 2258 config.wepKeys[i] = null; 2259 } 2260 } 2261 2262 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 2263 if (!TextUtils.isEmpty(value)) { 2264 config.preSharedKey = value; 2265 } else { 2266 config.preSharedKey = null; 2267 } 2268 2269 value = mWifiNative.getNetworkVariable(config.networkId, 2270 WifiConfiguration.Protocol.varName); 2271 if (!TextUtils.isEmpty(value)) { 2272 String vals[] = value.split(" "); 2273 for (String val : vals) { 2274 int index = 2275 lookupString(val, WifiConfiguration.Protocol.strings); 2276 if (0 <= index) { 2277 config.allowedProtocols.set(index); 2278 } 2279 } 2280 } 2281 2282 value = mWifiNative.getNetworkVariable(config.networkId, 2283 WifiConfiguration.KeyMgmt.varName); 2284 if (!TextUtils.isEmpty(value)) { 2285 String vals[] = value.split(" "); 2286 for (String val : vals) { 2287 int index = 2288 lookupString(val, WifiConfiguration.KeyMgmt.strings); 2289 if (0 <= index) { 2290 config.allowedKeyManagement.set(index); 2291 } 2292 } 2293 } 2294 2295 value = mWifiNative.getNetworkVariable(config.networkId, 2296 WifiConfiguration.AuthAlgorithm.varName); 2297 if (!TextUtils.isEmpty(value)) { 2298 String vals[] = value.split(" "); 2299 for (String val : vals) { 2300 int index = 2301 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 2302 if (0 <= index) { 2303 config.allowedAuthAlgorithms.set(index); 2304 } 2305 } 2306 } 2307 2308 value = mWifiNative.getNetworkVariable(config.networkId, 2309 WifiConfiguration.PairwiseCipher.varName); 2310 if (!TextUtils.isEmpty(value)) { 2311 String vals[] = value.split(" "); 2312 for (String val : vals) { 2313 int index = 2314 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 2315 if (0 <= index) { 2316 config.allowedPairwiseCiphers.set(index); 2317 } 2318 } 2319 } 2320 2321 value = mWifiNative.getNetworkVariable(config.networkId, 2322 WifiConfiguration.GroupCipher.varName); 2323 if (!TextUtils.isEmpty(value)) { 2324 String vals[] = value.split(" "); 2325 for (String val : vals) { 2326 int index = 2327 lookupString(val, WifiConfiguration.GroupCipher.strings); 2328 if (0 <= index) { 2329 config.allowedGroupCiphers.set(index); 2330 } 2331 } 2332 } 2333 2334 if (config.enterpriseConfig == null) { 2335 config.enterpriseConfig = new WifiEnterpriseConfig(); 2336 } 2337 HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields(); 2338 for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) { 2339 value = mWifiNative.getNetworkVariable(netId, key); 2340 if (!TextUtils.isEmpty(value)) { 2341 enterpriseFields.put(key, removeDoubleQuotes(value)); 2342 } else { 2343 enterpriseFields.put(key, EMPTY_VALUE); 2344 } 2345 } 2346 2347 if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) { 2348 saveConfig(); 2349 } 2350 2351 migrateCerts(config.enterpriseConfig); 2352 // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore); 2353 } 2354 2355 private static String removeDoubleQuotes(String string) { 2356 int length = string.length(); 2357 if ((length > 1) && (string.charAt(0) == '"') 2358 && (string.charAt(length - 1) == '"')) { 2359 return string.substring(1, length - 1); 2360 } 2361 return string; 2362 } 2363 2364 private static String makeString(BitSet set, String[] strings) { 2365 StringBuffer buf = new StringBuffer(); 2366 int nextSetBit = -1; 2367 2368 /* Make sure all set bits are in [0, strings.length) to avoid 2369 * going out of bounds on strings. (Shouldn't happen, but...) */ 2370 set = set.get(0, strings.length); 2371 2372 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 2373 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 2374 } 2375 2376 // remove trailing space 2377 if (set.cardinality() > 0) { 2378 buf.setLength(buf.length() - 1); 2379 } 2380 2381 return buf.toString(); 2382 } 2383 2384 private int lookupString(String string, String[] strings) { 2385 int size = strings.length; 2386 2387 string = string.replace('-', '_'); 2388 2389 for (int i = 0; i < size; i++) 2390 if (string.equals(strings[i])) 2391 return i; 2392 2393 // if we ever get here, we should probably add the 2394 // value to WifiConfiguration to reflect that it's 2395 // supported by the WPA supplicant 2396 loge("Failed to look-up a string: " + string); 2397 2398 return -1; 2399 } 2400 2401 /* return the allowed key management based on a scan result */ 2402 2403 public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 2404 WifiConfiguration config = new WifiConfiguration(); 2405 2406 config.SSID = "\"" + result.SSID + "\""; 2407 2408 if (VDBG) { 2409 loge("WifiConfiguration from scan results " + 2410 config.SSID + " cap " + result.capabilities); 2411 } 2412 if (result.capabilities.contains("WEP")) { 2413 config.allowedKeyManagement.set(KeyMgmt.NONE); 2414 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //? 2415 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 2416 } 2417 2418 if (result.capabilities.contains("PSK")) { 2419 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 2420 } 2421 2422 if (result.capabilities.contains("EAP")) { 2423 //this is probably wrong, as we don't have a way to enter the enterprise config 2424 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 2425 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 2426 } 2427 2428 config.scanResultCache = new HashMap<String, ScanResult>(); 2429 if (config.scanResultCache == null) 2430 return null; 2431 config.scanResultCache.put(result.BSSID, result); 2432 2433 return config; 2434 } 2435 2436 2437 /* Returns a unique for a given configuration */ 2438 private static int configKey(WifiConfiguration config) { 2439 String key = config.configKey(); 2440 return key.hashCode(); 2441 } 2442 2443 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2444 pw.println("WifiConfigStore"); 2445 pw.println("mLastPriority " + mLastPriority); 2446 pw.println("Configured networks"); 2447 for (WifiConfiguration conf : getConfiguredNetworks()) { 2448 pw.println(conf); 2449 } 2450 pw.println(); 2451 2452 if (mLocalLog != null) { 2453 pw.println("WifiConfigStore - Log Begin ----"); 2454 mLocalLog.dump(fd, pw, args); 2455 pw.println("WifiConfigStore - Log End ----"); 2456 } 2457 } 2458 2459 public String getConfigFile() { 2460 return ipConfigFile; 2461 } 2462 2463 protected void loge(String s) { 2464 loge(s, false); 2465 } 2466 2467 protected void loge(String s, boolean stack) { 2468 if (stack) { 2469 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2470 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 2471 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 2472 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 2473 } else { 2474 Log.e(TAG, s); 2475 } 2476 } 2477 2478 protected void log(String s) { 2479 Log.d(TAG, s); 2480 } 2481 2482 private void localLog(String s) { 2483 if (mLocalLog != null) { 2484 mLocalLog.log(s); 2485 } 2486 } 2487 2488 private void localLog(String s, int netId) { 2489 if (mLocalLog == null) { 2490 return; 2491 } 2492 2493 WifiConfiguration config; 2494 synchronized(mConfiguredNetworks) { 2495 config = mConfiguredNetworks.get(netId); 2496 } 2497 2498 if (config != null) { 2499 mLocalLog.log(s + " " + config.getPrintableSsid()); 2500 } else { 2501 mLocalLog.log(s + " " + netId); 2502 } 2503 } 2504 2505 // Certificate and private key management for EnterpriseConfig 2506 static boolean needsKeyStore(WifiEnterpriseConfig config) { 2507 // Has no keys to be installed 2508 if (config.getClientCertificate() == null && config.getCaCertificate() == null) 2509 return false; 2510 return true; 2511 } 2512 2513 static boolean isHardwareBackedKey(PrivateKey key) { 2514 return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); 2515 } 2516 2517 static boolean hasHardwareBackedKey(Certificate certificate) { 2518 return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); 2519 } 2520 2521 static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 2522 String client = config.getClientCertificateAlias(); 2523 if (!TextUtils.isEmpty(client)) { 2524 // a valid client certificate is configured 2525 2526 // BUGBUG: keyStore.get() never returns certBytes; because it is not 2527 // taking WIFI_UID as a parameter. It always looks for certificate 2528 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 2529 // all certificates need software keystore until we get the get() API 2530 // fixed. 2531 2532 return true; 2533 } 2534 2535 /* 2536 try { 2537 2538 if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials 2539 .USER_CERTIFICATE + client); 2540 2541 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 2542 if (factory == null) { 2543 Slog.e(TAG, "Error getting certificate factory"); 2544 return; 2545 } 2546 2547 byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); 2548 if (certBytes != null) { 2549 Certificate cert = (X509Certificate) factory.generateCertificate( 2550 new ByteArrayInputStream(certBytes)); 2551 2552 if (cert != null) { 2553 mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); 2554 2555 if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials 2556 .USER_CERTIFICATE + client); 2557 if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : 2558 "does not need" ) + " software key store"); 2559 } else { 2560 Slog.d(TAG, "could not generate certificate"); 2561 } 2562 } else { 2563 Slog.e(TAG, "Could not load client certificate " + Credentials 2564 .USER_CERTIFICATE + client); 2565 mNeedsSoftwareKeystore = true; 2566 } 2567 2568 } catch(CertificateException e) { 2569 Slog.e(TAG, "Could not read certificates"); 2570 mCaCert = null; 2571 mClientCertificate = null; 2572 } 2573 */ 2574 2575 return false; 2576 } 2577 2578 /** called when CS ask WiFistateMachine to disconnect the current network 2579 * because the score is bad. 2580 */ 2581 void handleBadNetworkDisconnectReport(int netId, WifiInfo info) { 2582 /* TODO verify the bad network is current */ 2583 WifiConfiguration config = mConfiguredNetworks.get(netId); 2584 if (config != null) { 2585 if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24 2586 && info.is24GHz()) || (info.getRssi() < 2587 WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) { 2588 //we got disconnected and RSSI was bad, so disable light 2589 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED 2590 + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP); 2591 loge("handleBadNetworkDisconnectReport (+4) " 2592 + Integer.toString(netId) + " " + info); 2593 } else { 2594 //we got disabled but RSSI is good, so disable hard 2595 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED 2596 + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP); 2597 loge("handleBadNetworkDisconnectReport (+8) " 2598 + Integer.toString(netId) + " " + info); 2599 } 2600 } 2601 } 2602 2603 void handleSSIDStateChange(int netId, boolean enabled, String message) { 2604 WifiConfiguration config = mConfiguredNetworks.get(netId); 2605 if (config != null) { 2606 if (enabled) { 2607 loge("SSID re-enabled for " + config.configKey() + 2608 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 2609 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 2610 //TODO: really I don't know if re-enabling is right but we should err on the side of trying to connect 2611 //TODO: even if the attempt will fail 2612 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 2613 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 2614 } 2615 } else { 2616 loge("SSID temp disabled for " + config.configKey() + 2617 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 2618 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 2619 if (message != null) { 2620 loge(" wpa_supplicant message=" + message); 2621 } 2622 if (config.selfAdded && config.lastConnected == 0) { 2623 //this is a network we self added, and we never succeeded, 2624 //the user did not create this network and never entered its credentials, so we want 2625 //to be very aggressive in disabling it completely. 2626 disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE); 2627 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 2628 config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE; 2629 } else { 2630 if (message != null) { 2631 if (message.contains("WRONG_KEY") 2632 || message.contains("AUTH_FAILED")) { 2633 //This configuration has received an auth failure, so disable it 2634 //temporarily because we don't want auto-join to try it out. 2635 //this network may be re-enabled by the "usual" 2636 // enableAllNetwork function 2637 //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE: 2638 //TODO: if we could count on the wrong_ley or auth_failure message to be correct 2639 //TODO: then we could just mark the configuration as DISABLED_ON_AUTH_FAILURE 2640 //TODO: and the configuration will stay there until user enter new credentials 2641 //TODO: It is not the case however, so instead of disabling, let's start 2642 //TODO: blacklisting hard 2643 if (config.autoJoinStatus <= 2644 WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 2645 //4 auth failure will reach 128 and disable permanently 2646 //autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128 2647 config.setAutoJoinStatus(4 + config.autoJoinStatus * 4); 2648 if (config.autoJoinStatus > WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) 2649 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 2650 } 2651 if (DBG) { 2652 loge("blacklisted " + config.configKey() + " to " 2653 + Integer.toString(config.autoJoinStatus)); 2654 } 2655 } 2656 message.replace("\n", ""); 2657 message.replace("\r", ""); 2658 config.lastFailure = message; 2659 } 2660 } 2661 } 2662 } 2663 } 2664 2665 boolean installKeys(WifiEnterpriseConfig config, String name) { 2666 boolean ret = true; 2667 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 2668 String userCertName = Credentials.USER_CERTIFICATE + name; 2669 String caCertName = Credentials.CA_CERTIFICATE + name; 2670 if (config.getClientCertificate() != null) { 2671 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 2672 if (isHardwareBackedKey(config.getClientPrivateKey())) { 2673 // Hardware backed key store is secure enough to store keys un-encrypted, this 2674 // removes the need for user to punch a PIN to get access to these keys 2675 if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store"); 2676 ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID, 2677 KeyStore.FLAG_NONE); 2678 } else { 2679 // Software backed key store is NOT secure enough to store keys un-encrypted. 2680 // Save keys encrypted so they are protected with user's PIN. User will 2681 // have to unlock phone before being able to use these keys and connect to 2682 // networks. 2683 if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store"); 2684 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 2685 KeyStore.FLAG_ENCRYPTED); 2686 } 2687 if (ret == false) { 2688 return ret; 2689 } 2690 2691 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 2692 if (ret == false) { 2693 // Remove private key installed 2694 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 2695 return ret; 2696 } 2697 } 2698 2699 if (config.getCaCertificate() != null) { 2700 ret = putCertInKeyStore(caCertName, config.getCaCertificate()); 2701 if (ret == false) { 2702 if (config.getClientCertificate() != null) { 2703 // Remove client key+cert 2704 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 2705 mKeyStore.delete(userCertName, Process.WIFI_UID); 2706 } 2707 return ret; 2708 } 2709 } 2710 2711 // Set alias names 2712 if (config.getClientCertificate() != null) { 2713 config.setClientCertificateAlias(name); 2714 config.resetClientKeyEntry(); 2715 } 2716 2717 if (config.getCaCertificate() != null) { 2718 config.setCaCertificateAlias(name); 2719 config.resetCaCertificate(); 2720 } 2721 2722 return ret; 2723 } 2724 2725 private boolean putCertInKeyStore(String name, Certificate cert) { 2726 try { 2727 byte[] certData = Credentials.convertToPem(cert); 2728 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 2729 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 2730 2731 } catch (IOException e1) { 2732 return false; 2733 } catch (CertificateException e2) { 2734 return false; 2735 } 2736 } 2737 2738 void removeKeys(WifiEnterpriseConfig config) { 2739 String client = config.getClientCertificateAlias(); 2740 // a valid client certificate is configured 2741 if (!TextUtils.isEmpty(client)) { 2742 if (DBG) Log.d(TAG, "removing client private key and user cert"); 2743 mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 2744 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 2745 } 2746 2747 String ca = config.getCaCertificateAlias(); 2748 // a valid ca certificate is configured 2749 if (!TextUtils.isEmpty(ca)) { 2750 if (DBG) Log.d(TAG, "removing CA cert"); 2751 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 2752 } 2753 } 2754 2755 2756 /** Migrates the old style TLS config to the new config style. This should only be used 2757 * when restoring an old wpa_supplicant.conf or upgrading from a previous 2758 * platform version. 2759 * @return true if the config was updated 2760 * @hide 2761 */ 2762 boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) { 2763 String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); 2764 /* 2765 * If the old configuration value is not present, then there is nothing 2766 * to do. 2767 */ 2768 if (TextUtils.isEmpty(oldPrivateKey)) { 2769 return false; 2770 } else { 2771 // Also ignore it if it's empty quotes. 2772 oldPrivateKey = removeDoubleQuotes(oldPrivateKey); 2773 if (TextUtils.isEmpty(oldPrivateKey)) { 2774 return false; 2775 } 2776 } 2777 2778 config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE); 2779 config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, 2780 WifiEnterpriseConfig.ENGINE_ID_KEYSTORE); 2781 2782 /* 2783 * The old key started with the keystore:// URI prefix, but we don't 2784 * need that anymore. Trim it off if it exists. 2785 */ 2786 final String keyName; 2787 if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) { 2788 keyName = new String( 2789 oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length())); 2790 } else { 2791 keyName = oldPrivateKey; 2792 } 2793 config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName); 2794 2795 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY, 2796 config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, "")); 2797 2798 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY, 2799 config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, "")); 2800 2801 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, 2802 config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, "")); 2803 2804 // Remove old private_key string so we don't run this again. 2805 mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); 2806 2807 return true; 2808 } 2809 2810 /** Migrate certs from global pool to wifi UID if not already done */ 2811 void migrateCerts(WifiEnterpriseConfig config) { 2812 String client = config.getClientCertificateAlias(); 2813 // a valid client certificate is configured 2814 if (!TextUtils.isEmpty(client)) { 2815 if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { 2816 mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, 2817 Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 2818 mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, 2819 Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 2820 } 2821 } 2822 2823 String ca = config.getCaCertificateAlias(); 2824 // a valid ca certificate is configured 2825 if (!TextUtils.isEmpty(ca)) { 2826 if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { 2827 mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, 2828 Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 2829 } 2830 } 2831 } 2832 2833} 2834