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