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