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