WifiConfigStore.java revision 992564e481af13cbcb058ee801f9254a520c54a1
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(InetAddress.getByName( 690 in.readUTF()), in.readInt()); 691 linkProperties.addLinkAddress(linkAddr); 692 } else if (key.equals(GATEWAY_KEY)) { 693 linkProperties.addGateway(InetAddress.getByName(in.readUTF())); 694 } else if (key.equals(DNS_KEY)) { 695 linkProperties.addDns(InetAddress.getByName(in.readUTF())); 696 } else if (key.equals(PROXY_SETTINGS_KEY)) { 697 proxySettings = ProxySettings.valueOf(in.readUTF()); 698 } else if (key.equals(PROXY_HOST_KEY)) { 699 proxyHost = in.readUTF(); 700 } else if (key.equals(PROXY_PORT_KEY)) { 701 proxyPort = in.readInt(); 702 } else if (key.equals(EXCLUSION_LIST_KEY)) { 703 exclusionList = in.readUTF(); 704 } else if (key.equals(EOS)) { 705 break; 706 } else { 707 Log.e(TAG, "Ignore unknown key " + key + "while reading"); 708 } 709 } catch (UnknownHostException e) { 710 Log.e(TAG, "Ignore invalid address while reading" + e); 711 } 712 } while (true); 713 714 if (id != -1) { 715 synchronized (sConfiguredNetworks) { 716 WifiConfiguration config = sConfiguredNetworks.get( 717 sNetworkIds.get(id)); 718 719 if (config == null) { 720 Log.e(TAG, "configuration found for missing network, ignored"); 721 } else { 722 config.linkProperties = linkProperties; 723 switch (ipAssignment) { 724 case STATIC: 725 case DHCP: 726 config.ipAssignment = ipAssignment; 727 break; 728 case UNASSIGNED: 729 //Ignore 730 break; 731 default: 732 Log.e(TAG, "Ignore invalid ip assignment while reading"); 733 break; 734 } 735 736 switch (proxySettings) { 737 case STATIC: 738 config.proxySettings = proxySettings; 739 ProxyProperties proxyProperties = 740 new ProxyProperties(proxyHost, proxyPort, exclusionList); 741 linkProperties.setHttpProxy(proxyProperties); 742 break; 743 case NONE: 744 config.proxySettings = proxySettings; 745 break; 746 case UNASSIGNED: 747 //Ignore 748 break; 749 default: 750 Log.e(TAG, "Ignore invalid proxy settings while reading"); 751 break; 752 } 753 } 754 } 755 } else { 756 Log.e(TAG, "Missing id while parsing configuration"); 757 } 758 } 759 } catch (EOFException ignore) { 760 } catch (IOException e) { 761 Log.e(TAG, "Error parsing configuration" + e); 762 } finally { 763 if (in != null) { 764 try { 765 in.close(); 766 } catch (Exception e) {} 767 } 768 } 769 } 770 771 private static NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) { 772 /* 773 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 774 * network configuration. Otherwise, the networkId should 775 * refer to an existing configuration. 776 */ 777 int netId = config.networkId; 778 boolean updateFailed = true; 779 // networkId of INVALID_NETWORK_ID means we want to create a new network 780 boolean newNetwork = (netId == INVALID_NETWORK_ID); 781 782 if (newNetwork) { 783 netId = WifiNative.addNetworkCommand(); 784 if (netId < 0) { 785 Log.e(TAG, "Failed to add a network!"); 786 return new NetworkUpdateResult(INVALID_NETWORK_ID); 787 } 788 } 789 790 setVariables: { 791 792 if (config.SSID != null && 793 !WifiNative.setNetworkVariableCommand( 794 netId, 795 WifiConfiguration.ssidVarName, 796 config.SSID)) { 797 Log.d(TAG, "failed to set SSID: "+config.SSID); 798 break setVariables; 799 } 800 801 if (config.BSSID != null && 802 !WifiNative.setNetworkVariableCommand( 803 netId, 804 WifiConfiguration.bssidVarName, 805 config.BSSID)) { 806 Log.d(TAG, "failed to set BSSID: "+config.BSSID); 807 break setVariables; 808 } 809 810 String allowedKeyManagementString = 811 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 812 if (config.allowedKeyManagement.cardinality() != 0 && 813 !WifiNative.setNetworkVariableCommand( 814 netId, 815 WifiConfiguration.KeyMgmt.varName, 816 allowedKeyManagementString)) { 817 Log.d(TAG, "failed to set key_mgmt: "+ 818 allowedKeyManagementString); 819 break setVariables; 820 } 821 822 String allowedProtocolsString = 823 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 824 if (config.allowedProtocols.cardinality() != 0 && 825 !WifiNative.setNetworkVariableCommand( 826 netId, 827 WifiConfiguration.Protocol.varName, 828 allowedProtocolsString)) { 829 Log.d(TAG, "failed to set proto: "+ 830 allowedProtocolsString); 831 break setVariables; 832 } 833 834 String allowedAuthAlgorithmsString = 835 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 836 if (config.allowedAuthAlgorithms.cardinality() != 0 && 837 !WifiNative.setNetworkVariableCommand( 838 netId, 839 WifiConfiguration.AuthAlgorithm.varName, 840 allowedAuthAlgorithmsString)) { 841 Log.d(TAG, "failed to set auth_alg: "+ 842 allowedAuthAlgorithmsString); 843 break setVariables; 844 } 845 846 String allowedPairwiseCiphersString = 847 makeString(config.allowedPairwiseCiphers, 848 WifiConfiguration.PairwiseCipher.strings); 849 if (config.allowedPairwiseCiphers.cardinality() != 0 && 850 !WifiNative.setNetworkVariableCommand( 851 netId, 852 WifiConfiguration.PairwiseCipher.varName, 853 allowedPairwiseCiphersString)) { 854 Log.d(TAG, "failed to set pairwise: "+ 855 allowedPairwiseCiphersString); 856 break setVariables; 857 } 858 859 String allowedGroupCiphersString = 860 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 861 if (config.allowedGroupCiphers.cardinality() != 0 && 862 !WifiNative.setNetworkVariableCommand( 863 netId, 864 WifiConfiguration.GroupCipher.varName, 865 allowedGroupCiphersString)) { 866 Log.d(TAG, "failed to set group: "+ 867 allowedGroupCiphersString); 868 break setVariables; 869 } 870 871 // Prevent client screw-up by passing in a WifiConfiguration we gave it 872 // by preventing "*" as a key. 873 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 874 !WifiNative.setNetworkVariableCommand( 875 netId, 876 WifiConfiguration.pskVarName, 877 config.preSharedKey)) { 878 Log.d(TAG, "failed to set psk: "+config.preSharedKey); 879 break setVariables; 880 } 881 882 boolean hasSetKey = false; 883 if (config.wepKeys != null) { 884 for (int i = 0; i < config.wepKeys.length; i++) { 885 // Prevent client screw-up by passing in a WifiConfiguration we gave it 886 // by preventing "*" as a key. 887 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 888 if (!WifiNative.setNetworkVariableCommand( 889 netId, 890 WifiConfiguration.wepKeyVarNames[i], 891 config.wepKeys[i])) { 892 Log.d(TAG, 893 "failed to set wep_key"+i+": " + 894 config.wepKeys[i]); 895 break setVariables; 896 } 897 hasSetKey = true; 898 } 899 } 900 } 901 902 if (hasSetKey) { 903 if (!WifiNative.setNetworkVariableCommand( 904 netId, 905 WifiConfiguration.wepTxKeyIdxVarName, 906 Integer.toString(config.wepTxKeyIndex))) { 907 Log.d(TAG, 908 "failed to set wep_tx_keyidx: "+ 909 config.wepTxKeyIndex); 910 break setVariables; 911 } 912 } 913 914 if (!WifiNative.setNetworkVariableCommand( 915 netId, 916 WifiConfiguration.priorityVarName, 917 Integer.toString(config.priority))) { 918 Log.d(TAG, config.SSID + ": failed to set priority: " 919 +config.priority); 920 break setVariables; 921 } 922 923 if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( 924 netId, 925 WifiConfiguration.hiddenSSIDVarName, 926 Integer.toString(config.hiddenSSID ? 1 : 0))) { 927 Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ 928 config.hiddenSSID); 929 break setVariables; 930 } 931 932 for (WifiConfiguration.EnterpriseField field 933 : config.enterpriseFields) { 934 String varName = field.varName(); 935 String value = field.value(); 936 if (value != null) { 937 if (field != config.eap) { 938 value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); 939 } 940 if (!WifiNative.setNetworkVariableCommand( 941 netId, 942 varName, 943 value)) { 944 Log.d(TAG, config.SSID + ": failed to set " + varName + 945 ": " + value); 946 break setVariables; 947 } 948 } 949 } 950 updateFailed = false; 951 } 952 953 if (updateFailed) { 954 if (newNetwork) { 955 WifiNative.removeNetworkCommand(netId); 956 Log.d(TAG, 957 "Failed to set a network variable, removed network: " 958 + netId); 959 } 960 return new NetworkUpdateResult(INVALID_NETWORK_ID); 961 } 962 963 /* An update of the network variables requires reading them 964 * back from the supplicant to update sConfiguredNetworks. 965 * This is because some of the variables (SSID, wep keys & 966 * passphrases) reflect different values when read back than 967 * when written. For example, wep key is stored as * irrespective 968 * of the value sent to the supplicant 969 */ 970 WifiConfiguration sConfig; 971 synchronized (sConfiguredNetworks) { 972 sConfig = sConfiguredNetworks.get(netId); 973 } 974 if (sConfig == null) { 975 sConfig = new WifiConfiguration(); 976 sConfig.networkId = netId; 977 synchronized (sConfiguredNetworks) { 978 sConfiguredNetworks.put(netId, sConfig); 979 } 980 } 981 readNetworkVariables(sConfig); 982 983 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(sConfig, config); 984 result.setNetworkId(netId); 985 return result; 986 } 987 988 /* Compare current and new configuration and write to file on change */ 989 private static NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 990 WifiConfiguration currentConfig, 991 WifiConfiguration newConfig) { 992 boolean ipChanged = false; 993 boolean proxyChanged = false; 994 LinkProperties linkProperties = new LinkProperties(); 995 996 switch (newConfig.ipAssignment) { 997 case STATIC: 998 Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties 999 .getLinkAddresses(); 1000 Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties 1001 .getLinkAddresses(); 1002 Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses(); 1003 Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses(); 1004 Collection<InetAddress> currentGateways = 1005 currentConfig.linkProperties.getGateways(); 1006 Collection<InetAddress> newGateways = newConfig.linkProperties.getGateways(); 1007 1008 boolean linkAddressesDiffer = 1009 (currentLinkAddresses.size() != newLinkAddresses.size()) || 1010 !currentLinkAddresses.containsAll(newLinkAddresses); 1011 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || 1012 !currentDnses.containsAll(newDnses); 1013 boolean gatewaysDiffer = (currentGateways.size() != newGateways.size()) || 1014 !currentGateways.containsAll(newGateways); 1015 1016 if ((currentConfig.ipAssignment != newConfig.ipAssignment) || 1017 linkAddressesDiffer || 1018 dnsesDiffer || 1019 gatewaysDiffer) { 1020 ipChanged = true; 1021 } 1022 break; 1023 case DHCP: 1024 if (currentConfig.ipAssignment != newConfig.ipAssignment) { 1025 ipChanged = true; 1026 } 1027 break; 1028 case UNASSIGNED: 1029 /* Ignore */ 1030 break; 1031 default: 1032 Log.e(TAG, "Ignore invalid ip assignment during write"); 1033 break; 1034 } 1035 1036 switch (newConfig.proxySettings) { 1037 case STATIC: 1038 ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy(); 1039 ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy(); 1040 1041 if (newHttpProxy != null) { 1042 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 1043 } else { 1044 proxyChanged = (currentHttpProxy != null); 1045 } 1046 break; 1047 case NONE: 1048 if (currentConfig.proxySettings != newConfig.proxySettings) { 1049 proxyChanged = true; 1050 } 1051 break; 1052 case UNASSIGNED: 1053 /* Ignore */ 1054 break; 1055 default: 1056 Log.e(TAG, "Ignore invalid proxy configuration during write"); 1057 break; 1058 } 1059 1060 if (!ipChanged) { 1061 addIpSettingsFromConfig(linkProperties, currentConfig); 1062 } else { 1063 currentConfig.ipAssignment = newConfig.ipAssignment; 1064 addIpSettingsFromConfig(linkProperties, newConfig); 1065 Log.d(TAG, "IP config changed SSID = " + currentConfig.SSID + " linkProperties: " + 1066 linkProperties.toString()); 1067 } 1068 1069 1070 if (!proxyChanged) { 1071 linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy()); 1072 } else { 1073 currentConfig.proxySettings = newConfig.proxySettings; 1074 linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy()); 1075 Log.d(TAG, "proxy changed SSID = " + currentConfig.SSID); 1076 if (linkProperties.getHttpProxy() != null) { 1077 Log.d(TAG, " proxyProperties: " + linkProperties.getHttpProxy().toString()); 1078 } 1079 } 1080 1081 if (ipChanged || proxyChanged) { 1082 currentConfig.linkProperties = linkProperties; 1083 writeIpAndProxyConfigurations(); 1084 sendConfiguredNetworksChangedBroadcast(); 1085 } 1086 return new NetworkUpdateResult(ipChanged, proxyChanged); 1087 } 1088 1089 private static void addIpSettingsFromConfig(LinkProperties linkProperties, 1090 WifiConfiguration config) { 1091 for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) { 1092 linkProperties.addLinkAddress(linkAddr); 1093 } 1094 for (InetAddress gateway : config.linkProperties.getGateways()) { 1095 linkProperties.addGateway(gateway); 1096 } 1097 for (InetAddress dns : config.linkProperties.getDnses()) { 1098 linkProperties.addDns(dns); 1099 } 1100 } 1101 1102 /** 1103 * Read the variables from the supplicant daemon that are needed to 1104 * fill in the WifiConfiguration object. 1105 * 1106 * @param config the {@link WifiConfiguration} object to be filled in. 1107 */ 1108 private static void readNetworkVariables(WifiConfiguration config) { 1109 1110 int netId = config.networkId; 1111 if (netId < 0) 1112 return; 1113 1114 /* 1115 * TODO: maybe should have a native method that takes an array of 1116 * variable names and returns an array of values. But we'd still 1117 * be doing a round trip to the supplicant daemon for each variable. 1118 */ 1119 String value; 1120 1121 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); 1122 if (!TextUtils.isEmpty(value)) { 1123 config.SSID = value; 1124 } else { 1125 config.SSID = null; 1126 } 1127 1128 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); 1129 if (!TextUtils.isEmpty(value)) { 1130 config.BSSID = value; 1131 } else { 1132 config.BSSID = null; 1133 } 1134 1135 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); 1136 config.priority = -1; 1137 if (!TextUtils.isEmpty(value)) { 1138 try { 1139 config.priority = Integer.parseInt(value); 1140 } catch (NumberFormatException ignore) { 1141 } 1142 } 1143 1144 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); 1145 config.hiddenSSID = false; 1146 if (!TextUtils.isEmpty(value)) { 1147 try { 1148 config.hiddenSSID = Integer.parseInt(value) != 0; 1149 } catch (NumberFormatException ignore) { 1150 } 1151 } 1152 1153 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); 1154 config.wepTxKeyIndex = -1; 1155 if (!TextUtils.isEmpty(value)) { 1156 try { 1157 config.wepTxKeyIndex = Integer.parseInt(value); 1158 } catch (NumberFormatException ignore) { 1159 } 1160 } 1161 1162 for (int i = 0; i < 4; i++) { 1163 value = WifiNative.getNetworkVariableCommand(netId, 1164 WifiConfiguration.wepKeyVarNames[i]); 1165 if (!TextUtils.isEmpty(value)) { 1166 config.wepKeys[i] = value; 1167 } else { 1168 config.wepKeys[i] = null; 1169 } 1170 } 1171 1172 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); 1173 if (!TextUtils.isEmpty(value)) { 1174 config.preSharedKey = value; 1175 } else { 1176 config.preSharedKey = null; 1177 } 1178 1179 value = WifiNative.getNetworkVariableCommand(config.networkId, 1180 WifiConfiguration.Protocol.varName); 1181 if (!TextUtils.isEmpty(value)) { 1182 String vals[] = value.split(" "); 1183 for (String val : vals) { 1184 int index = 1185 lookupString(val, WifiConfiguration.Protocol.strings); 1186 if (0 <= index) { 1187 config.allowedProtocols.set(index); 1188 } 1189 } 1190 } 1191 1192 value = WifiNative.getNetworkVariableCommand(config.networkId, 1193 WifiConfiguration.KeyMgmt.varName); 1194 if (!TextUtils.isEmpty(value)) { 1195 String vals[] = value.split(" "); 1196 for (String val : vals) { 1197 int index = 1198 lookupString(val, WifiConfiguration.KeyMgmt.strings); 1199 if (0 <= index) { 1200 config.allowedKeyManagement.set(index); 1201 } 1202 } 1203 } 1204 1205 value = WifiNative.getNetworkVariableCommand(config.networkId, 1206 WifiConfiguration.AuthAlgorithm.varName); 1207 if (!TextUtils.isEmpty(value)) { 1208 String vals[] = value.split(" "); 1209 for (String val : vals) { 1210 int index = 1211 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 1212 if (0 <= index) { 1213 config.allowedAuthAlgorithms.set(index); 1214 } 1215 } 1216 } 1217 1218 value = WifiNative.getNetworkVariableCommand(config.networkId, 1219 WifiConfiguration.PairwiseCipher.varName); 1220 if (!TextUtils.isEmpty(value)) { 1221 String vals[] = value.split(" "); 1222 for (String val : vals) { 1223 int index = 1224 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 1225 if (0 <= index) { 1226 config.allowedPairwiseCiphers.set(index); 1227 } 1228 } 1229 } 1230 1231 value = WifiNative.getNetworkVariableCommand(config.networkId, 1232 WifiConfiguration.GroupCipher.varName); 1233 if (!TextUtils.isEmpty(value)) { 1234 String vals[] = value.split(" "); 1235 for (String val : vals) { 1236 int index = 1237 lookupString(val, WifiConfiguration.GroupCipher.strings); 1238 if (0 <= index) { 1239 config.allowedGroupCiphers.set(index); 1240 } 1241 } 1242 } 1243 1244 for (WifiConfiguration.EnterpriseField field : 1245 config.enterpriseFields) { 1246 value = WifiNative.getNetworkVariableCommand(netId, 1247 field.varName()); 1248 if (!TextUtils.isEmpty(value)) { 1249 if (field != config.eap) value = removeDoubleQuotes(value); 1250 field.setValue(value); 1251 } 1252 } 1253 } 1254 1255 private static String removeDoubleQuotes(String string) { 1256 if (string.length() <= 2) return ""; 1257 return string.substring(1, string.length() - 1); 1258 } 1259 1260 private static String convertToQuotedString(String string) { 1261 return "\"" + string + "\""; 1262 } 1263 1264 private static String makeString(BitSet set, String[] strings) { 1265 StringBuffer buf = new StringBuffer(); 1266 int nextSetBit = -1; 1267 1268 /* Make sure all set bits are in [0, strings.length) to avoid 1269 * going out of bounds on strings. (Shouldn't happen, but...) */ 1270 set = set.get(0, strings.length); 1271 1272 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 1273 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 1274 } 1275 1276 // remove trailing space 1277 if (set.cardinality() > 0) { 1278 buf.setLength(buf.length() - 1); 1279 } 1280 1281 return buf.toString(); 1282 } 1283 1284 private static int lookupString(String string, String[] strings) { 1285 int size = strings.length; 1286 1287 string = string.replace('-', '_'); 1288 1289 for (int i = 0; i < size; i++) 1290 if (string.equals(strings[i])) 1291 return i; 1292 1293 // if we ever get here, we should probably add the 1294 // value to WifiConfiguration to reflect that it's 1295 // supported by the WPA supplicant 1296 Log.w(TAG, "Failed to look-up a string: " + string); 1297 1298 return -1; 1299 } 1300 1301 /* Returns a unique for a given configuration */ 1302 private static int configKey(WifiConfiguration config) { 1303 String key; 1304 1305 if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1306 key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK]; 1307 } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) || 1308 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1309 key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP]; 1310 } else if (config.wepKeys[0] != null) { 1311 key = config.SSID + "WEP"; 1312 } else { 1313 key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE]; 1314 } 1315 1316 return key.hashCode(); 1317 } 1318 1319 static String dump() { 1320 StringBuffer sb = new StringBuffer(); 1321 String LS = System.getProperty("line.separator"); 1322 sb.append("sLastPriority ").append(sLastPriority).append(LS); 1323 sb.append("Configured networks ").append(LS); 1324 for (WifiConfiguration conf : getConfiguredNetworks()) { 1325 sb.append(conf).append(LS); 1326 } 1327 return sb.toString(); 1328 } 1329} 1330