WifiConfigStore.java revision dfcb187e5608e125fe161d70226c300bd7b781ad
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 com.android.server.wifi; 18 19import android.content.Context; 20import android.content.Intent; 21import android.net.IpConfiguration; 22import android.net.IpConfiguration.IpAssignment; 23import android.net.IpConfiguration.ProxySettings; 24import android.net.LinkAddress; 25import android.net.LinkProperties; 26import android.net.NetworkInfo.DetailedState; 27import android.net.ProxyInfo; 28import android.net.RouteInfo; 29import android.net.wifi.WifiConfiguration; 30import android.net.wifi.WifiConfiguration.KeyMgmt; 31import android.net.wifi.WifiConfiguration.Status; 32import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 33 34import android.net.wifi.WifiEnterpriseConfig; 35import android.net.wifi.WifiManager; 36import android.net.wifi.WifiSsid; 37import android.net.wifi.WpsInfo; 38import android.net.wifi.WpsResult; 39import android.net.wifi.ScanResult; 40 41import android.os.Environment; 42import android.os.FileObserver; 43import android.os.Process; 44import android.os.SystemClock; 45import android.os.UserHandle; 46import android.security.Credentials; 47import android.security.KeyChain; 48import android.security.KeyStore; 49import android.text.TextUtils; 50import android.util.LocalLog; 51import android.util.Log; 52import android.util.SparseArray; 53 54import com.android.server.net.DelayedDiskWrite; 55import com.android.server.net.IpConfigStore; 56 57import java.io.BufferedReader; 58import java.io.BufferedInputStream; 59import java.io.DataInputStream; 60import java.io.DataOutputStream; 61import java.io.EOFException; 62import java.io.File; 63import java.io.FileDescriptor; 64import java.io.FileInputStream; 65import java.io.FileNotFoundException; 66import java.io.FileReader; 67import java.io.IOException; 68import java.io.PrintWriter; 69import java.math.BigInteger; 70import java.net.InetAddress; 71import java.nio.charset.Charset; 72import java.security.PrivateKey; 73import java.security.cert.Certificate; 74import java.security.cert.CertificateException; 75import java.text.SimpleDateFormat; 76import java.text.DateFormat; 77import java.util.ArrayList; 78import java.util.BitSet; 79import java.util.Collection; 80import java.util.Date; 81import java.util.HashMap; 82import java.util.List; 83import java.util.Map; 84 85/** 86 * This class provides the API to manage configured 87 * wifi networks. The API is not thread safe is being 88 * used only from WifiStateMachine. 89 * 90 * It deals with the following 91 * - Add/update/remove a WifiConfiguration 92 * The configuration contains two types of information. 93 * = IP and proxy configuration that is handled by WifiConfigStore and 94 * is saved to disk on any change. 95 * 96 * The format of configuration file is as follows: 97 * <version> 98 * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS> 99 * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS> 100 * .. 101 * 102 * (key, value) pairs for a given network are grouped together and can 103 * be in any order. A EOS at the end of a set of (key, value) pairs 104 * indicates that the next set of (key, value) pairs are for a new 105 * network. A network is identified by a unique ID_KEY. If there is no 106 * ID_KEY in the (key, value) pairs, the data is discarded. 107 * 108 * An invalid version on read would result in discarding the contents of 109 * the file. On the next write, the latest version is written to file. 110 * 111 * Any failures during read or write to the configuration file are ignored 112 * without reporting to the user since the likelihood of these errors are 113 * low and the impact on connectivity is low. 114 * 115 * = SSID & security details that is pushed to the supplicant. 116 * supplicant saves these details to the disk on calling 117 * saveConfigCommand(). 118 * 119 * We have two kinds of APIs exposed: 120 * > public API calls that provide fine grained control 121 * - enableNetwork, disableNetwork, addOrUpdateNetwork(), 122 * removeNetwork(). For these calls, the config is not persisted 123 * to the disk. (TODO: deprecate these calls in WifiManager) 124 * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork(). 125 * These calls persist the supplicant config to disk. 126 * 127 * - Maintain a list of configured networks for quick access 128 * 129 */ 130public class WifiConfigStore extends IpConfigStore { 131 132 private Context mContext; 133 private static final String TAG = "WifiConfigStore"; 134 private static final boolean DBG = true; 135 private static boolean VDBG = false; 136 137 private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; 138 139 /* configured networks with network id as the key */ 140 private HashMap<Integer, WifiConfiguration> mConfiguredNetworks = 141 new HashMap<Integer, WifiConfiguration>(); 142 143 /* A network id is a unique identifier for a network configured in the 144 * supplicant. Network ids are generated when the supplicant reads 145 * the configuration file at start and can thus change for networks. 146 * We store the IP configuration for networks along with a unique id 147 * that is generated from SSID and security type of the network. A mapping 148 * from the generated unique id to network id of the network is needed to 149 * map supplicant config to IP configuration. */ 150 private HashMap<Integer, Integer> mNetworkIds = 151 new HashMap<Integer, Integer>(); 152 153 /* Tracks the highest priority of configured networks */ 154 private int mLastPriority = -1; 155 156 private static final String ipConfigFile = Environment.getDataDirectory() + 157 "/misc/wifi/ipconfig.txt"; 158 159 private static final String networkHistoryConfigFile = Environment.getDataDirectory() + 160 "/misc/wifi/networkHistory.txt"; 161 162 /* Network History Keys */ 163 private static final String SSID_KEY = "SSID: "; 164 private static final String CONFIG_KEY = "CONFIG: "; 165 private static final String CHOICE_KEY = "CHOICE: "; 166 private static final String LINK_KEY = "LINK: "; 167 private static final String BSSID_KEY = "BSSID: "; 168 private static final String BSSID_KEY_END = "/BSSID: "; 169 private static final String RSSI_KEY = "RSSI: "; 170 private static final String FREQ_KEY = "FREQ: "; 171 private static final String DATE_KEY = "DATE: "; 172 private static final String MILLI_KEY = "MILLI: "; 173 private static final String NETWORK_ID_KEY = "ID: "; 174 private static final String PRIORITY_KEY = "PRIORITY: "; 175 private static final String DEFAULT_GW_KEY = "DEFAULT_GW: "; 176 private static final String AUTH_KEY = "AUTH: "; 177 private static final String SEPARATOR_KEY = "\n"; 178 private static final String STATUS_KEY = "AUTO_JOIN_STATUS: "; 179 private static final String SELF_ADDED_KEY = "SELF_ADDED: "; 180 private static final String FAILURE_KEY = "FAILURE: "; 181 182 /* Enterprise configuration keys */ 183 /** 184 * In old configurations, the "private_key" field was used. However, newer 185 * configurations use the key_id field with the engine_id set to "keystore". 186 * If this field is found in the configuration, the migration code is 187 * triggered. 188 */ 189 public static final String OLD_PRIVATE_KEY_NAME = "private_key"; 190 191 /** This represents an empty value of an enterprise field. 192 * NULL is used at wpa_supplicant to indicate an empty value 193 */ 194 static final String EMPTY_VALUE = "NULL"; 195 196 /** Internal use only */ 197 private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] { 198 WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY, 199 WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY, 200 WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY, 201 WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY, 202 WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY, 203 WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY }; 204 205 private final LocalLog mLocalLog; 206 private final WpaConfigFileObserver mFileObserver; 207 208 private WifiNative mWifiNative; 209 private final KeyStore mKeyStore = KeyStore.getInstance(); 210 211 /* 212 * lastSelectedConfiguration is used to remember which network was selected last by the user. 213 * The connection to this network may not be successful, as well 214 * the selection (i.e. network priority) might not be persisted. 215 * WiFi state machine is the only object that sets this variable. 216 */ 217 private String lastSelectedConfiguration = null; 218 219 WifiConfigStore(Context c, WifiNative wn) { 220 mContext = c; 221 mWifiNative = wn; 222 223 if (VDBG) { 224 mLocalLog = mWifiNative.getLocalLog(); 225 mFileObserver = new WpaConfigFileObserver(); 226 mFileObserver.startWatching(); 227 } else { 228 mLocalLog = null; 229 mFileObserver = null; 230 } 231 } 232 233 void enableVerboseLogging(int verbose) { 234 if (verbose > 0) { 235 VDBG = true; 236 } else { 237 VDBG = false; 238 } 239 } 240 241 class WpaConfigFileObserver extends FileObserver { 242 243 public WpaConfigFileObserver() { 244 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 245 } 246 247 @Override 248 public void onEvent(int event, String path) { 249 if (event == CLOSE_WRITE) { 250 File file = new File(SUPPLICANT_CONFIG_FILE); 251 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 252 } 253 } 254 } 255 256 257 /** 258 * Fetch the list of configured networks 259 * and enable all stored networks in supplicant. 260 */ 261 void loadAndEnableAllNetworks() { 262 if (DBG) log("Loading config and enabling all networks "); 263 loadConfiguredNetworks(); 264 enableAllNetworks(); 265 } 266 267 /** 268 * Fetch the list of currently configured networks 269 * @return List of networks 270 */ 271 List<WifiConfiguration> getConfiguredNetworks() { 272 List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 273 for(WifiConfiguration config : mConfiguredNetworks.values()) { 274 networks.add(new WifiConfiguration(config)); 275 } 276 return networks; 277 } 278 279 /** 280 * Fetch the list of currently configured networks that were recently seen 281 * 282 * @return List of networks 283 */ 284 List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) { 285 List<WifiConfiguration> networks = null; 286 287 for (WifiConfiguration config : mConfiguredNetworks.values()) { 288 // calculate the RSSI for scan results that are more recent than milli 289 config.setVisibility(milli); 290 291 if (config.visibility == null) { 292 continue; 293 } 294 if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI && 295 config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) { 296 continue; 297 } 298 if (networks == null) 299 networks = new ArrayList<WifiConfiguration>(); 300 if (copy) { 301 networks.add(new WifiConfiguration(config)); 302 } else { 303 networks.add(config); 304 } 305 } 306 return networks; 307 } 308 309 /** 310 * get the Wificonfiguration for this netId 311 * 312 * @return Wificonfiguration 313 */ 314 315 WifiConfiguration getWifiConfiguration(int netId) { 316 if (mConfiguredNetworks == null) 317 return null; 318 return mConfiguredNetworks.get(netId); 319 } 320 321 322 /** 323 * get the Wificonfiguration for this key 324 * 325 * @return Wificonfiguration 326 */ 327 328 WifiConfiguration getWifiConfiguration(String key) { 329 if (key == null) 330 return null; 331 int hash = key.hashCode(); 332 if (mNetworkIds == null) 333 return null; 334 Integer n = mNetworkIds.get(hash); 335 if (n == null) 336 return null; 337 int netId = n.intValue(); 338 return getWifiConfiguration(netId); 339 } 340 341 /** 342 * enable all networks and save config. This will be a no-op if the list 343 * of configured networks indicates all networks as being enabled 344 */ 345 void enableAllNetworks() { 346 boolean networkEnabledStateChanged = false; 347 for(WifiConfiguration config : mConfiguredNetworks.values()) { 348 if(config != null && config.status == Status.DISABLED 349 && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) { 350 if(mWifiNative.enableNetwork(config.networkId, false)) { 351 networkEnabledStateChanged = true; 352 config.status = Status.ENABLED; 353 } else { 354 loge("Enable network failed on " + config.networkId); 355 } 356 } 357 } 358 359 if (networkEnabledStateChanged) { 360 mWifiNative.saveConfig(); 361 sendConfiguredNetworksChangedBroadcast(); 362 } 363 } 364 365 366 /** 367 * Selects the specified network for connection. This involves 368 * updating the priority of all the networks and enabling the given 369 * network while disabling others. 370 * 371 * Selecting a network will leave the other networks disabled and 372 * a call to enableAllNetworks() needs to be issued upon a connection 373 * or a failure event from supplicant 374 * 375 * @param netId network to select for connection 376 * @return false if the network id is invalid 377 */ 378 boolean selectNetwork(int netId) { 379 if (VDBG) localLog("selectNetwork", netId); 380 if (netId == INVALID_NETWORK_ID) return false; 381 382 // Reset the priority of each network at start or if it goes too high. 383 if (mLastPriority == -1 || mLastPriority > 1000000) { 384 for(WifiConfiguration config : mConfiguredNetworks.values()) { 385 if (config.networkId != INVALID_NETWORK_ID) { 386 config.priority = 0; 387 addOrUpdateNetworkNative(config); 388 } 389 } 390 mLastPriority = 0; 391 } 392 393 // Set to the highest priority and save the configuration. 394 WifiConfiguration config = new WifiConfiguration(); 395 config.networkId = netId; 396 config.priority = ++mLastPriority; 397 398 addOrUpdateNetworkNative(config); 399 mWifiNative.saveConfig(); 400 401 /* Enable the given network while disabling all other networks */ 402 enableNetworkWithoutBroadcast(netId, true); 403 404 /* Avoid saving the config & sending a broadcast to prevent settings 405 * from displaying a disabled list of networks */ 406 return true; 407 } 408 409 /** 410 * Add/update the specified configuration and save config 411 * 412 * @param config WifiConfiguration to be saved 413 * @return network update result 414 */ 415 NetworkUpdateResult saveNetwork(WifiConfiguration config) { 416 WifiConfiguration conf; 417 418 // A new network cannot have null SSID 419 if (config == null || (config.networkId == INVALID_NETWORK_ID && 420 config.SSID == null)) { 421 return new NetworkUpdateResult(INVALID_NETWORK_ID); 422 } 423 if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId); 424 if (VDBG) { 425 loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size() 426 + " SSID=" + config.SSID); 427 } 428 boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); 429 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 430 int netId = result.getNetworkId(); 431 432 if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId); 433 434 /* enable a new network */ 435 if (newNetwork && netId != INVALID_NETWORK_ID) { 436 if (VDBG) localLog("WifiConfigStore: will enable netId=", netId); 437 438 mWifiNative.enableNetwork(netId, false); 439 conf = mConfiguredNetworks.get(netId); 440 if (conf != null) 441 conf.status = Status.ENABLED; 442 } 443 444 conf = mConfiguredNetworks.get(netId); 445 if (conf != null) { 446 if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) { 447 if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID); 448 449 // reenable autojoin, since new information has been provided 450 conf.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED; 451 enableNetworkWithoutBroadcast(conf.networkId, false); 452 } 453 } 454 455 mWifiNative.saveConfig(); 456 sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ? 457 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 458 return result; 459 } 460 461 void updateStatus(int netId, DetailedState state) { 462 if (netId != INVALID_NETWORK_ID) { 463 WifiConfiguration config = mConfiguredNetworks.get(netId); 464 if (config == null) return; 465 switch (state) { 466 case CONNECTED: 467 config.status = Status.CURRENT; 468 break; 469 case DISCONNECTED: 470 //If network is already disabled, keep the status 471 if (config.status == Status.CURRENT) { 472 config.status = Status.ENABLED; 473 } 474 break; 475 default: 476 //do nothing, retain the existing state 477 break; 478 } 479 } 480 } 481 482 /** 483 * Forget the specified network and save config 484 * 485 * @param netId network to forget 486 * @return {@code true} if it succeeds, {@code false} otherwise 487 */ 488 boolean forgetNetwork(int netId) { 489 if (VDBG) localLog("forgetNetwork", netId); 490 if (mWifiNative.removeNetwork(netId)) { 491 mWifiNative.saveConfig(); 492 removeConfigAndSendBroadcastIfNeeded(netId); 493 return true; 494 } else { 495 loge("Failed to remove network " + netId); 496 return false; 497 } 498 } 499 500 /** 501 * Add/update a network. Note that there is no saveConfig operation. 502 * This function is retained for compatibility with the public 503 * API. The more powerful saveNetwork() is used by the 504 * state machine 505 * 506 * @param config wifi configuration to add/update 507 * @return network Id 508 */ 509 int addOrUpdateNetwork(WifiConfiguration config) { 510 if (VDBG) localLog("addOrUpdateNetwork", config.networkId); 511 //adding unconditional message to chase b/15111865 512 Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)); 513 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 514 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 515 WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId()); 516 if (conf != null) { 517 sendConfiguredNetworksChangedBroadcast(conf, 518 result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED : 519 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 520 } 521 } 522 return result.getNetworkId(); 523 } 524 525 /** 526 * Remove a network. Note that there is no saveConfig operation. 527 * This function is retained for compatibility with the public 528 * API. The more powerful forgetNetwork() is used by the 529 * state machine for network removal 530 * 531 * @param netId network to be removed 532 * @return {@code true} if it succeeds, {@code false} otherwise 533 */ 534 boolean removeNetwork(int netId) { 535 if (VDBG) localLog("removeNetwork", netId); 536 boolean ret = mWifiNative.removeNetwork(netId); 537 if (ret) { 538 removeConfigAndSendBroadcastIfNeeded(netId); 539 } 540 return ret; 541 } 542 543 private void removeConfigAndSendBroadcastIfNeeded(int netId) { 544 WifiConfiguration config = mConfiguredNetworks.get(netId); 545 if (config != null) { 546 if (VDBG) { 547 loge("removeNetwork " + Integer.toString(netId) + " key=" + 548 config.configKey() + " config.id=" + Integer.toString(config.networkId)); 549 } 550 551 // cancel the last user choice 552 if (config.configKey().equals(lastSelectedConfiguration)) { 553 lastSelectedConfiguration = null; 554 } 555 556 // Remove any associated keys 557 if (config.enterpriseConfig != null) { 558 removeKeys(config.enterpriseConfig); 559 } 560 mConfiguredNetworks.remove(netId); 561 mNetworkIds.remove(configKey(config)); 562 if (VDBG) { 563 loge("removeNetwork -> num " + mConfiguredNetworks.size()); 564 for (WifiConfiguration c : mConfiguredNetworks.values()) { 565 loge(" has got " + c.configKey() + " id=" + Integer.toString(c.networkId)); 566 567 } 568 } 569 570 writeIpAndProxyConfigurations(); 571 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 572 writeKnownNetworkHistory(); 573 } 574 } 575 576 /** 577 * Enable a network. Note that there is no saveConfig operation. 578 * This function is retained for compatibility with the public 579 * API. The more powerful selectNetwork()/saveNetwork() is used by the 580 * state machine for connecting to a network 581 * 582 * @param netId network to be enabled 583 * @return {@code true} if it succeeds, {@code false} otherwise 584 */ 585 boolean enableNetwork(int netId, boolean disableOthers) { 586 boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers); 587 if (disableOthers) { 588 if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId); 589 sendConfiguredNetworksChangedBroadcast(); 590 } else { 591 if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId); 592 WifiConfiguration enabledNetwork = null; 593 synchronized(mConfiguredNetworks) { 594 enabledNetwork = mConfiguredNetworks.get(netId); 595 } 596 // check just in case the network was removed by someone else. 597 if (enabledNetwork != null) { 598 sendConfiguredNetworksChangedBroadcast(enabledNetwork, 599 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 600 } 601 } 602 return ret; 603 } 604 605 boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) { 606 boolean ret = mWifiNative.enableNetwork(netId, disableOthers); 607 608 WifiConfiguration config = mConfiguredNetworks.get(netId); 609 if (config != null) config.status = Status.ENABLED; 610 611 if (disableOthers) { 612 markAllNetworksDisabledExcept(netId); 613 } 614 return ret; 615 } 616 617 void disableAllNetworks() { 618 if (VDBG) localLog("disableAllNetworks"); 619 boolean networkDisabled = false; 620 for(WifiConfiguration config : mConfiguredNetworks.values()) { 621 if(config != null && config.status != Status.DISABLED) { 622 if(mWifiNative.disableNetwork(config.networkId)) { 623 networkDisabled = true; 624 config.status = Status.DISABLED; 625 } else { 626 loge("Disable network failed on " + config.networkId); 627 } 628 } 629 } 630 631 if (networkDisabled) { 632 sendConfiguredNetworksChangedBroadcast(); 633 } 634 } 635 /** 636 * Disable a network. Note that there is no saveConfig operation. 637 * @param netId network to be disabled 638 * @return {@code true} if it succeeds, {@code false} otherwise 639 */ 640 boolean disableNetwork(int netId) { 641 return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON); 642 } 643 644 /** 645 * Disable a network. Note that there is no saveConfig operation. 646 * @param netId network to be disabled 647 * @param reason reason code network was disabled 648 * @return {@code true} if it succeeds, {@code false} otherwise 649 */ 650 boolean disableNetwork(int netId, int reason) { 651 if (VDBG) localLog("disableNetwork", netId); 652 boolean ret = mWifiNative.disableNetwork(netId); 653 WifiConfiguration network = null; 654 WifiConfiguration config = mConfiguredNetworks.get(netId); 655 656 if (VDBG) { 657 if (config != null) { 658 loge("disableNetwork netId=" + Integer.toString(netId) 659 + " SSID=" + config.SSID 660 + " disabled=" + (config.status == Status.DISABLED) 661 + " reason=" + Integer.toString(config.disableReason)); 662 } 663 } 664 /* Only change the reason if the network was not previously disabled */ 665 if (config != null && config.status != Status.DISABLED) { 666 config.status = Status.DISABLED; 667 config.disableReason = reason; 668 network = config; 669 } 670 if (network != null) { 671 sendConfiguredNetworksChangedBroadcast(network, 672 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 673 } 674 return ret; 675 } 676 677 /** 678 * Save the configured networks in supplicant to disk 679 * @return {@code true} if it succeeds, {@code false} otherwise 680 */ 681 boolean saveConfig() { 682 return mWifiNative.saveConfig(); 683 } 684 685 /** 686 * Start WPS pin method configuration with pin obtained 687 * from the access point 688 * @param config WPS configuration 689 * @return Wps result containing status and pin 690 */ 691 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) { 692 WpsResult result = new WpsResult(); 693 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 694 /* WPS leaves all networks disabled */ 695 markAllNetworksDisabled(); 696 result.status = WpsResult.Status.SUCCESS; 697 } else { 698 loge("Failed to start WPS pin method configuration"); 699 result.status = WpsResult.Status.FAILURE; 700 } 701 return result; 702 } 703 704 /** 705 * Start WPS pin method configuration with pin obtained 706 * from the device 707 * @return WpsResult indicating status and pin 708 */ 709 WpsResult startWpsWithPinFromDevice(WpsInfo config) { 710 WpsResult result = new WpsResult(); 711 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 712 /* WPS leaves all networks disabled */ 713 if (!TextUtils.isEmpty(result.pin)) { 714 markAllNetworksDisabled(); 715 result.status = WpsResult.Status.SUCCESS; 716 } else { 717 loge("Failed to start WPS pin method configuration"); 718 result.status = WpsResult.Status.FAILURE; 719 } 720 return result; 721 } 722 723 /** 724 * Start WPS push button configuration 725 * @param config WPS configuration 726 * @return WpsResult indicating status and pin 727 */ 728 WpsResult startWpsPbc(WpsInfo config) { 729 WpsResult result = new WpsResult(); 730 if (mWifiNative.startWpsPbc(config.BSSID)) { 731 /* WPS leaves all networks disabled */ 732 markAllNetworksDisabled(); 733 result.status = WpsResult.Status.SUCCESS; 734 } else { 735 loge("Failed to start WPS push button configuration"); 736 result.status = WpsResult.Status.FAILURE; 737 } 738 return result; 739 } 740 741 /** 742 * Fetch the link properties for a given network id 743 * 744 * @return LinkProperties for the given network id 745 */ 746 LinkProperties getLinkProperties(int netId) { 747 WifiConfiguration config = mConfiguredNetworks.get(netId); 748 if (config != null) return new LinkProperties(config.getLinkProperties()); 749 return null; 750 } 751 752 /** 753 * set IP configuration for a given network id 754 */ 755 void setLinkProperties(int netId, LinkProperties linkProperties) { 756 WifiConfiguration config = mConfiguredNetworks.get(netId); 757 if (config != null) { 758 // add old proxy details - TODO - is this still needed? 759 if(config.getLinkProperties() != null) { 760 linkProperties.setHttpProxy(config.getLinkProperties().getHttpProxy()); 761 } 762 config.setLinkProperties(linkProperties); 763 } 764 } 765 766 /** 767 * set default GW MAC address 768 */ 769 void setDefaultGwMacAddress(int netId, String macAddress) { 770 WifiConfiguration config = mConfiguredNetworks.get(netId); 771 if (config != null) { 772 //update defaultGwMacAddress 773 config.defaultGwMacAddress = macAddress; 774 } 775 } 776 777 778 /** 779 * clear IP configuration for a given network id 780 * @param network id 781 */ 782 void clearLinkProperties(int netId) { 783 WifiConfiguration config = mConfiguredNetworks.get(netId); 784 if (config != null && config.getLinkProperties() != null) { 785 // Clear everything except proxy 786 ProxyInfo proxy = config.getLinkProperties().getHttpProxy(); 787 config.getLinkProperties().clear(); 788 config.getLinkProperties().setHttpProxy(proxy); 789 } 790 } 791 792 793 /** 794 * Fetch the proxy properties for a given network id 795 * @param network id 796 * @return ProxyInfo for the network id 797 */ 798 ProxyInfo getProxyProperties(int netId) { 799 LinkProperties linkProperties = getLinkProperties(netId); 800 if (linkProperties != null) { 801 return new ProxyInfo(linkProperties.getHttpProxy()); 802 } 803 return null; 804 } 805 806 /** 807 * Return if the specified network is using static IP 808 * @param network id 809 * @return {@code true} if using static ip for netId 810 */ 811 boolean isUsingStaticIp(int netId) { 812 WifiConfiguration config = mConfiguredNetworks.get(netId); 813 if (config != null && config.getIpAssignment() == IpAssignment.STATIC) { 814 return true; 815 } 816 return false; 817 } 818 819 /** 820 * Should be called when a single network configuration is made. 821 * @param network The network configuration that changed. 822 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 823 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 824 */ 825 private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, 826 int reason) { 827 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 828 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 829 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 830 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); 831 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 832 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 833 } 834 835 /** 836 * Should be called when multiple network configuration changes are made. 837 */ 838 private void sendConfiguredNetworksChangedBroadcast() { 839 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 840 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 841 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 842 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 843 } 844 845 void loadConfiguredNetworks() { 846 String listStr = mWifiNative.listNetworks(); 847 mLastPriority = 0; 848 849 mConfiguredNetworks.clear(); 850 mNetworkIds.clear(); 851 852 if (listStr == null) 853 return; 854 855 String[] lines = listStr.split("\n"); 856 857 if (VDBG) { 858 loge("loadConfiguredNetworks: found " + Integer.toString(lines.length) 859 + " networks", true); 860 } 861 862 // Skip the first line, which is a header 863 for (int i = 1; i < lines.length; i++) { 864 String[] result = lines[i].split("\t"); 865 // network-id | ssid | bssid | flags 866 WifiConfiguration config = new WifiConfiguration(); 867 try { 868 config.networkId = Integer.parseInt(result[0]); 869 } catch(NumberFormatException e) { 870 loge("Failed to read network-id '" + result[0] + "'"); 871 continue; 872 } 873 if (result.length > 3) { 874 if (result[3].indexOf("[CURRENT]") != -1) 875 config.status = WifiConfiguration.Status.CURRENT; 876 else if (result[3].indexOf("[DISABLED]") != -1) 877 config.status = WifiConfiguration.Status.DISABLED; 878 else 879 config.status = WifiConfiguration.Status.ENABLED; 880 } else { 881 config.status = WifiConfiguration.Status.ENABLED; 882 } 883 readNetworkVariables(config); 884 if (config.priority > mLastPriority) { 885 mLastPriority = config.priority; 886 } 887 888 config.setIpAssignment(IpAssignment.DHCP); 889 config.setProxySettings(ProxySettings.NONE); 890 891 if (mNetworkIds.containsKey(configKey(config))) { 892 // That SSID is already known, just ignore this duplicate entry 893 if (VDBG) localLog("discarded duplicate network ", config.networkId); 894 } else if(config.isValid()){ 895 mConfiguredNetworks.put(config.networkId, config); 896 mNetworkIds.put(configKey(config), config.networkId); 897 if (VDBG) localLog("loaded configured network", config.networkId); 898 } else { 899 if (DBG) log("Ignoring loaded configured for network " + config.networkId 900 + " because config are not valid"); 901 } 902 } 903 904 readIpAndProxyConfigurations(); 905 readNetworkHistory(); 906 907 sendConfiguredNetworksChangedBroadcast(); 908 909 if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks"); 910 911 if (mNetworkIds.size() == 0) { 912 // no networks? Lets log if the wpa_supplicant.conf file contents 913 BufferedReader reader = null; 914 try { 915 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 916 if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---"); 917 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 918 if (VDBG) localLog(line); 919 } 920 if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---"); 921 } catch (FileNotFoundException e) { 922 if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 923 } catch (IOException e) { 924 if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 925 } finally { 926 try { 927 if (reader != null) { 928 reader.close(); 929 } 930 } catch (IOException e) { 931 // Just ignore the fact that we couldn't close 932 } 933 } 934 } 935 } 936 937 private String readNetworkVariableFromSupplicantFile(String ssid, String key) { 938 BufferedReader reader = null; 939 if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key); 940 941 String value = null; 942 try { 943 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 944 boolean found = false; 945 boolean networkMatched = false; 946 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 947 if (VDBG) loge(line); 948 949 if (line.matches("[ \\t]*network=\\{")) { 950 found = true; 951 networkMatched = false; 952 } else if (line.matches("[ \\t]*\\{")) { 953 found = false; 954 networkMatched = false; 955 } 956 957 if (found) { 958 int index; 959 if ((index = line.indexOf("ssid=")) >= 0) { 960 String networkSSid = line.substring(index + 5); 961 if (networkSSid.regionMatches(0, ssid, 0, ssid.length())) { 962 networkMatched = true; 963 } else { 964 networkMatched = false; 965 } 966 } 967 968 if (networkMatched) { 969 if ((index = line.indexOf(key + "=")) >= 0) { 970 971 value = line.substring(index + key.length() + 1); 972 if (VDBG) loge("found key " + value); 973 974 } 975 } 976 } 977 } 978 } catch (FileNotFoundException e) { 979 if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 980 } catch (IOException e) { 981 if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 982 } finally { 983 try { 984 if (reader != null) { 985 reader.close(); 986 } 987 } catch (IOException e) { 988 // Just ignore the fact that we couldn't close 989 } 990 } 991 992 return value; 993 } 994 995 /* Mark all networks except specified netId as disabled */ 996 private void markAllNetworksDisabledExcept(int netId) { 997 for(WifiConfiguration config : mConfiguredNetworks.values()) { 998 if(config != null && config.networkId != netId) { 999 if (config.status != Status.DISABLED) { 1000 config.status = Status.DISABLED; 1001 config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON; 1002 } 1003 } 1004 } 1005 } 1006 1007 private void markAllNetworksDisabled() { 1008 markAllNetworksDisabledExcept(INVALID_NETWORK_ID); 1009 } 1010 1011 boolean needsUnlockedKeyStore() { 1012 1013 // Any network using certificates to authenticate access requires 1014 // unlocked key store; unless the certificates can be stored with 1015 // hardware encryption 1016 1017 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1018 1019 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) 1020 && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1021 1022 if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 1023 return true; 1024 } 1025 } 1026 } 1027 1028 return false; 1029 } 1030 1031 public void writeKnownNetworkHistory() { 1032 if (VDBG) { 1033 loge(" writeKnownNetworkHistory() num networks:" + 1034 Integer.toString(mConfiguredNetworks.size()), true); 1035 } 1036 1037 /* Make a copy */ 1038 final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 1039 for (WifiConfiguration config : mConfiguredNetworks.values()) { 1040 networks.add(new WifiConfiguration(config)); 1041 } 1042 1043 mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() { 1044 public void onWriteCalled(DataOutputStream out) throws IOException { 1045 for (WifiConfiguration config : networks) { 1046 //loge("onWriteCalled write SSID: " + config.SSID); 1047 /* if (config.getLinkProperties() != null) 1048 loge(" lp " + config.getLinkProperties().toString()); 1049 else 1050 loge("attempt config w/o lp"); 1051 */ 1052 1053 if (VDBG) { 1054 int num = 0; 1055 if (config.connectChoices != null) { 1056 num = config.connectChoices.size(); 1057 } 1058 loge("saving network history: " + config.configKey() + " gw: " + 1059 config.defaultGwMacAddress + " autojoin status: " + 1060 config.autoJoinStatus + " ephemeral=" + config.ephemeral 1061 + " choices:" + Integer.toString(num)); 1062 } 1063 if (config.ephemeral == true) 1064 continue; 1065 1066 if (config.isValid() == false) 1067 continue; 1068 1069 if (config.SSID == null) { 1070 if (VDBG) { 1071 loge("writeKnownNetworkHistory trying to write config with null SSID"); 1072 } 1073 continue; 1074 } 1075 1076 out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY); 1077 1078 out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY); 1079 1080 out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY); 1081 out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus) 1082 + SEPARATOR_KEY); 1083 out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId) 1084 + SEPARATOR_KEY); 1085 out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded) 1086 + SEPARATOR_KEY); 1087 1088 String allowedKeyManagementString = 1089 makeString(config.allowedKeyManagement, 1090 WifiConfiguration.KeyMgmt.strings); 1091 out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY); 1092 1093 if (config.connectChoices != null) { 1094 for (String key : config.connectChoices.keySet()) { 1095 out.writeUTF(CHOICE_KEY + key + SEPARATOR_KEY); 1096 } 1097 } 1098 if (config.linkedConfigurations != null) { 1099 for (String key : config.linkedConfigurations.keySet()) { 1100 out.writeUTF(LINK_KEY + key + SEPARATOR_KEY); 1101 } 1102 } 1103 1104 if (config.getLinkProperties() != null) { 1105 String macAddress = config.defaultGwMacAddress; 1106 if (macAddress != null) { 1107 out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY); 1108 } 1109 } 1110 1111 if (config.scanResultCache != null) { 1112 for (ScanResult result : config.scanResultCache.values()) { 1113 out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY); 1114 1115 out.writeUTF(FREQ_KEY + Integer.toString(result.frequency) 1116 + SEPARATOR_KEY); 1117 1118 out.writeUTF(RSSI_KEY + Integer.toString(result.level) 1119 + SEPARATOR_KEY); 1120 1121 if (result.seen != 0) { 1122 out.writeUTF(MILLI_KEY + Long.toString(result.seen) 1123 + SEPARATOR_KEY); 1124 } 1125 out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY); 1126 } 1127 } 1128 if (config.lastFailure != null) { 1129 out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY); 1130 } 1131 out.writeUTF(SEPARATOR_KEY); 1132 } 1133 } 1134 1135 }); 1136 } 1137 1138 public void setLastSelectedConfiguration(int netId) { 1139 if (DBG) { 1140 loge("setLastSelectedConfiguration " + Integer.toString(netId)); 1141 } 1142 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 1143 lastSelectedConfiguration = null; 1144 } else { 1145 WifiConfiguration selected = getWifiConfiguration(netId); 1146 if (selected == null) { 1147 lastSelectedConfiguration = null; 1148 } else { 1149 lastSelectedConfiguration = selected.configKey(); 1150 if (VDBG) { 1151 loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration); 1152 } 1153 } 1154 } 1155 } 1156 1157 public String getLastSelectedConfiguration() { 1158 return lastSelectedConfiguration; 1159 } 1160 1161 private void readNetworkHistory() { 1162 if (VDBG) { 1163 loge("readNetworkHistory path:" + networkHistoryConfigFile, true); 1164 } 1165 DataInputStream in = null; 1166 try { 1167 in = new DataInputStream(new BufferedInputStream(new FileInputStream( 1168 networkHistoryConfigFile))); 1169 WifiConfiguration config = null; 1170 while (true) { 1171 int id = -1; 1172 String key = in.readUTF(); 1173 String bssid = null; 1174 String ssid = null; 1175 1176 int freq = 0; 1177 long seen = 0; 1178 int rssi = WifiConfiguration.INVALID_RSSI; 1179 String caps = null; 1180 if (key.startsWith(CONFIG_KEY)) { 1181 1182 if (config != null) { 1183 config = null; 1184 } 1185 String configKey = key.replace(CONFIG_KEY, ""); 1186 configKey = configKey.replace(SEPARATOR_KEY, ""); 1187 // get the networkId for that config Key 1188 Integer n = mNetworkIds.get(configKey.hashCode()); 1189 // skip reading that configuration data 1190 // since we don't have a corresponding network ID 1191 if (n == null) { 1192 loge("readNetworkHistory didnt find netid for hash=" 1193 + Integer.toString(configKey.hashCode()) 1194 + " key: " + configKey); 1195 continue; 1196 } 1197 config = mConfiguredNetworks.get(n); 1198 if (config == null) { 1199 loge("readNetworkHistory didnt find config for netid=" 1200 + n.toString() 1201 + " key: " + configKey); 1202 } 1203 ssid = null; 1204 bssid = null; 1205 freq = 0; 1206 seen = 0; 1207 rssi = WifiConfiguration.INVALID_RSSI; 1208 caps = null; 1209 1210 } else if (config != null) { 1211 if (key.startsWith(SSID_KEY)) { 1212 ssid = key.replace(SSID_KEY, ""); 1213 ssid = ssid.replace(SEPARATOR_KEY, ""); 1214 if (config.SSID != null && !config.SSID.equals(ssid)) { 1215 loge("Error parsing network history file, mismatched SSIDs"); 1216 config = null; //error 1217 ssid = null; 1218 } else { 1219 config.SSID = ssid; 1220 } 1221 } 1222 1223 if (key.startsWith(DEFAULT_GW_KEY)) { 1224 String gateway = key.replace(DEFAULT_GW_KEY, ""); 1225 gateway = gateway.replace(SEPARATOR_KEY, ""); 1226 config.defaultGwMacAddress = gateway; 1227 } 1228 1229 if (key.startsWith(STATUS_KEY)) { 1230 String status = key.replace(STATUS_KEY, ""); 1231 status = status.replace(SEPARATOR_KEY, ""); 1232 config.autoJoinStatus = Integer.parseInt(status); 1233 } 1234 1235 if (key.startsWith(SELF_ADDED_KEY)) { 1236 String status = key.replace(STATUS_KEY, ""); 1237 status = status.replace(SEPARATOR_KEY, ""); 1238 config.selfAdded = Boolean.parseBoolean(status); 1239 } 1240 1241 if (key.startsWith(FAILURE_KEY)) { 1242 config.lastFailure = key.replace(FAILURE_KEY, ""); 1243 config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, ""); 1244 } 1245 1246 if (key.startsWith(CHOICE_KEY)) { 1247 String configKey = key.replace(CHOICE_KEY, ""); 1248 configKey = configKey.replace(SEPARATOR_KEY, ""); 1249 if (config.connectChoices == null) { 1250 config.connectChoices = new HashMap<String, Integer>(); 1251 } 1252 config.connectChoices.put(configKey, -1); 1253 } 1254 1255 if (key.startsWith(LINK_KEY)) { 1256 String configKey = key.replace(LINK_KEY, ""); 1257 configKey = configKey.replace(SEPARATOR_KEY, ""); 1258 if (config.linkedConfigurations == null) { 1259 config.linkedConfigurations = new HashMap<String, Integer>(); 1260 } 1261 if (config.linkedConfigurations != null) { 1262 config.linkedConfigurations.put(configKey, -1); 1263 } 1264 } 1265 1266 if (key.startsWith(BSSID_KEY)) { 1267 if (key.startsWith(BSSID_KEY)) { 1268 bssid = key.replace(BSSID_KEY, ""); 1269 bssid = bssid.replace(SEPARATOR_KEY, ""); 1270 freq = 0; 1271 seen = 0; 1272 rssi = WifiConfiguration.INVALID_RSSI; 1273 caps = ""; 1274 } 1275 1276 if (key.startsWith(RSSI_KEY)) { 1277 String lvl = key.replace(RSSI_KEY, ""); 1278 lvl = lvl.replace(SEPARATOR_KEY, ""); 1279 rssi = Integer.parseInt(lvl); 1280 } 1281 1282 if (key.startsWith(FREQ_KEY)) { 1283 String channel = key.replace(FREQ_KEY, ""); 1284 channel = channel.replace(SEPARATOR_KEY, ""); 1285 freq = Integer.parseInt(channel); 1286 } 1287 1288 if (key.startsWith(DATE_KEY)) { 1289 /* 1290 * when reading the configuration from file we don't update the date 1291 * so as to avoid reading back stale or non-sensical data that would 1292 * depend on network time. 1293 * The date of a WifiConfiguration should only come from actual scan result. 1294 * 1295 String s = key.replace(FREQ_KEY, ""); 1296 seen = Integer.getInteger(s); 1297 */ 1298 } 1299 1300 if (key.startsWith(BSSID_KEY_END)) { 1301 1302 if ((bssid != null) && (ssid != null)) { 1303 1304 if (config.scanResultCache == null) { 1305 config.scanResultCache = new HashMap<String, ScanResult>(); 1306 } 1307 WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid); 1308 ScanResult result = new ScanResult(wssid, bssid, 1309 caps, rssi, freq, (long) 0); 1310 result.seen = seen; 1311 config.scanResultCache.put(bssid, result); 1312 } 1313 } 1314 } 1315 } 1316 } 1317 } catch (EOFException ignore) { 1318 if (in != null) { 1319 try { 1320 in.close(); 1321 } catch (Exception e) { 1322 loge("readNetworkHistory: Error reading file" + e); 1323 } 1324 } 1325 } catch (IOException e) { 1326 loge("readNetworkHistory: Error parsing configuration" + e); 1327 } 1328 1329 if(in!=null) { 1330 try { 1331 in.close(); 1332 } catch (Exception e) { 1333 loge("readNetworkHistory: Error closing file" + e); 1334 } 1335 } 1336 } 1337 1338 private void writeIpAndProxyConfigurations() { 1339 final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 1340 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1341 if (!config.ephemeral) { 1342 networks.put(configKey(config), config.getIpConfiguration()); 1343 } 1344 } 1345 1346 super.writeIpAndProxyConfigurations(ipConfigFile, networks); 1347 } 1348 1349 private void readIpAndProxyConfigurations() { 1350 SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile); 1351 1352 if (networks.size() == 0) { 1353 // IpConfigStore.readIpAndProxyConfigurations has already logged an error. 1354 return; 1355 } 1356 1357 for (int i = 0; i < networks.size(); i++) { 1358 Integer id = (Integer) i; 1359 WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id)); 1360 1361 if (config == null) { 1362 loge("configuration found for missing network, nid="+Integer.toString(id) 1363 +", ignored, networks.size=" + Integer.toString(networks.size())); 1364 } else { 1365 config.setIpConfiguration(networks.valueAt(i)); 1366 } 1367 } 1368 } 1369 1370 /* 1371 * Convert string to Hexadecimal before passing to wifi native layer 1372 * In native function "doCommand()" have trouble in converting Unicode character string to UTF8 1373 * conversion to hex is required because SSIDs can have space characters in them; 1374 * and that can confuses the supplicant because it uses space charaters as delimiters 1375 */ 1376 1377 private String encodeSSID(String str){ 1378 String tmp = removeDoubleQuotes(str); 1379 return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8")))); 1380 } 1381 1382 private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) { 1383 /* 1384 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1385 * network configuration. Otherwise, the networkId should 1386 * refer to an existing configuration. 1387 */ 1388 1389 if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); 1390 1391 int netId = config.networkId; 1392 boolean newNetwork = false; 1393 // networkId of INVALID_NETWORK_ID means we want to create a new network 1394 if (netId == INVALID_NETWORK_ID) { 1395 Integer savedNetId = mNetworkIds.get(configKey(config)); 1396 if (savedNetId != null) { 1397 netId = savedNetId; 1398 } else { 1399 newNetwork = true; 1400 netId = mWifiNative.addNetwork(); 1401 if (netId < 0) { 1402 loge("Failed to add a network!"); 1403 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1404 } else { 1405 loge("addOrUpdateNetworkNative created netId=" + netId); 1406 } 1407 } 1408 } 1409 1410 boolean updateFailed = true; 1411 1412 setVariables: { 1413 1414 if (config.SSID != null && 1415 !mWifiNative.setNetworkVariable( 1416 netId, 1417 WifiConfiguration.ssidVarName, 1418 encodeSSID(config.SSID))) { 1419 loge("failed to set SSID: "+config.SSID); 1420 break setVariables; 1421 } 1422 1423 if (config.BSSID != null && 1424 !mWifiNative.setNetworkVariable( 1425 netId, 1426 WifiConfiguration.bssidVarName, 1427 config.BSSID)) { 1428 loge("failed to set BSSID: "+config.BSSID); 1429 break setVariables; 1430 } 1431 1432 String allowedKeyManagementString = 1433 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 1434 if (config.allowedKeyManagement.cardinality() != 0 && 1435 !mWifiNative.setNetworkVariable( 1436 netId, 1437 WifiConfiguration.KeyMgmt.varName, 1438 allowedKeyManagementString)) { 1439 loge("failed to set key_mgmt: "+ 1440 allowedKeyManagementString); 1441 break setVariables; 1442 } 1443 1444 String allowedProtocolsString = 1445 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 1446 if (config.allowedProtocols.cardinality() != 0 && 1447 !mWifiNative.setNetworkVariable( 1448 netId, 1449 WifiConfiguration.Protocol.varName, 1450 allowedProtocolsString)) { 1451 loge("failed to set proto: "+ 1452 allowedProtocolsString); 1453 break setVariables; 1454 } 1455 1456 String allowedAuthAlgorithmsString = 1457 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 1458 if (config.allowedAuthAlgorithms.cardinality() != 0 && 1459 !mWifiNative.setNetworkVariable( 1460 netId, 1461 WifiConfiguration.AuthAlgorithm.varName, 1462 allowedAuthAlgorithmsString)) { 1463 loge("failed to set auth_alg: "+ 1464 allowedAuthAlgorithmsString); 1465 break setVariables; 1466 } 1467 1468 String allowedPairwiseCiphersString = 1469 makeString(config.allowedPairwiseCiphers, 1470 WifiConfiguration.PairwiseCipher.strings); 1471 if (config.allowedPairwiseCiphers.cardinality() != 0 && 1472 !mWifiNative.setNetworkVariable( 1473 netId, 1474 WifiConfiguration.PairwiseCipher.varName, 1475 allowedPairwiseCiphersString)) { 1476 loge("failed to set pairwise: "+ 1477 allowedPairwiseCiphersString); 1478 break setVariables; 1479 } 1480 1481 String allowedGroupCiphersString = 1482 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 1483 if (config.allowedGroupCiphers.cardinality() != 0 && 1484 !mWifiNative.setNetworkVariable( 1485 netId, 1486 WifiConfiguration.GroupCipher.varName, 1487 allowedGroupCiphersString)) { 1488 loge("failed to set group: "+ 1489 allowedGroupCiphersString); 1490 break setVariables; 1491 } 1492 1493 // Prevent client screw-up by passing in a WifiConfiguration we gave it 1494 // by preventing "*" as a key. 1495 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 1496 !mWifiNative.setNetworkVariable( 1497 netId, 1498 WifiConfiguration.pskVarName, 1499 config.preSharedKey)) { 1500 loge("failed to set psk"); 1501 break setVariables; 1502 } 1503 1504 boolean hasSetKey = false; 1505 if (config.wepKeys != null) { 1506 for (int i = 0; i < config.wepKeys.length; i++) { 1507 // Prevent client screw-up by passing in a WifiConfiguration we gave it 1508 // by preventing "*" as a key. 1509 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 1510 if (!mWifiNative.setNetworkVariable( 1511 netId, 1512 WifiConfiguration.wepKeyVarNames[i], 1513 config.wepKeys[i])) { 1514 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 1515 break setVariables; 1516 } 1517 hasSetKey = true; 1518 } 1519 } 1520 } 1521 1522 if (hasSetKey) { 1523 if (!mWifiNative.setNetworkVariable( 1524 netId, 1525 WifiConfiguration.wepTxKeyIdxVarName, 1526 Integer.toString(config.wepTxKeyIndex))) { 1527 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 1528 break setVariables; 1529 } 1530 } 1531 1532 if (!mWifiNative.setNetworkVariable( 1533 netId, 1534 WifiConfiguration.priorityVarName, 1535 Integer.toString(config.priority))) { 1536 loge(config.SSID + ": failed to set priority: " 1537 +config.priority); 1538 break setVariables; 1539 } 1540 1541 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 1542 netId, 1543 WifiConfiguration.hiddenSSIDVarName, 1544 Integer.toString(config.hiddenSSID ? 1 : 0))) { 1545 loge(config.SSID + ": failed to set hiddenSSID: "+ 1546 config.hiddenSSID); 1547 break setVariables; 1548 } 1549 1550 if (config.enterpriseConfig != null && 1551 config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 1552 1553 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 1554 1555 if (needsKeyStore(enterpriseConfig)) { 1556 /** 1557 * Keyguard settings may eventually be controlled by device policy. 1558 * We check here if keystore is unlocked before installing 1559 * credentials. 1560 * TODO: Do we need a dialog here ? 1561 */ 1562 if (mKeyStore.state() != KeyStore.State.UNLOCKED) { 1563 loge(config.SSID + ": key store is locked"); 1564 break setVariables; 1565 } 1566 1567 try { 1568 /* config passed may include only fields being updated. 1569 * In order to generate the key id, fetch uninitialized 1570 * fields from the currently tracked configuration 1571 */ 1572 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1573 String keyId = config.getKeyIdForCredentials(currentConfig); 1574 1575 if (!installKeys(enterpriseConfig, keyId)) { 1576 loge(config.SSID + ": failed to install keys"); 1577 break setVariables; 1578 } 1579 } catch (IllegalStateException e) { 1580 loge(config.SSID + " invalid config for key installation"); 1581 break setVariables; 1582 } 1583 } 1584 1585 HashMap<String, String> enterpriseFields = enterpriseConfig.getFields(); 1586 for (String key : enterpriseFields.keySet()) { 1587 String value = enterpriseFields.get(key); 1588 if (!mWifiNative.setNetworkVariable( 1589 netId, 1590 key, 1591 value)) { 1592 removeKeys(enterpriseConfig); 1593 loge(config.SSID + ": failed to set " + key + 1594 ": " + value); 1595 break setVariables; 1596 } 1597 } 1598 } 1599 updateFailed = false; 1600 } //end of setVariables 1601 1602 if (updateFailed) { 1603 if (newNetwork) { 1604 mWifiNative.removeNetwork(netId); 1605 loge("Failed to set a network variable, removed network: " + netId); 1606 } 1607 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1608 } 1609 1610 /* An update of the network variables requires reading them 1611 * back from the supplicant to update mConfiguredNetworks. 1612 * This is because some of the variables (SSID, wep keys & 1613 * passphrases) reflect different values when read back than 1614 * when written. For example, wep key is stored as * irrespective 1615 * of the value sent to the supplicant 1616 */ 1617 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 1618 if (currentConfig == null) { 1619 currentConfig = new WifiConfiguration(); 1620 currentConfig.setIpAssignment(IpAssignment.DHCP); 1621 currentConfig.setProxySettings(ProxySettings.NONE); 1622 currentConfig.networkId = netId; 1623 if (DBG) loge("created new config netId=" + Integer.toString(netId)); 1624 } 1625 1626 if (DBG) loge("will read network variables netId=" + Integer.toString(netId)); 1627 1628 readNetworkVariables(currentConfig); 1629 1630 mConfiguredNetworks.put(netId, currentConfig); 1631 mNetworkIds.put(configKey(currentConfig), netId); 1632 1633 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config); 1634 result.setIsNewNetwork(newNetwork); 1635 result.setNetworkId(netId); 1636 return result; 1637 } 1638 1639 1640 public void linkConfiguration(WifiConfiguration config) { 1641 for (WifiConfiguration link : mConfiguredNetworks.values()) { 1642 boolean doLink = false; 1643 1644 if (link.configKey().equals(config.configKey())) { 1645 continue; 1646 } 1647 1648 //autojoin will be allowed to dynamically jump from a linked configuration 1649 //to another, hence only link configurations that have equivalent level of security 1650 if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) { 1651 continue; 1652 } 1653 1654 if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) { 1655 //if both default GW are known, compare based on RSSI only if the GW is equal 1656 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) { 1657 1658 if (VDBG) { 1659 loge("linkConfiguration link due to same gw" + link.SSID + 1660 " and " + config.SSID + " GW " + config.defaultGwMacAddress); 1661 } 1662 doLink = true; 1663 } 1664 } else { 1665 // we do not know BOTH default gateways hence we will try to link 1666 // hoping that WifiConfigurations are indeed behind the same gateway 1667 // once both WifiConfiguration will have been tried we will know 1668 // the default gateway and revisit the choice of linking them 1669 if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 5) 1670 && (link.scanResultCache != null) && (link.scanResultCache.size() <= 5)) { 1671 String abssid = ""; 1672 String bbssid = ""; 1673 for (String key : config.scanResultCache.keySet()) { 1674 abssid = key; 1675 } 1676 for (String key : link.scanResultCache.keySet()) { 1677 bbssid = key; 1678 } 1679 if (VDBG) { 1680 loge("linkConfiguration link due to DBDC BSSID match " + link.SSID + 1681 " and " + config.SSID + " bssida " + abssid + " bssidb " + bbssid); 1682 } 1683 if (abssid.regionMatches(true, 0, bbssid, 0, 16)) { 1684 //if first 16 ascii characters of BSSID matches, we assume this is a DBDC 1685 doLink = true; 1686 } 1687 } 1688 } 1689 1690 if (doLink) { 1691 if (VDBG) { 1692 loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID); 1693 } 1694 if (link.linkedConfigurations == null) { 1695 link.linkedConfigurations = new HashMap<String, Integer>(); 1696 } 1697 if (config.linkedConfigurations == null) { 1698 config.linkedConfigurations = new HashMap<String, Integer>(); 1699 } 1700 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 1701 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 1702 } else { 1703 //todo if they are linked, break the link 1704 } 1705 } 1706 } 1707 1708 /* 1709 * We try to link a scan result with a WifiConfiguration for which SSID and ket management dont match, 1710 * for instance, we try identify the 5GHz SSID of a DBDC AP, even though we know only of the 2.4GHz 1711 * 1712 * Obviously, this function is not optimal since it is used to compare every scan 1713 * result with every Saved WifiConfiguration, with a string.equals operation. 1714 * As a speed up, might be better to implement the mConfiguredNetworks store as a 1715 * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object 1716 * so as to speed this up. Also to prevent the tiny probability of hash collision. 1717 * 1718 */ 1719 public WifiConfiguration associateWithConfiguration(ScanResult result) { 1720 String configKey = WifiConfiguration.configKey(result); 1721 if (configKey == null) { 1722 if (DBG) loge("associateWithConfiguration(): no config key " ); 1723 return null; 1724 } 1725 1726 //need to compare with quoted string 1727 String SSID = "\"" + result.SSID + "\""; 1728 1729 WifiConfiguration config = null; 1730 for (WifiConfiguration link : mConfiguredNetworks.values()) { 1731 boolean doLink = false; 1732 1733 if (configKey.equals(link.configKey())) { 1734 if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey ); 1735 return link; //found it exactly 1736 } 1737 1738 if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) { 1739 String bssid = ""; 1740 for (String key : link.scanResultCache.keySet()) { 1741 bssid = key; 1742 } 1743 1744 if (result.BSSID.regionMatches(true, 0, bssid, 0, 16) 1745 && SSID.regionMatches(false, 0, link.SSID, 0, 3)) { 1746 // if first 16 ascii characters of BSSID matches, and first 3 1747 // characters of SSID match, we assume this is a home setup 1748 // and thus we will try to transfer the password from the known 1749 // BSSID/SSID to the recently found BSSID/SSID 1750 1751 //if (VDBG) 1752 // loge("associateWithConfiguration OK " ); 1753 doLink = true; 1754 } 1755 } 1756 1757 if (doLink) { 1758 //try to make a non verified WifiConfiguration 1759 if (VDBG) { 1760 loge("associateWithConfiguration: will create " + 1761 result.SSID + " and associate it with: " + link.SSID); 1762 } 1763 config = wifiConfigurationFromScanResult(result); 1764 config.selfAdded = true; 1765 if (config != null) { 1766 if (config.allowedKeyManagement.equals(link.allowedKeyManagement) && 1767 config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 1768 1769 //transfer the credentials 1770 1771 String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk"); 1772 if (psk != null) { 1773 1774 1775 config.preSharedKey = psk; 1776 if (VDBG) { 1777 if (config.preSharedKey != null) 1778 loge(" transfer PSK : " + config.preSharedKey); 1779 } 1780 1781 //link configurations 1782 if (link.linkedConfigurations == null) { 1783 link.linkedConfigurations = new HashMap<String, Integer>(); 1784 } 1785 if (config.linkedConfigurations == null) { 1786 config.linkedConfigurations = new HashMap<String, Integer>(); 1787 } 1788 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 1789 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 1790 } else { 1791 config = null; 1792 } 1793 } 1794 } 1795 } else { 1796 //todo if they are linked, break the link 1797 } 1798 } 1799 return config; 1800 } 1801 1802 1803 public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) { 1804 WifiConfiguration found = null; 1805 if (scanResult == null) 1806 return found; 1807 1808 //first step, look for this scan Result by SSID + Key Management 1809 String key = WifiConfiguration.configKey(scanResult); 1810 int hash = key.hashCode(); 1811 1812 Integer netId = mNetworkIds.get(hash); 1813 if (netId == null) return null; 1814 WifiConfiguration config = mConfiguredNetworks.get(netId); 1815 if (config != null) { 1816 if (config.scanResultCache == null) { 1817 config.scanResultCache = new HashMap<String, ScanResult>(); 1818 } 1819 if (config.scanResultCache == null) { 1820 return null; 1821 } 1822 //add the scan result to this WifiConfiguration 1823 config.scanResultCache.put(scanResult.BSSID, scanResult); 1824 mConfiguredNetworks.put(netId, config); 1825 linkConfiguration(config); 1826 found = config; 1827 } 1828 1829 if (VDBG) { 1830 config = mConfiguredNetworks.get(netId); 1831 if (config != null) { 1832 if (config.scanResultCache != null) { 1833 loge(" tested " + scanResult.SSID + " " + 1834 scanResult.BSSID + " key : " + key + " num: " + 1835 Integer.toString(config.scanResultCache.size())); 1836 } else { 1837 loge(" tested " + scanResult.SSID + " " + 1838 scanResult.BSSID + " key : " + key); 1839 } 1840 } 1841 } 1842 return found; 1843 1844 } 1845 1846 /* Compare current and new configuration and write to file on change */ 1847 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 1848 WifiConfiguration currentConfig, 1849 WifiConfiguration newConfig) { 1850 boolean ipChanged = false; 1851 boolean proxyChanged = false; 1852 LinkProperties linkProperties = null; 1853 1854 if (VDBG) { 1855 loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " + 1856 newConfig.SSID + " path: " + ipConfigFile); 1857 } 1858 1859 1860 switch (newConfig.getIpAssignment()) { 1861 case STATIC: 1862 Collection<LinkAddress> currentLinkAddresses = currentConfig.getLinkProperties() 1863 .getLinkAddresses(); 1864 Collection<LinkAddress> newLinkAddresses = newConfig.getLinkProperties() 1865 .getLinkAddresses(); 1866 Collection<InetAddress> currentDnses = currentConfig.getLinkProperties().getDnses(); 1867 Collection<InetAddress> newDnses = newConfig.getLinkProperties().getDnses(); 1868 Collection<RouteInfo> currentRoutes = currentConfig.getLinkProperties().getRoutes(); 1869 Collection<RouteInfo> newRoutes = newConfig.getLinkProperties().getRoutes(); 1870 1871 boolean linkAddressesDiffer = 1872 (currentLinkAddresses.size() != newLinkAddresses.size()) || 1873 !currentLinkAddresses.containsAll(newLinkAddresses); 1874 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || 1875 !currentDnses.containsAll(newDnses); 1876 boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) || 1877 !currentRoutes.containsAll(newRoutes); 1878 1879 if ((currentConfig.getIpAssignment() != newConfig.getIpAssignment()) || 1880 linkAddressesDiffer || 1881 dnsesDiffer || 1882 routesDiffer) { 1883 ipChanged = true; 1884 } 1885 break; 1886 case DHCP: 1887 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 1888 ipChanged = true; 1889 } 1890 break; 1891 case UNASSIGNED: 1892 /* Ignore */ 1893 break; 1894 default: 1895 loge("Ignore invalid ip assignment during write"); 1896 break; 1897 } 1898 1899 switch (newConfig.getProxySettings()) { 1900 case STATIC: 1901 case PAC: 1902 ProxyInfo newHttpProxy = newConfig.getLinkProperties().getHttpProxy(); 1903 ProxyInfo currentHttpProxy = currentConfig.getLinkProperties().getHttpProxy(); 1904 1905 if (newHttpProxy != null) { 1906 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 1907 } else { 1908 proxyChanged = (currentHttpProxy != null); 1909 } 1910 break; 1911 case NONE: 1912 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) { 1913 proxyChanged = true; 1914 } 1915 break; 1916 case UNASSIGNED: 1917 /* Ignore */ 1918 break; 1919 default: 1920 loge("Ignore invalid proxy configuration during write"); 1921 break; 1922 } 1923 1924 if (!ipChanged) { 1925 linkProperties = copyIpSettingsFromConfig(currentConfig); 1926 } else { 1927 currentConfig.setIpAssignment(newConfig.getIpAssignment()); 1928 linkProperties = copyIpSettingsFromConfig(newConfig); 1929 log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " + 1930 linkProperties.toString()); 1931 } 1932 1933 1934 if (!proxyChanged) { 1935 linkProperties.setHttpProxy(currentConfig.getLinkProperties().getHttpProxy()); 1936 } else { 1937 currentConfig.setProxySettings(newConfig.getProxySettings()); 1938 linkProperties.setHttpProxy(newConfig.getLinkProperties().getHttpProxy()); 1939 log("proxy changed SSID = " + currentConfig.SSID); 1940 if (linkProperties.getHttpProxy() != null) { 1941 log(" proxyProperties: " + linkProperties.getHttpProxy().toString()); 1942 } 1943 } 1944 1945 if (ipChanged || proxyChanged) { 1946 currentConfig.setLinkProperties(linkProperties); 1947 writeIpAndProxyConfigurations(); 1948 sendConfiguredNetworksChangedBroadcast(currentConfig, 1949 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 1950 } 1951 return new NetworkUpdateResult(ipChanged, proxyChanged); 1952 } 1953 1954 private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) { 1955 LinkProperties linkProperties = new LinkProperties(); 1956 linkProperties.setInterfaceName(config.getLinkProperties().getInterfaceName()); 1957 for (LinkAddress linkAddr : config.getLinkProperties().getLinkAddresses()) { 1958 linkProperties.addLinkAddress(linkAddr); 1959 } 1960 for (RouteInfo route : config.getLinkProperties().getRoutes()) { 1961 linkProperties.addRoute(route); 1962 } 1963 for (InetAddress dns : config.getLinkProperties().getDnses()) { 1964 linkProperties.addDns(dns); 1965 } 1966 return linkProperties; 1967 } 1968 1969 /** 1970 * Read the variables from the supplicant daemon that are needed to 1971 * fill in the WifiConfiguration object. 1972 * 1973 * @param config the {@link WifiConfiguration} object to be filled in. 1974 */ 1975 private void readNetworkVariables(WifiConfiguration config) { 1976 1977 int netId = config.networkId; 1978 if (netId < 0) 1979 return; 1980 1981 /* 1982 * TODO: maybe should have a native method that takes an array of 1983 * variable names and returns an array of values. But we'd still 1984 * be doing a round trip to the supplicant daemon for each variable. 1985 */ 1986 String value; 1987 1988 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 1989 if (!TextUtils.isEmpty(value)) { 1990 if (value.charAt(0) != '"') { 1991 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 1992 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 1993 //supplicant string 1994 } else { 1995 config.SSID = value; 1996 } 1997 } else { 1998 config.SSID = null; 1999 } 2000 2001 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 2002 if (!TextUtils.isEmpty(value)) { 2003 config.BSSID = value; 2004 } else { 2005 config.BSSID = null; 2006 } 2007 2008 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 2009 config.priority = -1; 2010 if (!TextUtils.isEmpty(value)) { 2011 try { 2012 config.priority = Integer.parseInt(value); 2013 } catch (NumberFormatException ignore) { 2014 } 2015 } 2016 2017 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 2018 config.hiddenSSID = false; 2019 if (!TextUtils.isEmpty(value)) { 2020 try { 2021 config.hiddenSSID = Integer.parseInt(value) != 0; 2022 } catch (NumberFormatException ignore) { 2023 } 2024 } 2025 2026 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 2027 config.wepTxKeyIndex = -1; 2028 if (!TextUtils.isEmpty(value)) { 2029 try { 2030 config.wepTxKeyIndex = Integer.parseInt(value); 2031 } catch (NumberFormatException ignore) { 2032 } 2033 } 2034 2035 for (int i = 0; i < 4; i++) { 2036 value = mWifiNative.getNetworkVariable(netId, 2037 WifiConfiguration.wepKeyVarNames[i]); 2038 if (!TextUtils.isEmpty(value)) { 2039 config.wepKeys[i] = value; 2040 } else { 2041 config.wepKeys[i] = null; 2042 } 2043 } 2044 2045 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 2046 if (!TextUtils.isEmpty(value)) { 2047 config.preSharedKey = value; 2048 } else { 2049 config.preSharedKey = null; 2050 } 2051 2052 value = mWifiNative.getNetworkVariable(config.networkId, 2053 WifiConfiguration.Protocol.varName); 2054 if (!TextUtils.isEmpty(value)) { 2055 String vals[] = value.split(" "); 2056 for (String val : vals) { 2057 int index = 2058 lookupString(val, WifiConfiguration.Protocol.strings); 2059 if (0 <= index) { 2060 config.allowedProtocols.set(index); 2061 } 2062 } 2063 } 2064 2065 value = mWifiNative.getNetworkVariable(config.networkId, 2066 WifiConfiguration.KeyMgmt.varName); 2067 if (!TextUtils.isEmpty(value)) { 2068 String vals[] = value.split(" "); 2069 for (String val : vals) { 2070 int index = 2071 lookupString(val, WifiConfiguration.KeyMgmt.strings); 2072 if (0 <= index) { 2073 config.allowedKeyManagement.set(index); 2074 } 2075 } 2076 } 2077 2078 value = mWifiNative.getNetworkVariable(config.networkId, 2079 WifiConfiguration.AuthAlgorithm.varName); 2080 if (!TextUtils.isEmpty(value)) { 2081 String vals[] = value.split(" "); 2082 for (String val : vals) { 2083 int index = 2084 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 2085 if (0 <= index) { 2086 config.allowedAuthAlgorithms.set(index); 2087 } 2088 } 2089 } 2090 2091 value = mWifiNative.getNetworkVariable(config.networkId, 2092 WifiConfiguration.PairwiseCipher.varName); 2093 if (!TextUtils.isEmpty(value)) { 2094 String vals[] = value.split(" "); 2095 for (String val : vals) { 2096 int index = 2097 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 2098 if (0 <= index) { 2099 config.allowedPairwiseCiphers.set(index); 2100 } 2101 } 2102 } 2103 2104 value = mWifiNative.getNetworkVariable(config.networkId, 2105 WifiConfiguration.GroupCipher.varName); 2106 if (!TextUtils.isEmpty(value)) { 2107 String vals[] = value.split(" "); 2108 for (String val : vals) { 2109 int index = 2110 lookupString(val, WifiConfiguration.GroupCipher.strings); 2111 if (0 <= index) { 2112 config.allowedGroupCiphers.set(index); 2113 } 2114 } 2115 } 2116 2117 if (config.enterpriseConfig == null) { 2118 config.enterpriseConfig = new WifiEnterpriseConfig(); 2119 } 2120 HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields(); 2121 for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) { 2122 value = mWifiNative.getNetworkVariable(netId, key); 2123 if (!TextUtils.isEmpty(value)) { 2124 enterpriseFields.put(key, removeDoubleQuotes(value)); 2125 } else { 2126 enterpriseFields.put(key, EMPTY_VALUE); 2127 } 2128 } 2129 2130 if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) { 2131 saveConfig(); 2132 } 2133 2134 migrateCerts(config.enterpriseConfig); 2135 // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore); 2136 } 2137 2138 private static String removeDoubleQuotes(String string) { 2139 int length = string.length(); 2140 if ((length > 1) && (string.charAt(0) == '"') 2141 && (string.charAt(length - 1) == '"')) { 2142 return string.substring(1, length - 1); 2143 } 2144 return string; 2145 } 2146 2147 private static String makeString(BitSet set, String[] strings) { 2148 StringBuffer buf = new StringBuffer(); 2149 int nextSetBit = -1; 2150 2151 /* Make sure all set bits are in [0, strings.length) to avoid 2152 * going out of bounds on strings. (Shouldn't happen, but...) */ 2153 set = set.get(0, strings.length); 2154 2155 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 2156 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 2157 } 2158 2159 // remove trailing space 2160 if (set.cardinality() > 0) { 2161 buf.setLength(buf.length() - 1); 2162 } 2163 2164 return buf.toString(); 2165 } 2166 2167 private int lookupString(String string, String[] strings) { 2168 int size = strings.length; 2169 2170 string = string.replace('-', '_'); 2171 2172 for (int i = 0; i < size; i++) 2173 if (string.equals(strings[i])) 2174 return i; 2175 2176 // if we ever get here, we should probably add the 2177 // value to WifiConfiguration to reflect that it's 2178 // supported by the WPA supplicant 2179 loge("Failed to look-up a string: " + string); 2180 2181 return -1; 2182 } 2183 2184 /* return the allowed key management based on a scan result */ 2185 2186 public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 2187 WifiConfiguration config = new WifiConfiguration(); 2188 2189 config.SSID = "\"" + result.SSID + "\""; 2190 2191 if (VDBG) { 2192 loge("WifiConfiguration from scan results " + 2193 config.SSID + " cap " + result.capabilities); 2194 } 2195 if (result.capabilities.contains("WEP")) { 2196 config.allowedKeyManagement.set(KeyMgmt.NONE); 2197 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //? 2198 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 2199 } 2200 2201 if (result.capabilities.contains("PSK")) { 2202 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 2203 } 2204 2205 if (result.capabilities.contains("EAP")) { 2206 //this is probably wrong, as we don't have a way to enter the enterprise config 2207 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 2208 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 2209 } 2210 2211 config.scanResultCache = new HashMap<String, ScanResult>(); 2212 if (config.scanResultCache == null) 2213 return null; 2214 config.scanResultCache.put(result.BSSID, result); 2215 2216 return config; 2217 } 2218 2219 2220 /* Returns a unique for a given configuration */ 2221 private static int configKey(WifiConfiguration config) { 2222 String key = config.configKey(); 2223 return key.hashCode(); 2224 } 2225 2226 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2227 pw.println("WifiConfigStore"); 2228 pw.println("mLastPriority " + mLastPriority); 2229 pw.println("Configured networks"); 2230 for (WifiConfiguration conf : getConfiguredNetworks()) { 2231 pw.println(conf); 2232 } 2233 pw.println(); 2234 2235 if (mLocalLog != null) { 2236 pw.println("WifiConfigStore - Log Begin ----"); 2237 mLocalLog.dump(fd, pw, args); 2238 pw.println("WifiConfigStore - Log End ----"); 2239 } 2240 } 2241 2242 public String getConfigFile() { 2243 return ipConfigFile; 2244 } 2245 2246 protected void loge(String s) { 2247 loge(s, false); 2248 } 2249 2250 protected void loge(String s, boolean stack) { 2251 if (stack) { 2252 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2253 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 2254 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 2255 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 2256 } else { 2257 Log.e(TAG, s); 2258 } 2259 } 2260 2261 protected void log(String s) { 2262 Log.d(TAG, s); 2263 } 2264 2265 private void localLog(String s) { 2266 if (mLocalLog != null) { 2267 mLocalLog.log(s); 2268 } 2269 } 2270 2271 private void localLog(String s, int netId) { 2272 if (mLocalLog == null) { 2273 return; 2274 } 2275 2276 WifiConfiguration config; 2277 synchronized(mConfiguredNetworks) { 2278 config = mConfiguredNetworks.get(netId); 2279 } 2280 2281 if (config != null) { 2282 mLocalLog.log(s + " " + config.getPrintableSsid()); 2283 } else { 2284 mLocalLog.log(s + " " + netId); 2285 } 2286 } 2287 2288 // Certificate and private key management for EnterpriseConfig 2289 static boolean needsKeyStore(WifiEnterpriseConfig config) { 2290 // Has no keys to be installed 2291 if (config.getClientCertificate() == null && config.getCaCertificate() == null) 2292 return false; 2293 return true; 2294 } 2295 2296 static boolean isHardwareBackedKey(PrivateKey key) { 2297 return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); 2298 } 2299 2300 static boolean hasHardwareBackedKey(Certificate certificate) { 2301 return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); 2302 } 2303 2304 static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 2305 String client = config.getClientCertificateAlias(); 2306 if (!TextUtils.isEmpty(client)) { 2307 // a valid client certificate is configured 2308 2309 // BUGBUG: keyStore.get() never returns certBytes; because it is not 2310 // taking WIFI_UID as a parameter. It always looks for certificate 2311 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 2312 // all certificates need software keystore until we get the get() API 2313 // fixed. 2314 2315 return true; 2316 } 2317 2318 /* 2319 try { 2320 2321 if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials 2322 .USER_CERTIFICATE + client); 2323 2324 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 2325 if (factory == null) { 2326 Slog.e(TAG, "Error getting certificate factory"); 2327 return; 2328 } 2329 2330 byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); 2331 if (certBytes != null) { 2332 Certificate cert = (X509Certificate) factory.generateCertificate( 2333 new ByteArrayInputStream(certBytes)); 2334 2335 if (cert != null) { 2336 mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); 2337 2338 if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials 2339 .USER_CERTIFICATE + client); 2340 if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : 2341 "does not need" ) + " software key store"); 2342 } else { 2343 Slog.d(TAG, "could not generate certificate"); 2344 } 2345 } else { 2346 Slog.e(TAG, "Could not load client certificate " + Credentials 2347 .USER_CERTIFICATE + client); 2348 mNeedsSoftwareKeystore = true; 2349 } 2350 2351 } catch(CertificateException e) { 2352 Slog.e(TAG, "Could not read certificates"); 2353 mCaCert = null; 2354 mClientCertificate = null; 2355 } 2356 */ 2357 2358 return false; 2359 } 2360 2361 void handleSSIDStateChange(int netId, boolean enabled, String message) { 2362 WifiConfiguration config = mConfiguredNetworks.get(netId); 2363 if (config != null) { 2364 if (enabled) { 2365 loge("SSID re-enabled for " + config.configKey() + 2366 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 2367 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 2368 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 2369 config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED; 2370 } 2371 } else { 2372 loge("SSID temp disabled for " + config.configKey() + 2373 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 2374 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 2375 if (message != null) { 2376 loge(" wpa_supplicant message=" + message); 2377 } 2378 if (config.selfAdded) { 2379 //this is a network we self added, so as auto-join can opportunistically try it 2380 //the user did not create this network and entered its credentials, so we want 2381 //to be very aggressive in disabling it completely. 2382 disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE); 2383 config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE; 2384 } else { 2385 if (message != null) { 2386 if (message.contains("WRONG_KEY") 2387 || message.contains("AUTH_FAILED")) { 2388 //This configuration has received an auth failure, so disable it 2389 //because we don't want auto-join to try it out. 2390 //this network may be re-enabled by the "usual" 2391 // enableAllNetwork function 2392 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED) { 2393 config.autoJoinStatus 2394 = WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED; 2395 } 2396 } 2397 message.replace("\n", ""); 2398 message.replace("\r", ""); 2399 config.lastFailure = message; 2400 } 2401 } 2402 } 2403 } 2404 } 2405 2406 boolean installKeys(WifiEnterpriseConfig config, String name) { 2407 boolean ret = true; 2408 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 2409 String userCertName = Credentials.USER_CERTIFICATE + name; 2410 String caCertName = Credentials.CA_CERTIFICATE + name; 2411 if (config.getClientCertificate() != null) { 2412 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 2413 if (isHardwareBackedKey(config.getClientPrivateKey())) { 2414 // Hardware backed key store is secure enough to store keys un-encrypted, this 2415 // removes the need for user to punch a PIN to get access to these keys 2416 if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store"); 2417 ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID, 2418 KeyStore.FLAG_NONE); 2419 } else { 2420 // Software backed key store is NOT secure enough to store keys un-encrypted. 2421 // Save keys encrypted so they are protected with user's PIN. User will 2422 // have to unlock phone before being able to use these keys and connect to 2423 // networks. 2424 if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store"); 2425 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 2426 KeyStore.FLAG_ENCRYPTED); 2427 } 2428 if (ret == false) { 2429 return ret; 2430 } 2431 2432 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 2433 if (ret == false) { 2434 // Remove private key installed 2435 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 2436 return ret; 2437 } 2438 } 2439 2440 if (config.getCaCertificate() != null) { 2441 ret = putCertInKeyStore(caCertName, config.getCaCertificate()); 2442 if (ret == false) { 2443 if (config.getClientCertificate() != null) { 2444 // Remove client key+cert 2445 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 2446 mKeyStore.delete(userCertName, Process.WIFI_UID); 2447 } 2448 return ret; 2449 } 2450 } 2451 2452 // Set alias names 2453 if (config.getClientCertificate() != null) { 2454 config.setClientCertificateAlias(name); 2455 config.resetClientKeyEntry(); 2456 } 2457 2458 if (config.getCaCertificate() != null) { 2459 config.setCaCertificateAlias(name); 2460 config.resetCaCertificate(); 2461 } 2462 2463 return ret; 2464 } 2465 2466 private boolean putCertInKeyStore(String name, Certificate cert) { 2467 try { 2468 byte[] certData = Credentials.convertToPem(cert); 2469 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 2470 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 2471 2472 } catch (IOException e1) { 2473 return false; 2474 } catch (CertificateException e2) { 2475 return false; 2476 } 2477 } 2478 2479 void removeKeys(WifiEnterpriseConfig config) { 2480 String client = config.getClientCertificateAlias(); 2481 // a valid client certificate is configured 2482 if (!TextUtils.isEmpty(client)) { 2483 if (DBG) Log.d(TAG, "removing client private key and user cert"); 2484 mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 2485 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 2486 } 2487 2488 String ca = config.getCaCertificateAlias(); 2489 // a valid ca certificate is configured 2490 if (!TextUtils.isEmpty(ca)) { 2491 if (DBG) Log.d(TAG, "removing CA cert"); 2492 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 2493 } 2494 } 2495 2496 2497 /** Migrates the old style TLS config to the new config style. This should only be used 2498 * when restoring an old wpa_supplicant.conf or upgrading from a previous 2499 * platform version. 2500 * @return true if the config was updated 2501 * @hide 2502 */ 2503 boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) { 2504 String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); 2505 /* 2506 * If the old configuration value is not present, then there is nothing 2507 * to do. 2508 */ 2509 if (TextUtils.isEmpty(oldPrivateKey)) { 2510 return false; 2511 } else { 2512 // Also ignore it if it's empty quotes. 2513 oldPrivateKey = removeDoubleQuotes(oldPrivateKey); 2514 if (TextUtils.isEmpty(oldPrivateKey)) { 2515 return false; 2516 } 2517 } 2518 2519 config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE); 2520 config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, 2521 WifiEnterpriseConfig.ENGINE_ID_KEYSTORE); 2522 2523 /* 2524 * The old key started with the keystore:// URI prefix, but we don't 2525 * need that anymore. Trim it off if it exists. 2526 */ 2527 final String keyName; 2528 if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) { 2529 keyName = new String( 2530 oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length())); 2531 } else { 2532 keyName = oldPrivateKey; 2533 } 2534 config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName); 2535 2536 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY, 2537 config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, "")); 2538 2539 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY, 2540 config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, "")); 2541 2542 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, 2543 config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, "")); 2544 2545 // Remove old private_key string so we don't run this again. 2546 mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); 2547 2548 return true; 2549 } 2550 2551 /** Migrate certs from global pool to wifi UID if not already done */ 2552 void migrateCerts(WifiEnterpriseConfig config) { 2553 String client = config.getClientCertificateAlias(); 2554 // a valid client certificate is configured 2555 if (!TextUtils.isEmpty(client)) { 2556 if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { 2557 mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, 2558 Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 2559 mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, 2560 Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 2561 } 2562 } 2563 2564 String ca = config.getCaCertificateAlias(); 2565 // a valid ca certificate is configured 2566 if (!TextUtils.isEmpty(ca)) { 2567 if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { 2568 mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, 2569 Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 2570 } 2571 } 2572 } 2573 2574} 2575 2576