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