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