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 17 package android.net.wifi; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.net.LinkAddress; 22 import android.net.LinkProperties; 23 import android.net.NetworkUtils; 24 import android.net.NetworkInfo.DetailedState; 25 import android.net.ProxyProperties; 26 import android.net.RouteInfo; 27 import android.net.wifi.WifiConfiguration.IpAssignment; 28 import android.net.wifi.WifiConfiguration.KeyMgmt; 29 import android.net.wifi.WifiConfiguration.ProxySettings; 30 import android.net.wifi.WifiConfiguration.Status; 31 import android.net.wifi.NetworkUpdateResult; 32 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 33 import android.os.Environment; 34 import android.os.FileObserver; 35 import android.os.Message; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.UserHandle; 39 import android.security.KeyStore; 40 import android.text.TextUtils; 41 import android.util.LocalLog; 42 import android.util.Log; 43 44 import java.io.BufferedInputStream; 45 import java.io.BufferedOutputStream; 46 import java.io.BufferedReader; 47 import java.io.DataInputStream; 48 import java.io.DataOutputStream; 49 import java.io.EOFException; 50 import java.io.File; 51 import java.io.FileDescriptor; 52 import java.io.FileInputStream; 53 import java.io.FileNotFoundException; 54 import java.io.FileOutputStream; 55 import java.io.FileReader; 56 import java.io.IOException; 57 import java.io.PrintWriter; 58 import java.net.InetAddress; 59 import java.net.UnknownHostException; 60 import java.security.PublicKey; 61 import java.util.ArrayList; 62 import java.util.BitSet; 63 import java.util.Collection; 64 import java.util.HashMap; 65 import java.util.Iterator; 66 import java.util.List; 67 import java.util.concurrent.atomic.AtomicInteger; 68 69 /** 70 * This class provides the API to manage configured 71 * wifi networks. The API is not thread safe is being 72 * used only from WifiStateMachine. 73 * 74 * It deals with the following 75 * - Add/update/remove a WifiConfiguration 76 * The configuration contains two types of information. 77 * = IP and proxy configuration that is handled by WifiConfigStore and 78 * is saved to disk on any change. 79 * 80 * The format of configuration file is as follows: 81 * <version> 82 * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS> 83 * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS> 84 * .. 85 * 86 * (key, value) pairs for a given network are grouped together and can 87 * be in any order. A EOS at the end of a set of (key, value) pairs 88 * indicates that the next set of (key, value) pairs are for a new 89 * network. A network is identified by a unique ID_KEY. If there is no 90 * ID_KEY in the (key, value) pairs, the data is discarded. 91 * 92 * An invalid version on read would result in discarding the contents of 93 * the file. On the next write, the latest version is written to file. 94 * 95 * Any failures during read or write to the configuration file are ignored 96 * without reporting to the user since the likelihood of these errors are 97 * low and the impact on connectivity is low. 98 * 99 * = SSID & security details that is pushed to the supplicant. 100 * supplicant saves these details to the disk on calling 101 * saveConfigCommand(). 102 * 103 * We have two kinds of APIs exposed: 104 * > public API calls that provide fine grained control 105 * - enableNetwork, disableNetwork, addOrUpdateNetwork(), 106 * removeNetwork(). For these calls, the config is not persisted 107 * to the disk. (TODO: deprecate these calls in WifiManager) 108 * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork(). 109 * These calls persist the supplicant config to disk. 110 * 111 * - Maintain a list of configured networks for quick access 112 * 113 */ 114 class WifiConfigStore { 115 116 private Context mContext; 117 private static final String TAG = "WifiConfigStore"; 118 private static final boolean DBG = true; 119 private static final boolean VDBG = false; 120 121 private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; 122 123 /* configured networks with network id as the key */ 124 private HashMap<Integer, WifiConfiguration> mConfiguredNetworks = 125 new HashMap<Integer, WifiConfiguration>(); 126 127 /* A network id is a unique identifier for a network configured in the 128 * supplicant. Network ids are generated when the supplicant reads 129 * the configuration file at start and can thus change for networks. 130 * We store the IP configuration for networks along with a unique id 131 * that is generated from SSID and security type of the network. A mapping 132 * from the generated unique id to network id of the network is needed to 133 * map supplicant config to IP configuration. */ 134 private HashMap<Integer, Integer> mNetworkIds = 135 new HashMap<Integer, Integer>(); 136 137 /* Tracks the highest priority of configured networks */ 138 private int mLastPriority = -1; 139 140 private static final String ipConfigFile = Environment.getDataDirectory() + 141 "/misc/wifi/ipconfig.txt"; 142 143 private static final int IPCONFIG_FILE_VERSION = 2; 144 145 /* IP and proxy configuration keys */ 146 private static final String ID_KEY = "id"; 147 private static final String IP_ASSIGNMENT_KEY = "ipAssignment"; 148 private static final String LINK_ADDRESS_KEY = "linkAddress"; 149 private static final String GATEWAY_KEY = "gateway"; 150 private static final String DNS_KEY = "dns"; 151 private static final String PROXY_SETTINGS_KEY = "proxySettings"; 152 private static final String PROXY_HOST_KEY = "proxyHost"; 153 private static final String PROXY_PORT_KEY = "proxyPort"; 154 private static final String PROXY_PAC_FILE = "proxyPac"; 155 private static final String EXCLUSION_LIST_KEY = "exclusionList"; 156 private static final String EOS = "eos"; 157 158 private final LocalLog mLocalLog; 159 private final WpaConfigFileObserver mFileObserver; 160 161 private WifiNative mWifiNative; 162 private final KeyStore mKeyStore = KeyStore.getInstance(); 163 164 WifiConfigStore(Context c, WifiNative wn) { 165 mContext = c; 166 mWifiNative = wn; 167 168 if (VDBG) { 169 mLocalLog = mWifiNative.getLocalLog(); 170 mFileObserver = new WpaConfigFileObserver(); 171 mFileObserver.startWatching(); 172 } else { 173 mLocalLog = null; 174 mFileObserver = null; 175 } 176 } 177 178 class WpaConfigFileObserver extends FileObserver { 179 180 public WpaConfigFileObserver() { 181 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 182 } 183 184 @Override 185 public void onEvent(int event, String path) { 186 if (event == CLOSE_WRITE) { 187 File file = new File(SUPPLICANT_CONFIG_FILE); 188 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 189 } 190 } 191 } 192 193 194 /** 195 * Fetch the list of configured networks 196 * and enable all stored networks in supplicant. 197 */ 198 void loadAndEnableAllNetworks() { 199 if (DBG) log("Loading config and enabling all networks"); 200 loadConfiguredNetworks(); 201 enableAllNetworks(); 202 } 203 204 /** 205 * Fetch the list of currently configured networks 206 * @return List of networks 207 */ 208 List<WifiConfiguration> getConfiguredNetworks() { 209 List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 210 for(WifiConfiguration config : mConfiguredNetworks.values()) { 211 networks.add(new WifiConfiguration(config)); 212 } 213 return networks; 214 } 215 216 /** 217 * enable all networks and save config. This will be a no-op if the list 218 * of configured networks indicates all networks as being enabled 219 */ 220 void enableAllNetworks() { 221 boolean networkEnabledStateChanged = false; 222 for(WifiConfiguration config : mConfiguredNetworks.values()) { 223 if(config != null && config.status == Status.DISABLED) { 224 if(mWifiNative.enableNetwork(config.networkId, false)) { 225 networkEnabledStateChanged = true; 226 config.status = Status.ENABLED; 227 } else { 228 loge("Enable network failed on " + config.networkId); 229 } 230 } 231 } 232 233 if (networkEnabledStateChanged) { 234 mWifiNative.saveConfig(); 235 sendConfiguredNetworksChangedBroadcast(); 236 } 237 } 238 239 240 /** 241 * Selects the specified network for connection. This involves 242 * updating the priority of all the networks and enabling the given 243 * network while disabling others. 244 * 245 * Selecting a network will leave the other networks disabled and 246 * a call to enableAllNetworks() needs to be issued upon a connection 247 * or a failure event from supplicant 248 * 249 * @param netId network to select for connection 250 * @return false if the network id is invalid 251 */ 252 boolean selectNetwork(int netId) { 253 if (VDBG) localLog("selectNetwork", netId); 254 if (netId == INVALID_NETWORK_ID) return false; 255 256 // Reset the priority of each network at start or if it goes too high. 257 if (mLastPriority == -1 || mLastPriority > 1000000) { 258 for(WifiConfiguration config : mConfiguredNetworks.values()) { 259 if (config.networkId != INVALID_NETWORK_ID) { 260 config.priority = 0; 261 addOrUpdateNetworkNative(config); 262 } 263 } 264 mLastPriority = 0; 265 } 266 267 // Set to the highest priority and save the configuration. 268 WifiConfiguration config = new WifiConfiguration(); 269 config.networkId = netId; 270 config.priority = ++mLastPriority; 271 272 addOrUpdateNetworkNative(config); 273 mWifiNative.saveConfig(); 274 275 /* Enable the given network while disabling all other networks */ 276 enableNetworkWithoutBroadcast(netId, true); 277 278 /* Avoid saving the config & sending a broadcast to prevent settings 279 * from displaying a disabled list of networks */ 280 return true; 281 } 282 283 /** 284 * Add/update the specified configuration and save config 285 * 286 * @param config WifiConfiguration to be saved 287 * @return network update result 288 */ 289 NetworkUpdateResult saveNetwork(WifiConfiguration config) { 290 if (VDBG) localLog("saveNetwork", config.networkId); 291 // A new network cannot have null SSID 292 if (config == null || (config.networkId == INVALID_NETWORK_ID && 293 config.SSID == null)) { 294 return new NetworkUpdateResult(INVALID_NETWORK_ID); 295 } 296 297 boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); 298 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 299 int netId = result.getNetworkId(); 300 /* enable a new network */ 301 if (newNetwork && netId != INVALID_NETWORK_ID) { 302 mWifiNative.enableNetwork(netId, false); 303 mConfiguredNetworks.get(netId).status = Status.ENABLED; 304 } 305 mWifiNative.saveConfig(); 306 sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ? 307 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 308 return result; 309 } 310 311 void updateStatus(int netId, DetailedState state) { 312 if (netId != INVALID_NETWORK_ID) { 313 WifiConfiguration config = mConfiguredNetworks.get(netId); 314 if (config == null) return; 315 switch (state) { 316 case CONNECTED: 317 config.status = Status.CURRENT; 318 break; 319 case DISCONNECTED: 320 //If network is already disabled, keep the status 321 if (config.status == Status.CURRENT) { 322 config.status = Status.ENABLED; 323 } 324 break; 325 default: 326 //do nothing, retain the existing state 327 break; 328 } 329 } 330 } 331 332 /** 333 * Forget the specified network and save config 334 * 335 * @param netId network to forget 336 * @return {@code true} if it succeeds, {@code false} otherwise 337 */ 338 boolean forgetNetwork(int netId) { 339 if (VDBG) localLog("forgetNetwork", netId); 340 if (mWifiNative.removeNetwork(netId)) { 341 mWifiNative.saveConfig(); 342 removeConfigAndSendBroadcastIfNeeded(netId); 343 return true; 344 } else { 345 loge("Failed to remove network " + netId); 346 return false; 347 } 348 } 349 350 /** 351 * Add/update a network. Note that there is no saveConfig operation. 352 * This function is retained for compatibility with the public 353 * API. The more powerful saveNetwork() is used by the 354 * state machine 355 * 356 * @param config wifi configuration to add/update 357 * @return network Id 358 */ 359 int addOrUpdateNetwork(WifiConfiguration config) { 360 if (VDBG) localLog("addOrUpdateNetwork", config.networkId); 361 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 362 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 363 sendConfiguredNetworksChangedBroadcast(mConfiguredNetworks.get(result.getNetworkId()), 364 result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED : 365 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 366 } 367 return result.getNetworkId(); 368 } 369 370 /** 371 * Remove a network. Note that there is no saveConfig operation. 372 * This function is retained for compatibility with the public 373 * API. The more powerful forgetNetwork() is used by the 374 * state machine for network removal 375 * 376 * @param netId network to be removed 377 * @return {@code true} if it succeeds, {@code false} otherwise 378 */ 379 boolean removeNetwork(int netId) { 380 if (VDBG) localLog("removeNetwork", netId); 381 boolean ret = mWifiNative.removeNetwork(netId); 382 if (ret) { 383 removeConfigAndSendBroadcastIfNeeded(netId); 384 } 385 return ret; 386 } 387 388 private void removeConfigAndSendBroadcastIfNeeded(int netId) { 389 WifiConfiguration config = mConfiguredNetworks.get(netId); 390 if (config != null) { 391 // Remove any associated keys 392 if (config.enterpriseConfig != null) { 393 config.enterpriseConfig.removeKeys(mKeyStore); 394 } 395 mConfiguredNetworks.remove(netId); 396 mNetworkIds.remove(configKey(config)); 397 398 writeIpAndProxyConfigurations(); 399 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 400 } 401 } 402 403 /** 404 * Enable a network. Note that there is no saveConfig operation. 405 * This function is retained for compatibility with the public 406 * API. The more powerful selectNetwork()/saveNetwork() is used by the 407 * state machine for connecting to a network 408 * 409 * @param netId network to be enabled 410 * @return {@code true} if it succeeds, {@code false} otherwise 411 */ 412 boolean enableNetwork(int netId, boolean disableOthers) { 413 boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers); 414 if (disableOthers) { 415 if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId); 416 sendConfiguredNetworksChangedBroadcast(); 417 } else { 418 if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId); 419 WifiConfiguration enabledNetwork = null; 420 synchronized(mConfiguredNetworks) { 421 enabledNetwork = mConfiguredNetworks.get(netId); 422 } 423 // check just in case the network was removed by someone else. 424 if (enabledNetwork != null) { 425 sendConfiguredNetworksChangedBroadcast(enabledNetwork, 426 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 427 } 428 } 429 return ret; 430 } 431 432 boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) { 433 boolean ret = mWifiNative.enableNetwork(netId, disableOthers); 434 435 WifiConfiguration config = mConfiguredNetworks.get(netId); 436 if (config != null) config.status = Status.ENABLED; 437 438 if (disableOthers) { 439 markAllNetworksDisabledExcept(netId); 440 } 441 return ret; 442 } 443 444 void disableAllNetworks() { 445 if (VDBG) localLog("disableAllNetworks"); 446 boolean networkDisabled = false; 447 for(WifiConfiguration config : mConfiguredNetworks.values()) { 448 if(config != null && config.status != Status.DISABLED) { 449 if(mWifiNative.disableNetwork(config.networkId)) { 450 networkDisabled = true; 451 config.status = Status.DISABLED; 452 } else { 453 loge("Disable network failed on " + config.networkId); 454 } 455 } 456 } 457 458 if (networkDisabled) { 459 sendConfiguredNetworksChangedBroadcast(); 460 } 461 } 462 /** 463 * Disable a network. Note that there is no saveConfig operation. 464 * @param netId network to be disabled 465 * @return {@code true} if it succeeds, {@code false} otherwise 466 */ 467 boolean disableNetwork(int netId) { 468 return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON); 469 } 470 471 /** 472 * Disable a network. Note that there is no saveConfig operation. 473 * @param netId network to be disabled 474 * @param reason reason code network was disabled 475 * @return {@code true} if it succeeds, {@code false} otherwise 476 */ 477 boolean disableNetwork(int netId, int reason) { 478 if (VDBG) localLog("disableNetwork", netId); 479 boolean ret = mWifiNative.disableNetwork(netId); 480 WifiConfiguration network = null; 481 WifiConfiguration config = mConfiguredNetworks.get(netId); 482 /* Only change the reason if the network was not previously disabled */ 483 if (config != null && config.status != Status.DISABLED) { 484 config.status = Status.DISABLED; 485 config.disableReason = reason; 486 network = config; 487 } 488 if (network != null) { 489 sendConfiguredNetworksChangedBroadcast(network, 490 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 491 } 492 return ret; 493 } 494 495 /** 496 * Save the configured networks in supplicant to disk 497 * @return {@code true} if it succeeds, {@code false} otherwise 498 */ 499 boolean saveConfig() { 500 return mWifiNative.saveConfig(); 501 } 502 503 /** 504 * Start WPS pin method configuration with pin obtained 505 * from the access point 506 * @param config WPS configuration 507 * @return Wps result containing status and pin 508 */ 509 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) { 510 WpsResult result = new WpsResult(); 511 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 512 /* WPS leaves all networks disabled */ 513 markAllNetworksDisabled(); 514 result.status = WpsResult.Status.SUCCESS; 515 } else { 516 loge("Failed to start WPS pin method configuration"); 517 result.status = WpsResult.Status.FAILURE; 518 } 519 return result; 520 } 521 522 /** 523 * Start WPS pin method configuration with pin obtained 524 * from the device 525 * @return WpsResult indicating status and pin 526 */ 527 WpsResult startWpsWithPinFromDevice(WpsInfo config) { 528 WpsResult result = new WpsResult(); 529 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 530 /* WPS leaves all networks disabled */ 531 if (!TextUtils.isEmpty(result.pin)) { 532 markAllNetworksDisabled(); 533 result.status = WpsResult.Status.SUCCESS; 534 } else { 535 loge("Failed to start WPS pin method configuration"); 536 result.status = WpsResult.Status.FAILURE; 537 } 538 return result; 539 } 540 541 /** 542 * Start WPS push button configuration 543 * @param config WPS configuration 544 * @return WpsResult indicating status and pin 545 */ 546 WpsResult startWpsPbc(WpsInfo config) { 547 WpsResult result = new WpsResult(); 548 if (mWifiNative.startWpsPbc(config.BSSID)) { 549 /* WPS leaves all networks disabled */ 550 markAllNetworksDisabled(); 551 result.status = WpsResult.Status.SUCCESS; 552 } else { 553 loge("Failed to start WPS push button configuration"); 554 result.status = WpsResult.Status.FAILURE; 555 } 556 return result; 557 } 558 559 /** 560 * Fetch the link properties for a given network id 561 * @return LinkProperties for the given network id 562 */ 563 LinkProperties getLinkProperties(int netId) { 564 WifiConfiguration config = mConfiguredNetworks.get(netId); 565 if (config != null) return new LinkProperties(config.linkProperties); 566 return null; 567 } 568 569 /** 570 * set IP configuration for a given network id 571 */ 572 void setLinkProperties(int netId, LinkProperties linkProperties) { 573 WifiConfiguration config = mConfiguredNetworks.get(netId); 574 if (config != null) { 575 // add old proxy details - TODO - is this still needed? 576 if(config.linkProperties != null) { 577 linkProperties.setHttpProxy(config.linkProperties.getHttpProxy()); 578 } 579 config.linkProperties = linkProperties; 580 } 581 } 582 583 /** 584 * clear IP configuration for a given network id 585 * @param network id 586 */ 587 void clearLinkProperties(int netId) { 588 WifiConfiguration config = mConfiguredNetworks.get(netId); 589 if (config != null && config.linkProperties != null) { 590 // Clear everything except proxy 591 ProxyProperties proxy = config.linkProperties.getHttpProxy(); 592 config.linkProperties.clear(); 593 config.linkProperties.setHttpProxy(proxy); 594 } 595 } 596 597 598 /** 599 * Fetch the proxy properties for a given network id 600 * @param network id 601 * @return ProxyProperties for the network id 602 */ 603 ProxyProperties getProxyProperties(int netId) { 604 LinkProperties linkProperties = getLinkProperties(netId); 605 if (linkProperties != null) { 606 return new ProxyProperties(linkProperties.getHttpProxy()); 607 } 608 return null; 609 } 610 611 /** 612 * Return if the specified network is using static IP 613 * @param network id 614 * @return {@code true} if using static ip for netId 615 */ 616 boolean isUsingStaticIp(int netId) { 617 WifiConfiguration config = mConfiguredNetworks.get(netId); 618 if (config != null && config.ipAssignment == IpAssignment.STATIC) { 619 return true; 620 } 621 return false; 622 } 623 624 /** 625 * Should be called when a single network configuration is made. 626 * @param network The network configuration that changed. 627 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 628 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 629 */ 630 private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, 631 int reason) { 632 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 633 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 634 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 635 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); 636 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 637 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 638 } 639 640 /** 641 * Should be called when multiple network configuration changes are made. 642 */ 643 private void sendConfiguredNetworksChangedBroadcast() { 644 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 645 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 646 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 647 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 648 } 649 650 void loadConfiguredNetworks() { 651 String listStr = mWifiNative.listNetworks(); 652 mLastPriority = 0; 653 654 mConfiguredNetworks.clear(); 655 mNetworkIds.clear(); 656 657 if (listStr == null) 658 return; 659 660 String[] lines = listStr.split("\n"); 661 // Skip the first line, which is a header 662 for (int i = 1; i < lines.length; i++) { 663 String[] result = lines[i].split("\t"); 664 // network-id | ssid | bssid | flags 665 WifiConfiguration config = new WifiConfiguration(); 666 try { 667 config.networkId = Integer.parseInt(result[0]); 668 } catch(NumberFormatException e) { 669 loge("Failed to read network-id '" + result[0] + "'"); 670 continue; 671 } 672 if (result.length > 3) { 673 if (result[3].indexOf("[CURRENT]") != -1) 674 config.status = WifiConfiguration.Status.CURRENT; 675 else if (result[3].indexOf("[DISABLED]") != -1) 676 config.status = WifiConfiguration.Status.DISABLED; 677 else 678 config.status = WifiConfiguration.Status.ENABLED; 679 } else { 680 config.status = WifiConfiguration.Status.ENABLED; 681 } 682 readNetworkVariables(config); 683 if (config.priority > mLastPriority) { 684 mLastPriority = config.priority; 685 } 686 config.ipAssignment = IpAssignment.DHCP; 687 config.proxySettings = ProxySettings.NONE; 688 689 if (mNetworkIds.containsKey(configKey(config))) { 690 // That SSID is already known, just ignore this duplicate entry 691 if (VDBG) localLog("discarded duplicate network", config.networkId); 692 } else { 693 mConfiguredNetworks.put(config.networkId, config); 694 mNetworkIds.put(configKey(config), config.networkId); 695 if (VDBG) localLog("loaded configured network", config.networkId); 696 } 697 } 698 699 readIpAndProxyConfigurations(); 700 sendConfiguredNetworksChangedBroadcast(); 701 702 if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks"); 703 704 if (mNetworkIds.size() == 0) { 705 // no networks? Lets log if the wpa_supplicant.conf file contents 706 BufferedReader reader = null; 707 try { 708 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 709 if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---"); 710 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 711 if (VDBG) localLog(line); 712 } 713 if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---"); 714 } catch (FileNotFoundException e) { 715 if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 716 } catch (IOException e) { 717 if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 718 } finally { 719 try { 720 if (reader != null) { 721 reader.close(); 722 } 723 } catch (IOException e) { 724 // Just ignore the fact that we couldn't close 725 } 726 } 727 } 728 } 729 730 /* Mark all networks except specified netId as disabled */ 731 private void markAllNetworksDisabledExcept(int netId) { 732 for(WifiConfiguration config : mConfiguredNetworks.values()) { 733 if(config != null && config.networkId != netId) { 734 if (config.status != Status.DISABLED) { 735 config.status = Status.DISABLED; 736 config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON; 737 } 738 } 739 } 740 } 741 742 private void markAllNetworksDisabled() { 743 markAllNetworksDisabledExcept(INVALID_NETWORK_ID); 744 } 745 746 boolean needsUnlockedKeyStore() { 747 748 // Any network using certificates to authenticate access requires 749 // unlocked key store; unless the certificates can be stored with 750 // hardware encryption 751 752 for(WifiConfiguration config : mConfiguredNetworks.values()) { 753 754 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) 755 && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 756 757 if (config.enterpriseConfig.needsSoftwareBackedKeyStore()) { 758 return true; 759 } 760 } 761 } 762 763 return false; 764 } 765 766 private void writeIpAndProxyConfigurations() { 767 768 /* Make a copy */ 769 List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 770 for(WifiConfiguration config : mConfiguredNetworks.values()) { 771 networks.add(new WifiConfiguration(config)); 772 } 773 774 DelayedDiskWrite.write(networks); 775 } 776 777 private static class DelayedDiskWrite { 778 779 private static HandlerThread sDiskWriteHandlerThread; 780 private static Handler sDiskWriteHandler; 781 /* Tracks multiple writes on the same thread */ 782 private static int sWriteSequence = 0; 783 private static final String TAG = "DelayedDiskWrite"; 784 785 static void write (final List<WifiConfiguration> networks) { 786 787 /* Do a delayed write to disk on a seperate handler thread */ 788 synchronized (DelayedDiskWrite.class) { 789 if (++sWriteSequence == 1) { 790 sDiskWriteHandlerThread = new HandlerThread("WifiConfigThread"); 791 sDiskWriteHandlerThread.start(); 792 sDiskWriteHandler = new Handler(sDiskWriteHandlerThread.getLooper()); 793 } 794 } 795 796 sDiskWriteHandler.post(new Runnable() { 797 @Override 798 public void run() { 799 onWriteCalled(networks); 800 } 801 }); 802 } 803 804 private static void onWriteCalled(List<WifiConfiguration> networks) { 805 806 DataOutputStream out = null; 807 try { 808 out = new DataOutputStream(new BufferedOutputStream( 809 new FileOutputStream(ipConfigFile))); 810 811 out.writeInt(IPCONFIG_FILE_VERSION); 812 813 for(WifiConfiguration config : networks) { 814 boolean writeToFile = false; 815 816 try { 817 LinkProperties linkProperties = config.linkProperties; 818 switch (config.ipAssignment) { 819 case STATIC: 820 out.writeUTF(IP_ASSIGNMENT_KEY); 821 out.writeUTF(config.ipAssignment.toString()); 822 for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) { 823 out.writeUTF(LINK_ADDRESS_KEY); 824 out.writeUTF(linkAddr.getAddress().getHostAddress()); 825 out.writeInt(linkAddr.getNetworkPrefixLength()); 826 } 827 for (RouteInfo route : linkProperties.getRoutes()) { 828 out.writeUTF(GATEWAY_KEY); 829 LinkAddress dest = route.getDestination(); 830 if (dest != null) { 831 out.writeInt(1); 832 out.writeUTF(dest.getAddress().getHostAddress()); 833 out.writeInt(dest.getNetworkPrefixLength()); 834 } else { 835 out.writeInt(0); 836 } 837 if (route.getGateway() != null) { 838 out.writeInt(1); 839 out.writeUTF(route.getGateway().getHostAddress()); 840 } else { 841 out.writeInt(0); 842 } 843 } 844 for (InetAddress inetAddr : linkProperties.getDnses()) { 845 out.writeUTF(DNS_KEY); 846 out.writeUTF(inetAddr.getHostAddress()); 847 } 848 writeToFile = true; 849 break; 850 case DHCP: 851 out.writeUTF(IP_ASSIGNMENT_KEY); 852 out.writeUTF(config.ipAssignment.toString()); 853 writeToFile = true; 854 break; 855 case UNASSIGNED: 856 /* Ignore */ 857 break; 858 default: 859 loge("Ignore invalid ip assignment while writing"); 860 break; 861 } 862 863 switch (config.proxySettings) { 864 case STATIC: 865 ProxyProperties proxyProperties = linkProperties.getHttpProxy(); 866 String exclusionList = proxyProperties.getExclusionList(); 867 out.writeUTF(PROXY_SETTINGS_KEY); 868 out.writeUTF(config.proxySettings.toString()); 869 out.writeUTF(PROXY_HOST_KEY); 870 out.writeUTF(proxyProperties.getHost()); 871 out.writeUTF(PROXY_PORT_KEY); 872 out.writeInt(proxyProperties.getPort()); 873 out.writeUTF(EXCLUSION_LIST_KEY); 874 out.writeUTF(exclusionList); 875 writeToFile = true; 876 break; 877 case PAC: 878 ProxyProperties proxyPacProperties = linkProperties.getHttpProxy(); 879 out.writeUTF(PROXY_SETTINGS_KEY); 880 out.writeUTF(config.proxySettings.toString()); 881 out.writeUTF(PROXY_PAC_FILE); 882 out.writeUTF(proxyPacProperties.getPacFileUrl()); 883 writeToFile = true; 884 break; 885 case NONE: 886 out.writeUTF(PROXY_SETTINGS_KEY); 887 out.writeUTF(config.proxySettings.toString()); 888 writeToFile = true; 889 break; 890 case UNASSIGNED: 891 /* Ignore */ 892 break; 893 default: 894 loge("Ignthisore invalid proxy settings while writing"); 895 break; 896 } 897 if (writeToFile) { 898 out.writeUTF(ID_KEY); 899 out.writeInt(configKey(config)); 900 } 901 } catch (NullPointerException e) { 902 loge("Failure in writing " + config.linkProperties + e); 903 } 904 out.writeUTF(EOS); 905 } 906 907 } catch (IOException e) { 908 loge("Error writing data file"); 909 } finally { 910 if (out != null) { 911 try { 912 out.close(); 913 } catch (Exception e) {} 914 } 915 916 //Quit if no more writes sent 917 synchronized (DelayedDiskWrite.class) { 918 if (--sWriteSequence == 0) { 919 sDiskWriteHandler.getLooper().quit(); 920 sDiskWriteHandler = null; 921 sDiskWriteHandlerThread = null; 922 } 923 } 924 } 925 } 926 927 private static void loge(String s) { 928 Log.e(TAG, s); 929 } 930 } 931 932 private void readIpAndProxyConfigurations() { 933 934 DataInputStream in = null; 935 try { 936 in = new DataInputStream(new BufferedInputStream(new FileInputStream( 937 ipConfigFile))); 938 939 int version = in.readInt(); 940 if (version != 2 && version != 1) { 941 loge("Bad version on IP configuration file, ignore read"); 942 return; 943 } 944 945 while (true) { 946 int id = -1; 947 // Default is DHCP with no proxy 948 IpAssignment ipAssignment = IpAssignment.DHCP; 949 ProxySettings proxySettings = ProxySettings.NONE; 950 LinkProperties linkProperties = new LinkProperties(); 951 String proxyHost = null; 952 String pacFileUrl = null; 953 int proxyPort = -1; 954 String exclusionList = null; 955 String key; 956 957 do { 958 key = in.readUTF(); 959 try { 960 if (key.equals(ID_KEY)) { 961 id = in.readInt(); 962 } else if (key.equals(IP_ASSIGNMENT_KEY)) { 963 ipAssignment = IpAssignment.valueOf(in.readUTF()); 964 } else if (key.equals(LINK_ADDRESS_KEY)) { 965 LinkAddress linkAddr = new LinkAddress( 966 NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt()); 967 linkProperties.addLinkAddress(linkAddr); 968 } else if (key.equals(GATEWAY_KEY)) { 969 LinkAddress dest = null; 970 InetAddress gateway = null; 971 if (version == 1) { 972 // only supported default gateways - leave the dest/prefix empty 973 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 974 } else { 975 if (in.readInt() == 1) { 976 dest = new LinkAddress( 977 NetworkUtils.numericToInetAddress(in.readUTF()), 978 in.readInt()); 979 } 980 if (in.readInt() == 1) { 981 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 982 } 983 } 984 linkProperties.addRoute(new RouteInfo(dest, gateway)); 985 } else if (key.equals(DNS_KEY)) { 986 linkProperties.addDns( 987 NetworkUtils.numericToInetAddress(in.readUTF())); 988 } else if (key.equals(PROXY_SETTINGS_KEY)) { 989 proxySettings = ProxySettings.valueOf(in.readUTF()); 990 } else if (key.equals(PROXY_HOST_KEY)) { 991 proxyHost = in.readUTF(); 992 } else if (key.equals(PROXY_PORT_KEY)) { 993 proxyPort = in.readInt(); 994 } else if (key.equals(PROXY_PAC_FILE)) { 995 pacFileUrl = in.readUTF(); 996 } else if (key.equals(EXCLUSION_LIST_KEY)) { 997 exclusionList = in.readUTF(); 998 } else if (key.equals(EOS)) { 999 break; 1000 } else { 1001 loge("Ignore unknown key " + key + "while reading"); 1002 } 1003 } catch (IllegalArgumentException e) { 1004 loge("Ignore invalid address while reading" + e); 1005 } 1006 } while (true); 1007 1008 if (id != -1) { 1009 WifiConfiguration config = mConfiguredNetworks.get( 1010 mNetworkIds.get(id)); 1011 1012 if (config == null) { 1013 loge("configuration found for missing network, ignored"); 1014 } else { 1015 config.linkProperties = linkProperties; 1016 switch (ipAssignment) { 1017 case STATIC: 1018 case DHCP: 1019 config.ipAssignment = ipAssignment; 1020 break; 1021 case UNASSIGNED: 1022 loge("BUG: Found UNASSIGNED IP on file, use DHCP"); 1023 config.ipAssignment = IpAssignment.DHCP; 1024 break; 1025 default: 1026 loge("Ignore invalid ip assignment while reading"); 1027 break; 1028 } 1029 1030 switch (proxySettings) { 1031 case STATIC: 1032 config.proxySettings = proxySettings; 1033 ProxyProperties proxyProperties = 1034 new ProxyProperties(proxyHost, proxyPort, exclusionList); 1035 linkProperties.setHttpProxy(proxyProperties); 1036 break; 1037 case PAC: 1038 config.proxySettings = proxySettings; 1039 ProxyProperties proxyPacProperties = 1040 new ProxyProperties(pacFileUrl); 1041 linkProperties.setHttpProxy(proxyPacProperties); 1042 break; 1043 case NONE: 1044 config.proxySettings = proxySettings; 1045 break; 1046 case UNASSIGNED: 1047 loge("BUG: Found UNASSIGNED proxy on file, use NONE"); 1048 config.proxySettings = ProxySettings.NONE; 1049 break; 1050 default: 1051 loge("Ignore invalid proxy settings while reading"); 1052 break; 1053 } 1054 } 1055 } else { 1056 if (DBG) log("Missing id while parsing configuration"); 1057 } 1058 } 1059 } catch (EOFException ignore) { 1060 } catch (IOException e) { 1061 loge("Error parsing configuration" + e); 1062 } finally { 1063 if (in != null) { 1064 try { 1065 in.close(); 1066 } catch (Exception e) {} 1067 } 1068 } 1069 } 1070 1071 private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) { 1072 /* 1073 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1074 * network configuration. Otherwise, the networkId should 1075 * refer to an existing configuration. 1076 */ 1077 1078 if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); 1079 1080 int netId = config.networkId; 1081 boolean newNetwork = false; 1082 // networkId of INVALID_NETWORK_ID means we want to create a new network 1083 if (netId == INVALID_NETWORK_ID) { 1084 Integer savedNetId = mNetworkIds.get(configKey(config)); 1085 if (savedNetId != null) { 1086 netId = savedNetId; 1087 } else { 1088 newNetwork = true; 1089 netId = mWifiNative.addNetwork(); 1090 if (netId < 0) { 1091 loge("Failed to add a network!"); 1092 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1093 } 1094 } 1095 } 1096 1097 boolean updateFailed = true; 1098 1099 setVariables: { 1100 1101 if (config.SSID != null && 1102 !mWifiNative.setNetworkVariable( 1103 netId, 1104 WifiConfiguration.ssidVarName, 1105 config.SSID)) { 1106 loge("failed to set SSID: "+config.SSID); 1107 break setVariables; 1108 } 1109 1110 if (config.BSSID != null && 1111 !mWifiNative.setNetworkVariable( 1112 netId, 1113 WifiConfiguration.bssidVarName, 1114 config.BSSID)) { 1115 loge("failed to set BSSID: "+config.BSSID); 1116 break setVariables; 1117 } 1118 1119 String allowedKeyManagementString = 1120 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 1121 if (config.allowedKeyManagement.cardinality() != 0 && 1122 !mWifiNative.setNetworkVariable( 1123 netId, 1124 WifiConfiguration.KeyMgmt.varName, 1125 allowedKeyManagementString)) { 1126 loge("failed to set key_mgmt: "+ 1127 allowedKeyManagementString); 1128 break setVariables; 1129 } 1130 1131 String allowedProtocolsString = 1132 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 1133 if (config.allowedProtocols.cardinality() != 0 && 1134 !mWifiNative.setNetworkVariable( 1135 netId, 1136 WifiConfiguration.Protocol.varName, 1137 allowedProtocolsString)) { 1138 loge("failed to set proto: "+ 1139 allowedProtocolsString); 1140 break setVariables; 1141 } 1142 1143 String allowedAuthAlgorithmsString = 1144 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 1145 if (config.allowedAuthAlgorithms.cardinality() != 0 && 1146 !mWifiNative.setNetworkVariable( 1147 netId, 1148 WifiConfiguration.AuthAlgorithm.varName, 1149 allowedAuthAlgorithmsString)) { 1150 loge("failed to set auth_alg: "+ 1151 allowedAuthAlgorithmsString); 1152 break setVariables; 1153 } 1154 1155 String allowedPairwiseCiphersString = 1156 makeString(config.allowedPairwiseCiphers, 1157 WifiConfiguration.PairwiseCipher.strings); 1158 if (config.allowedPairwiseCiphers.cardinality() != 0 && 1159 !mWifiNative.setNetworkVariable( 1160 netId, 1161 WifiConfiguration.PairwiseCipher.varName, 1162 allowedPairwiseCiphersString)) { 1163 loge("failed to set pairwise: "+ 1164 allowedPairwiseCiphersString); 1165 break setVariables; 1166 } 1167 1168 String allowedGroupCiphersString = 1169 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 1170 if (config.allowedGroupCiphers.cardinality() != 0 && 1171 !mWifiNative.setNetworkVariable( 1172 netId, 1173 WifiConfiguration.GroupCipher.varName, 1174 allowedGroupCiphersString)) { 1175 loge("failed to set group: "+ 1176 allowedGroupCiphersString); 1177 break setVariables; 1178 } 1179 1180 // Prevent client screw-up by passing in a WifiConfiguration we gave it 1181 // by preventing "*" as a key. 1182 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 1183 !mWifiNative.setNetworkVariable( 1184 netId, 1185 WifiConfiguration.pskVarName, 1186 config.preSharedKey)) { 1187 loge("failed to set psk"); 1188 break setVariables; 1189 } 1190 1191 boolean hasSetKey = false; 1192 if (config.wepKeys != null) { 1193 for (int i = 0; i < config.wepKeys.length; i++) { 1194 // Prevent client screw-up by passing in a WifiConfiguration we gave it 1195 // by preventing "*" as a key. 1196 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 1197 if (!mWifiNative.setNetworkVariable( 1198 netId, 1199 WifiConfiguration.wepKeyVarNames[i], 1200 config.wepKeys[i])) { 1201 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 1202 break setVariables; 1203 } 1204 hasSetKey = true; 1205 } 1206 } 1207 } 1208 1209 if (hasSetKey) { 1210 if (!mWifiNative.setNetworkVariable( 1211 netId, 1212 WifiConfiguration.wepTxKeyIdxVarName, 1213 Integer.toString(config.wepTxKeyIndex))) { 1214 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 1215 break setVariables; 1216 } 1217 } 1218 1219 if (!mWifiNative.setNetworkVariable( 1220 netId, 1221 WifiConfiguration.priorityVarName, 1222 Integer.toString(config.priority))) { 1223 loge(config.SSID + ": failed to set priority: " 1224 +config.priority); 1225 break setVariables; 1226 } 1227 1228 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 1229 netId, 1230 WifiConfiguration.hiddenSSIDVarName, 1231 Integer.toString(config.hiddenSSID ? 1 : 0))) { 1232 loge(config.SSID + ": failed to set hiddenSSID: "+ 1233 config.hiddenSSID); 1234 break setVariables; 1235 } 1236 1237 if (config.enterpriseConfig != null && 1238 config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 1239 1240 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 1241 1242 if (enterpriseConfig.needsKeyStore()) { 1243 /** 1244 * Keyguard settings may eventually be controlled by device policy. 1245 * We check here if keystore is unlocked before installing 1246 * credentials. 1247 * TODO: Do we need a dialog here ? 1248 */ 1249 if (mKeyStore.state() != KeyStore.State.UNLOCKED) { 1250 loge(config.SSID + ": key store is locked"); 1251 break setVariables; 1252 } 1253 1254 try { 1255 /* config passed may include only fields being updated. 1256 * In order to generate the key id, fetch uninitialized 1257 * fields from the currently tracked configuration 1258 */ 1259 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1260 String keyId = config.getKeyIdForCredentials(currentConfig); 1261 1262 if (!enterpriseConfig.installKeys(mKeyStore, keyId)) { 1263 loge(config.SSID + ": failed to install keys"); 1264 break setVariables; 1265 } 1266 } catch (IllegalStateException e) { 1267 loge(config.SSID + " invalid config for key installation"); 1268 break setVariables; 1269 } 1270 } 1271 1272 HashMap<String, String> enterpriseFields = enterpriseConfig.getFields(); 1273 for (String key : enterpriseFields.keySet()) { 1274 String value = enterpriseFields.get(key); 1275 if (!mWifiNative.setNetworkVariable( 1276 netId, 1277 key, 1278 value)) { 1279 enterpriseConfig.removeKeys(mKeyStore); 1280 loge(config.SSID + ": failed to set " + key + 1281 ": " + value); 1282 break setVariables; 1283 } 1284 } 1285 } 1286 updateFailed = false; 1287 } //end of setVariables 1288 1289 if (updateFailed) { 1290 if (newNetwork) { 1291 mWifiNative.removeNetwork(netId); 1292 loge("Failed to set a network variable, removed network: " + netId); 1293 } 1294 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1295 } 1296 1297 /* An update of the network variables requires reading them 1298 * back from the supplicant to update mConfiguredNetworks. 1299 * This is because some of the variables (SSID, wep keys & 1300 * passphrases) reflect different values when read back than 1301 * when written. For example, wep key is stored as * irrespective 1302 * of the value sent to the supplicant 1303 */ 1304 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1305 if (currentConfig == null) { 1306 currentConfig = new WifiConfiguration(); 1307 currentConfig.ipAssignment = IpAssignment.DHCP; 1308 currentConfig.proxySettings = ProxySettings.NONE; 1309 currentConfig.networkId = netId; 1310 } 1311 1312 readNetworkVariables(currentConfig); 1313 1314 mConfiguredNetworks.put(netId, currentConfig); 1315 mNetworkIds.put(configKey(currentConfig), netId); 1316 1317 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config); 1318 result.setIsNewNetwork(newNetwork); 1319 result.setNetworkId(netId); 1320 return result; 1321 } 1322 1323 /* Compare current and new configuration and write to file on change */ 1324 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 1325 WifiConfiguration currentConfig, 1326 WifiConfiguration newConfig) { 1327 boolean ipChanged = false; 1328 boolean proxyChanged = false; 1329 LinkProperties linkProperties = null; 1330 1331 switch (newConfig.ipAssignment) { 1332 case STATIC: 1333 Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties 1334 .getLinkAddresses(); 1335 Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties 1336 .getLinkAddresses(); 1337 Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses(); 1338 Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses(); 1339 Collection<RouteInfo> currentRoutes = currentConfig.linkProperties.getRoutes(); 1340 Collection<RouteInfo> newRoutes = newConfig.linkProperties.getRoutes(); 1341 1342 boolean linkAddressesDiffer = 1343 (currentLinkAddresses.size() != newLinkAddresses.size()) || 1344 !currentLinkAddresses.containsAll(newLinkAddresses); 1345 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || 1346 !currentDnses.containsAll(newDnses); 1347 boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) || 1348 !currentRoutes.containsAll(newRoutes); 1349 1350 if ((currentConfig.ipAssignment != newConfig.ipAssignment) || 1351 linkAddressesDiffer || 1352 dnsesDiffer || 1353 routesDiffer) { 1354 ipChanged = true; 1355 } 1356 break; 1357 case DHCP: 1358 if (currentConfig.ipAssignment != newConfig.ipAssignment) { 1359 ipChanged = true; 1360 } 1361 break; 1362 case UNASSIGNED: 1363 /* Ignore */ 1364 break; 1365 default: 1366 loge("Ignore invalid ip assignment during write"); 1367 break; 1368 } 1369 1370 switch (newConfig.proxySettings) { 1371 case STATIC: 1372 case PAC: 1373 ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy(); 1374 ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy(); 1375 1376 if (newHttpProxy != null) { 1377 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 1378 } else { 1379 proxyChanged = (currentHttpProxy != null); 1380 } 1381 break; 1382 case NONE: 1383 if (currentConfig.proxySettings != newConfig.proxySettings) { 1384 proxyChanged = true; 1385 } 1386 break; 1387 case UNASSIGNED: 1388 /* Ignore */ 1389 break; 1390 default: 1391 loge("Ignore invalid proxy configuration during write"); 1392 break; 1393 } 1394 1395 if (!ipChanged) { 1396 linkProperties = copyIpSettingsFromConfig(currentConfig); 1397 } else { 1398 currentConfig.ipAssignment = newConfig.ipAssignment; 1399 linkProperties = copyIpSettingsFromConfig(newConfig); 1400 log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " + 1401 linkProperties.toString()); 1402 } 1403 1404 1405 if (!proxyChanged) { 1406 linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy()); 1407 } else { 1408 currentConfig.proxySettings = newConfig.proxySettings; 1409 linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy()); 1410 log("proxy changed SSID = " + currentConfig.SSID); 1411 if (linkProperties.getHttpProxy() != null) { 1412 log(" proxyProperties: " + linkProperties.getHttpProxy().toString()); 1413 } 1414 } 1415 1416 if (ipChanged || proxyChanged) { 1417 currentConfig.linkProperties = linkProperties; 1418 writeIpAndProxyConfigurations(); 1419 sendConfiguredNetworksChangedBroadcast(currentConfig, 1420 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1421 } 1422 return new NetworkUpdateResult(ipChanged, proxyChanged); 1423 } 1424 1425 private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) { 1426 LinkProperties linkProperties = new LinkProperties(); 1427 linkProperties.setInterfaceName(config.linkProperties.getInterfaceName()); 1428 for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) { 1429 linkProperties.addLinkAddress(linkAddr); 1430 } 1431 for (RouteInfo route : config.linkProperties.getRoutes()) { 1432 linkProperties.addRoute(route); 1433 } 1434 for (InetAddress dns : config.linkProperties.getDnses()) { 1435 linkProperties.addDns(dns); 1436 } 1437 return linkProperties; 1438 } 1439 1440 /** 1441 * Read the variables from the supplicant daemon that are needed to 1442 * fill in the WifiConfiguration object. 1443 * 1444 * @param config the {@link WifiConfiguration} object to be filled in. 1445 */ 1446 private void readNetworkVariables(WifiConfiguration config) { 1447 1448 int netId = config.networkId; 1449 if (netId < 0) 1450 return; 1451 1452 /* 1453 * TODO: maybe should have a native method that takes an array of 1454 * variable names and returns an array of values. But we'd still 1455 * be doing a round trip to the supplicant daemon for each variable. 1456 */ 1457 String value; 1458 1459 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 1460 if (!TextUtils.isEmpty(value)) { 1461 if (value.charAt(0) != '"') { 1462 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 1463 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 1464 //supplicant string 1465 } else { 1466 config.SSID = value; 1467 } 1468 } else { 1469 config.SSID = null; 1470 } 1471 1472 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 1473 if (!TextUtils.isEmpty(value)) { 1474 config.BSSID = value; 1475 } else { 1476 config.BSSID = null; 1477 } 1478 1479 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 1480 config.priority = -1; 1481 if (!TextUtils.isEmpty(value)) { 1482 try { 1483 config.priority = Integer.parseInt(value); 1484 } catch (NumberFormatException ignore) { 1485 } 1486 } 1487 1488 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 1489 config.hiddenSSID = false; 1490 if (!TextUtils.isEmpty(value)) { 1491 try { 1492 config.hiddenSSID = Integer.parseInt(value) != 0; 1493 } catch (NumberFormatException ignore) { 1494 } 1495 } 1496 1497 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 1498 config.wepTxKeyIndex = -1; 1499 if (!TextUtils.isEmpty(value)) { 1500 try { 1501 config.wepTxKeyIndex = Integer.parseInt(value); 1502 } catch (NumberFormatException ignore) { 1503 } 1504 } 1505 1506 for (int i = 0; i < 4; i++) { 1507 value = mWifiNative.getNetworkVariable(netId, 1508 WifiConfiguration.wepKeyVarNames[i]); 1509 if (!TextUtils.isEmpty(value)) { 1510 config.wepKeys[i] = value; 1511 } else { 1512 config.wepKeys[i] = null; 1513 } 1514 } 1515 1516 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 1517 if (!TextUtils.isEmpty(value)) { 1518 config.preSharedKey = value; 1519 } else { 1520 config.preSharedKey = null; 1521 } 1522 1523 value = mWifiNative.getNetworkVariable(config.networkId, 1524 WifiConfiguration.Protocol.varName); 1525 if (!TextUtils.isEmpty(value)) { 1526 String vals[] = value.split(" "); 1527 for (String val : vals) { 1528 int index = 1529 lookupString(val, WifiConfiguration.Protocol.strings); 1530 if (0 <= index) { 1531 config.allowedProtocols.set(index); 1532 } 1533 } 1534 } 1535 1536 value = mWifiNative.getNetworkVariable(config.networkId, 1537 WifiConfiguration.KeyMgmt.varName); 1538 if (!TextUtils.isEmpty(value)) { 1539 String vals[] = value.split(" "); 1540 for (String val : vals) { 1541 int index = 1542 lookupString(val, WifiConfiguration.KeyMgmt.strings); 1543 if (0 <= index) { 1544 config.allowedKeyManagement.set(index); 1545 } 1546 } 1547 } 1548 1549 value = mWifiNative.getNetworkVariable(config.networkId, 1550 WifiConfiguration.AuthAlgorithm.varName); 1551 if (!TextUtils.isEmpty(value)) { 1552 String vals[] = value.split(" "); 1553 for (String val : vals) { 1554 int index = 1555 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 1556 if (0 <= index) { 1557 config.allowedAuthAlgorithms.set(index); 1558 } 1559 } 1560 } 1561 1562 value = mWifiNative.getNetworkVariable(config.networkId, 1563 WifiConfiguration.PairwiseCipher.varName); 1564 if (!TextUtils.isEmpty(value)) { 1565 String vals[] = value.split(" "); 1566 for (String val : vals) { 1567 int index = 1568 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 1569 if (0 <= index) { 1570 config.allowedPairwiseCiphers.set(index); 1571 } 1572 } 1573 } 1574 1575 value = mWifiNative.getNetworkVariable(config.networkId, 1576 WifiConfiguration.GroupCipher.varName); 1577 if (!TextUtils.isEmpty(value)) { 1578 String vals[] = value.split(" "); 1579 for (String val : vals) { 1580 int index = 1581 lookupString(val, WifiConfiguration.GroupCipher.strings); 1582 if (0 <= index) { 1583 config.allowedGroupCiphers.set(index); 1584 } 1585 } 1586 } 1587 1588 if (config.enterpriseConfig == null) { 1589 config.enterpriseConfig = new WifiEnterpriseConfig(); 1590 } 1591 HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields(); 1592 for (String key : WifiEnterpriseConfig.getSupplicantKeys()) { 1593 value = mWifiNative.getNetworkVariable(netId, key); 1594 if (!TextUtils.isEmpty(value)) { 1595 enterpriseFields.put(key, removeDoubleQuotes(value)); 1596 } else { 1597 enterpriseFields.put(key, WifiEnterpriseConfig.EMPTY_VALUE); 1598 } 1599 } 1600 1601 if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) { 1602 saveConfig(); 1603 } 1604 1605 config.enterpriseConfig.migrateCerts(mKeyStore); 1606 config.enterpriseConfig.initializeSoftwareKeystoreFlag(mKeyStore); 1607 } 1608 1609 private String removeDoubleQuotes(String string) { 1610 int length = string.length(); 1611 if ((length > 1) && (string.charAt(0) == '"') 1612 && (string.charAt(length - 1) == '"')) { 1613 return string.substring(1, length - 1); 1614 } 1615 return string; 1616 } 1617 1618 private String convertToQuotedString(String string) { 1619 return "\"" + string + "\""; 1620 } 1621 1622 private String makeString(BitSet set, String[] strings) { 1623 StringBuffer buf = new StringBuffer(); 1624 int nextSetBit = -1; 1625 1626 /* Make sure all set bits are in [0, strings.length) to avoid 1627 * going out of bounds on strings. (Shouldn't happen, but...) */ 1628 set = set.get(0, strings.length); 1629 1630 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 1631 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 1632 } 1633 1634 // remove trailing space 1635 if (set.cardinality() > 0) { 1636 buf.setLength(buf.length() - 1); 1637 } 1638 1639 return buf.toString(); 1640 } 1641 1642 private int lookupString(String string, String[] strings) { 1643 int size = strings.length; 1644 1645 string = string.replace('-', '_'); 1646 1647 for (int i = 0; i < size; i++) 1648 if (string.equals(strings[i])) 1649 return i; 1650 1651 // if we ever get here, we should probably add the 1652 // value to WifiConfiguration to reflect that it's 1653 // supported by the WPA supplicant 1654 loge("Failed to look-up a string: " + string); 1655 1656 return -1; 1657 } 1658 1659 /* Returns a unique for a given configuration */ 1660 private static int configKey(WifiConfiguration config) { 1661 String key; 1662 1663 if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1664 key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK]; 1665 } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) || 1666 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1667 key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP]; 1668 } else if (config.wepKeys[0] != null) { 1669 key = config.SSID + "WEP"; 1670 } else { 1671 key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE]; 1672 } 1673 1674 return key.hashCode(); 1675 } 1676 1677 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1678 pw.println("WifiConfigStore"); 1679 pw.println("mLastPriority " + mLastPriority); 1680 pw.println("Configured networks"); 1681 for (WifiConfiguration conf : getConfiguredNetworks()) { 1682 pw.println(conf); 1683 } 1684 pw.println(); 1685 1686 if (mLocalLog != null) { 1687 pw.println("WifiConfigStore - Log Begin ----"); 1688 mLocalLog.dump(fd, pw, args); 1689 pw.println("WifiConfigStore - Log End ----"); 1690 } 1691 } 1692 1693 public String getConfigFile() { 1694 return ipConfigFile; 1695 } 1696 1697 private void loge(String s) { 1698 Log.e(TAG, s); 1699 } 1700 1701 private void log(String s) { 1702 Log.d(TAG, s); 1703 } 1704 1705 private void localLog(String s) { 1706 if (mLocalLog != null) { 1707 mLocalLog.log(s); 1708 } 1709 } 1710 1711 private void localLog(String s, int netId) { 1712 if (mLocalLog == null) { 1713 return; 1714 } 1715 1716 WifiConfiguration config; 1717 synchronized(mConfiguredNetworks) { 1718 config = mConfiguredNetworks.get(netId); 1719 } 1720 1721 if (config != null) { 1722 mLocalLog.log(s + " " + config.getPrintableSsid()); 1723 } else { 1724 mLocalLog.log(s + " " + netId); 1725 } 1726 } 1727 1728 } 1729