WifiConfigStore.java revision 3b26801d62a06475b722bbf29cba7f48f376654e
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.NetworkInfo.DetailedState; 26import android.net.ProxyInfo; 27import android.net.RouteInfo; 28import android.net.StaticIpConfiguration; 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; 40import android.net.wifi.WifiInfo; 41 42import android.os.Environment; 43import android.os.FileObserver; 44import android.os.Process; 45import android.os.SystemClock; 46import android.os.UserHandle; 47import android.provider.Settings; 48import android.security.Credentials; 49import android.security.KeyChain; 50import android.security.KeyStore; 51import android.text.TextUtils; 52import android.util.LocalLog; 53import android.util.Log; 54import android.util.SparseArray; 55 56import com.android.server.net.DelayedDiskWrite; 57import com.android.server.net.IpConfigStore; 58 59import java.io.BufferedReader; 60import java.io.BufferedInputStream; 61import java.io.DataInputStream; 62import java.io.DataOutputStream; 63import java.io.EOFException; 64import java.io.File; 65import java.io.FileDescriptor; 66import java.io.FileInputStream; 67import java.io.FileNotFoundException; 68import java.io.FileReader; 69import java.io.IOException; 70import java.io.PrintWriter; 71import java.math.BigInteger; 72import java.net.InetAddress; 73import java.nio.charset.Charset; 74import java.security.PrivateKey; 75import java.security.cert.Certificate; 76import java.security.cert.CertificateException; 77import java.text.SimpleDateFormat; 78import java.text.DateFormat; 79import java.util.regex.Matcher; 80import java.util.regex.Pattern; 81import java.util.*; 82 83/** 84 * This class provides the API to manage configured 85 * wifi networks. The API is not thread safe is being 86 * used only from WifiStateMachine. 87 * 88 * It deals with the following 89 * - Add/update/remove a WifiConfiguration 90 * The configuration contains two types of information. 91 * = IP and proxy configuration that is handled by WifiConfigStore and 92 * is saved to disk on any change. 93 * 94 * The format of configuration file is as follows: 95 * <version> 96 * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS> 97 * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS> 98 * .. 99 * 100 * (key, value) pairs for a given network are grouped together and can 101 * be in any order. A EOS at the end of a set of (key, value) pairs 102 * indicates that the next set of (key, value) pairs are for a new 103 * network. A network is identified by a unique ID_KEY. If there is no 104 * ID_KEY in the (key, value) pairs, the data is discarded. 105 * 106 * An invalid version on read would result in discarding the contents of 107 * the file. On the next write, the latest version is written to file. 108 * 109 * Any failures during read or write to the configuration file are ignored 110 * without reporting to the user since the likelihood of these errors are 111 * low and the impact on connectivity is low. 112 * 113 * = SSID & security details that is pushed to the supplicant. 114 * supplicant saves these details to the disk on calling 115 * saveConfigCommand(). 116 * 117 * We have two kinds of APIs exposed: 118 * > public API calls that provide fine grained control 119 * - enableNetwork, disableNetwork, addOrUpdateNetwork(), 120 * removeNetwork(). For these calls, the config is not persisted 121 * to the disk. (TODO: deprecate these calls in WifiManager) 122 * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork(). 123 * These calls persist the supplicant config to disk. 124 * 125 * - Maintain a list of configured networks for quick access 126 * 127 */ 128public class WifiConfigStore extends IpConfigStore { 129 130 private Context mContext; 131 private static final String TAG = "WifiConfigStore"; 132 private static final boolean DBG = true; 133 private static boolean VDBG = false; 134 135 private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; 136 137 /* configured networks with network id as the key */ 138 private HashMap<Integer, WifiConfiguration> mConfiguredNetworks = 139 new HashMap<Integer, WifiConfiguration>(); 140 141 /* A network id is a unique identifier for a network configured in the 142 * supplicant. Network ids are generated when the supplicant reads 143 * the configuration file at start and can thus change for networks. 144 * We store the IP configuration for networks along with a unique id 145 * that is generated from SSID and security type of the network. A mapping 146 * from the generated unique id to network id of the network is needed to 147 * map supplicant config to IP configuration. */ 148 private HashMap<Integer, Integer> mNetworkIds = 149 new HashMap<Integer, Integer>(); 150 151 /* Tracks the highest priority of configured networks */ 152 private int mLastPriority = -1; 153 154 private static final String ipConfigFile = Environment.getDataDirectory() + 155 "/misc/wifi/ipconfig.txt"; 156 157 private static final String networkHistoryConfigFile = Environment.getDataDirectory() + 158 "/misc/wifi/networkHistory.txt"; 159 160 private static final String autoJoinConfigFile = Environment.getDataDirectory() + 161 "/misc/wifi/autojoinconfig.txt"; 162 163 /* Network History Keys */ 164 private static final String SSID_KEY = "SSID: "; 165 private static final String CONFIG_KEY = "CONFIG: "; 166 private static final String CHOICE_KEY = "CHOICE: "; 167 private static final String LINK_KEY = "LINK: "; 168 private static final String BSSID_KEY = "BSSID: "; 169 private static final String BSSID_KEY_END = "/BSSID: "; 170 private static final String RSSI_KEY = "RSSI: "; 171 private static final String FREQ_KEY = "FREQ: "; 172 private static final String DATE_KEY = "DATE: "; 173 private static final String MILLI_KEY = "MILLI: "; 174 private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI: "; 175 private static final String NETWORK_ID_KEY = "ID: "; 176 private static final String PRIORITY_KEY = "PRIORITY: "; 177 private static final String DEFAULT_GW_KEY = "DEFAULT_GW: "; 178 private static final String AUTH_KEY = "AUTH: "; 179 private static final String SEPARATOR_KEY = "\n"; 180 private static final String STATUS_KEY = "AUTO_JOIN_STATUS: "; 181 private static final String BSSID_STATUS_KEY = "BSSID_STATUS: "; 182 private static final String SELF_ADDED_KEY = "SELF_ADDED: "; 183 private static final String FAILURE_KEY = "FAILURE: "; 184 private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD: "; 185 private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION: "; 186 private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY: "; 187 private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY: "; 188 private static final String UPDATE_UID_KEY = "UPDATE_UID: "; 189 private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS: "; 190 private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON: "; 191 private static final String FQDN_KEY = "FQDN: "; 192 private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES: "; 193 private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE: "; 194 private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH: "; 195 private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION: "; 196 private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY 197 = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G: "; 198 private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY 199 = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G: "; 200 private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY 201 = "THRESHOLD_UNBLACKLIST_HARD_5G: "; 202 private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY 203 = "THRESHOLD_UNBLACKLIST_SOFT_5G: "; 204 private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY 205 = "THRESHOLD_UNBLACKLIST_HARD_24G: "; 206 private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY 207 = "THRESHOLD_UNBLACKLIST_SOFT_24G: "; 208 private static final String THRESHOLD_GOOD_RSSI_5_KEY 209 = "THRESHOLD_GOOD_RSSI_5: "; 210 private static final String THRESHOLD_LOW_RSSI_5_KEY 211 = "THRESHOLD_LOW_RSSI_5: "; 212 private static final String THRESHOLD_BAD_RSSI_5_KEY 213 = "THRESHOLD_BAD_RSSI_5: "; 214 private static final String THRESHOLD_GOOD_RSSI_24_KEY 215 = "THRESHOLD_GOOD_RSSI_24: "; 216 private static final String THRESHOLD_LOW_RSSI_24_KEY 217 = "THRESHOLD_LOW_RSSI_24: "; 218 private static final String THRESHOLD_BAD_RSSI_24_KEY 219 = "THRESHOLD_BAD_RSSI_24: "; 220 private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY 221 = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING: "; 222 private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY 223 = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING: "; 224 225 private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY = 226 "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW: "; 227 private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY = 228 "A_BAND_PREFERENCE_RSSI_THRESHOLD: "; 229 private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY = 230 "G_BAND_PREFERENCE_RSSI_THRESHOLD: "; 231 232 private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY 233 = "ENABLE_AUTOJOIN_WHILE_ASSOCIATED: "; 234 235 public boolean enableAutoJoinWhileAssociated = true; 236 237 public int maxTxPacketForNetworkSwitching = 40; 238 public int maxRxPacketForNetworkSwitching = 80; 239 240 public int thresholdInitialAutoJoinAttemptMin5RSSI 241 = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5; 242 public int thresholdInitialAutoJoinAttemptMin24RSSI 243 = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24; 244 245 public int thresholdBadRssi5 = WifiConfiguration.BAD_RSSI_5; 246 public int thresholdLowRssi5 = WifiConfiguration.LOW_RSSI_5; 247 public int thresholdGoodRssi5 = WifiConfiguration.GOOD_RSSI_5; 248 public int thresholdBadRssi24 = WifiConfiguration.BAD_RSSI_24; 249 public int thresholdLowRssi24 = WifiConfiguration.LOW_RSSI_24; 250 public int thresholdGoodRssi24 = WifiConfiguration.GOOD_RSSI_24; 251 252 public int thresholdBandPreferenceRssi24 253 = WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD; 254 public int thresholdBandPreferenceRssi5 255 = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD; 256 public int thresholdBandPreferenceLowRssi5 257 = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW; 258 259 public int thresholdUnblacklistThreshold5Hard 260 = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD; 261 public int thresholdUnblacklistThreshold5Soft 262 = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT; 263 public int thresholdUnblacklistThreshold24Hard 264 = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD; 265 public int thresholdUnblacklistThreshold24Soft 266 = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT; 267 268 /** 269 * Regex pattern for extracting a connect choice. 270 * Matches a strings like the following: 271 * <configKey>=([0:9]+) 272 */ 273 private static Pattern mConnectChoice = 274 Pattern.compile("(.*)=([0-9]+)"); 275 276 277 /* Enterprise configuration keys */ 278 /** 279 * In old configurations, the "private_key" field was used. However, newer 280 * configurations use the key_id field with the engine_id set to "keystore". 281 * If this field is found in the configuration, the migration code is 282 * triggered. 283 */ 284 public static final String OLD_PRIVATE_KEY_NAME = "private_key"; 285 286 /** 287 * This represents an empty value of an enterprise field. 288 * NULL is used at wpa_supplicant to indicate an empty value 289 */ 290 static final String EMPTY_VALUE = "NULL"; 291 292 // Internal use only 293 private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] { 294 WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY, 295 WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY, 296 WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY, 297 WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY, 298 WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY, 299 WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY }; 300 301 302 /** 303 * The maximum number of times we will retry a connection to an access point 304 * for which we have failed in acquiring an IP address from DHCP. A value of 305 * N means that we will make N+1 connection attempts in all. 306 * <p> 307 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default 308 * value if a Settings value is not present. 309 */ 310 private static final int DEFAULT_MAX_DHCP_RETRIES = 9; 311 312 313 private final LocalLog mLocalLog; 314 private final WpaConfigFileObserver mFileObserver; 315 316 private WifiNative mWifiNative; 317 private final KeyStore mKeyStore = KeyStore.getInstance(); 318 319 /** 320 * The lastSelectedConfiguration is used to remember which network 321 * was selected last by the user. 322 * The connection to this network may not be successful, as well 323 * the selection (i.e. network priority) might not be persisted. 324 * WiFi state machine is the only object that sets this variable. 325 */ 326 private String lastSelectedConfiguration = null; 327 328 WifiConfigStore(Context c, WifiNative wn) { 329 mContext = c; 330 mWifiNative = wn; 331 332 if (VDBG) { 333 mLocalLog = mWifiNative.getLocalLog(); 334 mFileObserver = new WpaConfigFileObserver(); 335 mFileObserver.startWatching(); 336 } else { 337 mLocalLog = null; 338 mFileObserver = null; 339 } 340 } 341 342 void enableVerboseLogging(int verbose) { 343 if (verbose > 0) { 344 VDBG = true; 345 } else { 346 VDBG = false; 347 } 348 } 349 350 class WpaConfigFileObserver extends FileObserver { 351 352 public WpaConfigFileObserver() { 353 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 354 } 355 356 @Override 357 public void onEvent(int event, String path) { 358 if (event == CLOSE_WRITE) { 359 File file = new File(SUPPLICANT_CONFIG_FILE); 360 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 361 } 362 } 363 } 364 365 366 /** 367 * Fetch the list of configured networks 368 * and enable all stored networks in supplicant. 369 */ 370 void loadAndEnableAllNetworks() { 371 if (DBG) log("Loading config and enabling all networks "); 372 loadConfiguredNetworks(); 373 enableAllNetworks(); 374 } 375 376 int getConfiguredNetworksSize() { 377 return mConfiguredNetworks.size(); 378 } 379 380 private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) { 381 List<WifiConfiguration> networks = new ArrayList<>(); 382 for(WifiConfiguration config : mConfiguredNetworks.values()) { 383 WifiConfiguration newConfig = new WifiConfiguration(config); 384 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 385 //do not enumerate and return this configuration to any one, 386 //for instance WiFi Picker. 387 //instead treat it as unknown. the configuration can still be retrieved 388 //directly by the key or networkId 389 continue; 390 } 391 if (pskMap != null && config.allowedKeyManagement != null 392 && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) 393 && pskMap.containsKey(config.SSID)) { 394 newConfig.preSharedKey = pskMap.get(config.SSID); 395 } 396 networks.add(newConfig); 397 } 398 return networks; 399 } 400 401 /** 402 * Fetch the list of currently configured networks 403 * @return List of networks 404 */ 405 List<WifiConfiguration> getConfiguredNetworks() { 406 return getConfiguredNetworks(null); 407 } 408 409 /** 410 * Fetch the list of currently configured networks, filled with real preSharedKeys 411 * @return List of networks 412 */ 413 List<WifiConfiguration> getPrivilegedConfiguredNetworks() { 414 Map<String, String> pskMap = getCredentialsBySsidMap(); 415 return getConfiguredNetworks(pskMap); 416 } 417 418 /** 419 * Fetch the preSharedKeys for all networks. 420 * @return a map from Ssid to preSharedKey. 421 */ 422 private Map<String, String> getCredentialsBySsidMap() { 423 return readNetworkVariablesFromSupplicantFile("psk"); 424 } 425 426 /** 427 * Fetch the list of currently configured networks that were recently seen 428 * 429 * @return List of networks 430 */ 431 List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) { 432 List<WifiConfiguration> networks = null; 433 434 for (WifiConfiguration config : mConfiguredNetworks.values()) { 435 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 436 // Do not enumerate and return this configuration to any one, 437 // instead treat it as unknown. the configuration can still be retrieved 438 // directly by the key or networkId 439 continue; 440 } 441 442 // Calculate the RSSI for scan results that are more recent than milli 443 config.setVisibility(milli); 444 445 if (config.visibility == null) { 446 continue; 447 } 448 if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI && 449 config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) { 450 continue; 451 } 452 if (networks == null) 453 networks = new ArrayList<WifiConfiguration>(); 454 if (copy) { 455 networks.add(new WifiConfiguration(config)); 456 } else { 457 networks.add(config); 458 } 459 } 460 return networks; 461 } 462 463 /** 464 * Update the configuration and BSSID with latest RSSI value. 465 */ 466 void updateConfiguration(WifiInfo info) { 467 WifiConfiguration config = getWifiConfiguration(info.getNetworkId()); 468 if (config != null && config.scanResultCache != null) { 469 ScanResult result = config.scanResultCache.get(info.getBSSID()); 470 if (result != null) { 471 long previousSeen = result.seen; 472 int previousRssi = result.level; 473 474 // Update the scan result 475 result.seen = System.currentTimeMillis(); 476 result.level = info.getRssi(); 477 478 // Average the RSSI value 479 result.averageRssi(previousRssi, previousSeen, 480 WifiAutoJoinController.mScanResultMaximumAge); 481 if (VDBG) { 482 loge("updateConfiguration freq=" + result.frequency 483 + " BSSID=" + result.BSSID 484 + " RSSI=" + result.level 485 + " " + config.configKey()); 486 } 487 } 488 } 489 } 490 491 /** 492 * get the Wificonfiguration for this netId 493 * 494 * @return Wificonfiguration 495 */ 496 WifiConfiguration getWifiConfiguration(int netId) { 497 if (mConfiguredNetworks == null) 498 return null; 499 return mConfiguredNetworks.get(netId); 500 } 501 502 /** 503 * Get the Wificonfiguration for this key 504 * @return Wificonfiguration 505 */ 506 WifiConfiguration getWifiConfiguration(String key) { 507 if (key == null) 508 return null; 509 int hash = key.hashCode(); 510 if (mNetworkIds == null) 511 return null; 512 Integer n = mNetworkIds.get(hash); 513 if (n == null) 514 return null; 515 int netId = n.intValue(); 516 return getWifiConfiguration(netId); 517 } 518 519 /** 520 * Enable all networks and save config. This will be a no-op if the list 521 * of configured networks indicates all networks as being enabled 522 */ 523 void enableAllNetworks() { 524 boolean networkEnabledStateChanged = false; 525 for(WifiConfiguration config : mConfiguredNetworks.values()) { 526 if(config != null && config.status == Status.DISABLED 527 && (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) { 528 if(mWifiNative.enableNetwork(config.networkId, false)) { 529 networkEnabledStateChanged = true; 530 config.status = Status.ENABLED; 531 } else { 532 loge("Enable network failed on " + config.networkId); 533 } 534 } 535 } 536 537 if (networkEnabledStateChanged) { 538 mWifiNative.saveConfig(); 539 sendConfiguredNetworksChangedBroadcast(); 540 } 541 } 542 543 544 /** 545 * Selects the specified network for connection. This involves 546 * updating the priority of all the networks and enabling the given 547 * network while disabling others. 548 * 549 * Selecting a network will leave the other networks disabled and 550 * a call to enableAllNetworks() needs to be issued upon a connection 551 * or a failure event from supplicant 552 * 553 * @param netId network to select for connection 554 * @return false if the network id is invalid 555 */ 556 boolean selectNetwork(int netId) { 557 if (VDBG) localLog("selectNetwork", netId); 558 if (netId == INVALID_NETWORK_ID) return false; 559 560 // Reset the priority of each network at start or if it goes too high. 561 if (mLastPriority == -1 || mLastPriority > 1000000) { 562 for(WifiConfiguration config : mConfiguredNetworks.values()) { 563 if (config.networkId != INVALID_NETWORK_ID) { 564 config.priority = 0; 565 addOrUpdateNetworkNative(config); 566 } 567 } 568 mLastPriority = 0; 569 } 570 571 // Set to the highest priority and save the configuration. 572 WifiConfiguration config = new WifiConfiguration(); 573 config.networkId = netId; 574 config.priority = ++mLastPriority; 575 576 addOrUpdateNetworkNative(config); 577 mWifiNative.saveConfig(); 578 579 /* Enable the given network while disabling all other networks */ 580 enableNetworkWithoutBroadcast(netId, true); 581 582 /* Avoid saving the config & sending a broadcast to prevent settings 583 * from displaying a disabled list of networks */ 584 return true; 585 } 586 587 /** 588 * Add/update the specified configuration and save config 589 * 590 * @param config WifiConfiguration to be saved 591 * @return network update result 592 */ 593 NetworkUpdateResult saveNetwork(WifiConfiguration config) { 594 WifiConfiguration conf; 595 596 // A new network cannot have null SSID 597 if (config == null || (config.networkId == INVALID_NETWORK_ID && 598 config.SSID == null)) { 599 return new NetworkUpdateResult(INVALID_NETWORK_ID); 600 } 601 if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId); 602 if (VDBG) { 603 loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size() 604 + " SSID=" + config.SSID 605 + " Uid=" + Integer.toString(config.creatorUid) 606 + "/" + Integer.toString(config.lastUpdateUid)); 607 } 608 boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); 609 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 610 int netId = result.getNetworkId(); 611 612 if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId); 613 614 /* enable a new network */ 615 if (newNetwork && netId != INVALID_NETWORK_ID) { 616 if (VDBG) localLog("WifiConfigStore: will enable netId=", netId); 617 618 mWifiNative.enableNetwork(netId, false); 619 conf = mConfiguredNetworks.get(netId); 620 if (conf != null) 621 conf.status = Status.ENABLED; 622 } 623 624 conf = mConfiguredNetworks.get(netId); 625 if (conf != null) { 626 if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) { 627 if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID); 628 629 // reenable autojoin, since new information has been provided 630 conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 631 enableNetworkWithoutBroadcast(conf.networkId, false); 632 } 633 if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId=" 634 + Integer.toString(netId) 635 + " uid=" + Integer.toString(config.creatorUid)); 636 } 637 638 mWifiNative.saveConfig(); 639 sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ? 640 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE); 641 return result; 642 } 643 644 void updateStatus(int netId, DetailedState state) { 645 if (netId != INVALID_NETWORK_ID) { 646 WifiConfiguration config = mConfiguredNetworks.get(netId); 647 if (config == null) return; 648 switch (state) { 649 case CONNECTED: 650 config.status = Status.CURRENT; 651 //we successfully connected, hence remove the blacklist 652 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 653 break; 654 case DISCONNECTED: 655 //If network is already disabled, keep the status 656 if (config.status == Status.CURRENT) { 657 config.status = Status.ENABLED; 658 } 659 break; 660 default: 661 //do nothing, retain the existing state 662 break; 663 } 664 } 665 } 666 667 /** 668 * Forget the specified network and save config 669 * 670 * @param netId network to forget 671 * @return {@code true} if it succeeds, {@code false} otherwise 672 */ 673 boolean forgetNetwork(int netId) { 674 if (VDBG) localLog("forgetNetwork", netId); 675 676 boolean remove = removeConfigAndSendBroadcastIfNeeded(netId); 677 if (!remove) { 678 //success but we dont want to remove the network from supplicant conf file 679 return true; 680 } 681 if (mWifiNative.removeNetwork(netId)) { 682 mWifiNative.saveConfig(); 683 return true; 684 } else { 685 loge("Failed to remove network " + netId); 686 return false; 687 } 688 } 689 690 /** 691 * Add/update a network. Note that there is no saveConfig operation. 692 * This function is retained for compatibility with the public 693 * API. The more powerful saveNetwork() is used by the 694 * state machine 695 * 696 * @param config wifi configuration to add/update 697 * @return network Id 698 */ 699 int addOrUpdateNetwork(WifiConfiguration config) { 700 if (VDBG) localLog("addOrUpdateNetwork id=", config.networkId); 701 //adding unconditional message to chase b/15111865 702 Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId) 703 + " uid=" + Integer.toString(config.creatorUid) 704 + "/" + Integer.toString(config.lastUpdateUid)); 705 NetworkUpdateResult result = addOrUpdateNetworkNative(config); 706 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 707 WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId()); 708 if (conf != null) { 709 sendConfiguredNetworksChangedBroadcast(conf, 710 result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED : 711 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 712 } 713 } 714 return result.getNetworkId(); 715 } 716 717 /** 718 * Remove a network. Note that there is no saveConfig operation. 719 * This function is retained for compatibility with the public 720 * API. The more powerful forgetNetwork() is used by the 721 * state machine for network removal 722 * 723 * @param netId network to be removed 724 * @return {@code true} if it succeeds, {@code false} otherwise 725 */ 726 boolean removeNetwork(int netId) { 727 if (VDBG) localLog("removeNetwork", netId); 728 boolean ret = mWifiNative.removeNetwork(netId); 729 if (ret) { 730 removeConfigAndSendBroadcastIfNeeded(netId); 731 } 732 return ret; 733 } 734 735 private boolean removeConfigAndSendBroadcastIfNeeded(int netId) { 736 boolean remove = true; 737 WifiConfiguration config = mConfiguredNetworks.get(netId); 738 if (config != null) { 739 if (VDBG) { 740 loge("removeNetwork " + Integer.toString(netId) + " key=" + 741 config.configKey() + " config.id=" + Integer.toString(config.networkId)); 742 } 743 744 // cancel the last user choice 745 if (config.configKey().equals(lastSelectedConfiguration)) { 746 lastSelectedConfiguration = null; 747 } 748 749 // Remove any associated keys 750 if (config.enterpriseConfig != null) { 751 removeKeys(config.enterpriseConfig); 752 } 753 754 if (config.didSelfAdd) { 755 if (config.peerWifiConfiguration != null) { 756 for (WifiConfiguration peer : mConfiguredNetworks.values()) { 757 if (config.peerWifiConfiguration.equals(peer.configKey())) { 758 /* the configuration that trigger the add is still there */ 759 remove = false; 760 } 761 } 762 } 763 } 764 765 if (remove) { 766 mConfiguredNetworks.remove(netId); 767 mNetworkIds.remove(configKey(config)); 768 } else { 769 /* we can't directly remove the configuration since we added it ourselves, because 770 * that could cause the system to re-add it right away. 771 * Instead black list it. It will be unblacklisted only thru a new add. 772 */ 773 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DELETED); 774 mWifiNative.disableNetwork(config.networkId); 775 remove = false; 776 } 777 778 writeIpAndProxyConfigurations(); 779 sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED); 780 writeKnownNetworkHistory(); 781 } 782 return remove; 783 } 784 785 /** 786 * Enable a network. Note that there is no saveConfig operation. 787 * This function is retained for compatibility with the public 788 * API. The more powerful selectNetwork()/saveNetwork() is used by the 789 * state machine for connecting to a network 790 * 791 * @param netId network to be enabled 792 * @return {@code true} if it succeeds, {@code false} otherwise 793 */ 794 boolean enableNetwork(int netId, boolean disableOthers) { 795 boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers); 796 if (disableOthers) { 797 if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId); 798 sendConfiguredNetworksChangedBroadcast(); 799 } else { 800 if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId); 801 WifiConfiguration enabledNetwork = null; 802 synchronized(mConfiguredNetworks) { 803 enabledNetwork = mConfiguredNetworks.get(netId); 804 } 805 // check just in case the network was removed by someone else. 806 if (enabledNetwork != null) { 807 sendConfiguredNetworksChangedBroadcast(enabledNetwork, 808 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 809 } 810 } 811 return ret; 812 } 813 814 boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) { 815 boolean ret = mWifiNative.enableNetwork(netId, disableOthers); 816 817 WifiConfiguration config = mConfiguredNetworks.get(netId); 818 if (config != null) config.status = Status.ENABLED; 819 820 if (disableOthers) { 821 markAllNetworksDisabledExcept(netId); 822 } 823 return ret; 824 } 825 826 void disableAllNetworks() { 827 if (VDBG) localLog("disableAllNetworks"); 828 boolean networkDisabled = false; 829 for(WifiConfiguration config : mConfiguredNetworks.values()) { 830 if(config != null && config.status != Status.DISABLED) { 831 if(mWifiNative.disableNetwork(config.networkId)) { 832 networkDisabled = true; 833 config.status = Status.DISABLED; 834 } else { 835 loge("Disable network failed on " + config.networkId); 836 } 837 } 838 } 839 840 if (networkDisabled) { 841 sendConfiguredNetworksChangedBroadcast(); 842 } 843 } 844 /** 845 * Disable a network. Note that there is no saveConfig operation. 846 * @param netId network to be disabled 847 * @return {@code true} if it succeeds, {@code false} otherwise 848 */ 849 boolean disableNetwork(int netId) { 850 return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON); 851 } 852 853 /** 854 * Disable a network. Note that there is no saveConfig operation. 855 * @param netId network to be disabled 856 * @param reason reason code network was disabled 857 * @return {@code true} if it succeeds, {@code false} otherwise 858 */ 859 boolean disableNetwork(int netId, int reason) { 860 if (VDBG) localLog("disableNetwork", netId); 861 boolean ret = mWifiNative.disableNetwork(netId); 862 WifiConfiguration network = null; 863 WifiConfiguration config = mConfiguredNetworks.get(netId); 864 865 if (VDBG) { 866 if (config != null) { 867 loge("disableNetwork netId=" + Integer.toString(netId) 868 + " SSID=" + config.SSID 869 + " disabled=" + (config.status == Status.DISABLED) 870 + " reason=" + Integer.toString(config.disableReason)); 871 } 872 } 873 /* Only change the reason if the network was not previously disabled */ 874 if (config != null && config.status != Status.DISABLED) { 875 config.status = Status.DISABLED; 876 config.disableReason = reason; 877 network = config; 878 } 879 if (network != null) { 880 sendConfiguredNetworksChangedBroadcast(network, 881 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 882 } 883 return ret; 884 } 885 886 /** 887 * Save the configured networks in supplicant to disk 888 * @return {@code true} if it succeeds, {@code false} otherwise 889 */ 890 boolean saveConfig() { 891 return mWifiNative.saveConfig(); 892 } 893 894 /** 895 * Start WPS pin method configuration with pin obtained 896 * from the access point 897 * @param config WPS configuration 898 * @return Wps result containing status and pin 899 */ 900 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) { 901 WpsResult result = new WpsResult(); 902 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 903 /* WPS leaves all networks disabled */ 904 markAllNetworksDisabled(); 905 result.status = WpsResult.Status.SUCCESS; 906 } else { 907 loge("Failed to start WPS pin method configuration"); 908 result.status = WpsResult.Status.FAILURE; 909 } 910 return result; 911 } 912 913 /** 914 * Start WPS pin method configuration with pin obtained 915 * from the device 916 * @return WpsResult indicating status and pin 917 */ 918 WpsResult startWpsWithPinFromDevice(WpsInfo config) { 919 WpsResult result = new WpsResult(); 920 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 921 /* WPS leaves all networks disabled */ 922 if (!TextUtils.isEmpty(result.pin)) { 923 markAllNetworksDisabled(); 924 result.status = WpsResult.Status.SUCCESS; 925 } else { 926 loge("Failed to start WPS pin method configuration"); 927 result.status = WpsResult.Status.FAILURE; 928 } 929 return result; 930 } 931 932 /** 933 * Start WPS push button configuration 934 * @param config WPS configuration 935 * @return WpsResult indicating status and pin 936 */ 937 WpsResult startWpsPbc(WpsInfo config) { 938 WpsResult result = new WpsResult(); 939 if (mWifiNative.startWpsPbc(config.BSSID)) { 940 /* WPS leaves all networks disabled */ 941 markAllNetworksDisabled(); 942 result.status = WpsResult.Status.SUCCESS; 943 } else { 944 loge("Failed to start WPS push button configuration"); 945 result.status = WpsResult.Status.FAILURE; 946 } 947 return result; 948 } 949 950 /** 951 * Fetch the static IP configuration for a given network id 952 */ 953 StaticIpConfiguration getStaticIpConfiguration(int netId) { 954 WifiConfiguration config = mConfiguredNetworks.get(netId); 955 if (config != null) { 956 return config.getStaticIpConfiguration(); 957 } 958 return null; 959 } 960 961 /** 962 * Set the static IP configuration for a given network id 963 */ 964 void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) { 965 WifiConfiguration config = mConfiguredNetworks.get(netId); 966 if (config != null) { 967 config.setStaticIpConfiguration(staticIpConfiguration); 968 } 969 } 970 971 /** 972 * set default GW MAC address 973 */ 974 void setDefaultGwMacAddress(int netId, String macAddress) { 975 WifiConfiguration config = mConfiguredNetworks.get(netId); 976 if (config != null) { 977 //update defaultGwMacAddress 978 config.defaultGwMacAddress = macAddress; 979 } 980 } 981 982 983 /** 984 * Fetch the proxy properties for a given network id 985 * @param network id 986 * @return ProxyInfo for the network id 987 */ 988 ProxyInfo getProxyProperties(int netId) { 989 WifiConfiguration config = mConfiguredNetworks.get(netId); 990 if (config != null) { 991 return config.getHttpProxy(); 992 } 993 return null; 994 } 995 996 /** 997 * Return if the specified network is using static IP 998 * @param network id 999 * @return {@code true} if using static ip for netId 1000 */ 1001 boolean isUsingStaticIp(int netId) { 1002 WifiConfiguration config = mConfiguredNetworks.get(netId); 1003 if (config != null && config.getIpAssignment() == IpAssignment.STATIC) { 1004 return true; 1005 } 1006 return false; 1007 } 1008 1009 /** 1010 * Should be called when a single network configuration is made. 1011 * @param network The network configuration that changed. 1012 * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED, 1013 * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE. 1014 */ 1015 private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, 1016 int reason) { 1017 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 1018 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1019 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); 1020 intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); 1021 intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); 1022 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1023 } 1024 1025 /** 1026 * Should be called when multiple network configuration changes are made. 1027 */ 1028 private void sendConfiguredNetworksChangedBroadcast() { 1029 Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 1030 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1031 intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true); 1032 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1033 } 1034 1035 void loadConfiguredNetworks() { 1036 String listStr = mWifiNative.listNetworks(); 1037 mLastPriority = 0; 1038 1039 mConfiguredNetworks.clear(); 1040 mNetworkIds.clear(); 1041 1042 if (listStr == null) 1043 return; 1044 1045 String[] lines = listStr.split("\n"); 1046 1047 if (VDBG) { 1048 loge("loadConfiguredNetworks: found " + Integer.toString(lines.length) 1049 + " networks", true); 1050 } 1051 1052 // Skip the first line, which is a header 1053 for (int i = 1; i < lines.length; i++) { 1054 String[] result = lines[i].split("\t"); 1055 // network-id | ssid | bssid | flags 1056 WifiConfiguration config = new WifiConfiguration(); 1057 try { 1058 config.networkId = Integer.parseInt(result[0]); 1059 } catch(NumberFormatException e) { 1060 loge("Failed to read network-id '" + result[0] + "'"); 1061 continue; 1062 } 1063 if (result.length > 3) { 1064 if (result[3].indexOf("[CURRENT]") != -1) 1065 config.status = WifiConfiguration.Status.CURRENT; 1066 else if (result[3].indexOf("[DISABLED]") != -1) 1067 config.status = WifiConfiguration.Status.DISABLED; 1068 else 1069 config.status = WifiConfiguration.Status.ENABLED; 1070 } else { 1071 config.status = WifiConfiguration.Status.ENABLED; 1072 } 1073 readNetworkVariables(config); 1074 if (config.priority > mLastPriority) { 1075 mLastPriority = config.priority; 1076 } 1077 1078 config.setIpAssignment(IpAssignment.DHCP); 1079 config.setProxySettings(ProxySettings.NONE); 1080 1081 if (mNetworkIds.containsKey(configKey(config))) { 1082 // That SSID is already known, just ignore this duplicate entry 1083 if (VDBG) localLog("discarded duplicate network ", config.networkId); 1084 } else if(config.isValid()){ 1085 mConfiguredNetworks.put(config.networkId, config); 1086 mNetworkIds.put(configKey(config), config.networkId); 1087 if (VDBG) localLog("loaded configured network", config.networkId); 1088 } else { 1089 if (DBG) log("Ignoring loaded configured for network " + config.networkId 1090 + " because config are not valid"); 1091 } 1092 } 1093 1094 readIpAndProxyConfigurations(); 1095 readNetworkHistory(); 1096 readAutoJoinConfig(); 1097 1098 sendConfiguredNetworksChangedBroadcast(); 1099 1100 if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks"); 1101 1102 if (mNetworkIds.size() == 0) { 1103 // no networks? Lets log if the wpa_supplicant.conf file contents 1104 BufferedReader reader = null; 1105 try { 1106 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 1107 if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---"); 1108 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1109 if (VDBG) localLog(line); 1110 } 1111 if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---"); 1112 } catch (FileNotFoundException e) { 1113 if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 1114 } catch (IOException e) { 1115 if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 1116 } finally { 1117 try { 1118 if (reader != null) { 1119 reader.close(); 1120 } 1121 } catch (IOException e) { 1122 // Just ignore the fact that we couldn't close 1123 } 1124 } 1125 } 1126 } 1127 1128 private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) { 1129 Map<String, String> result = new HashMap<>(); 1130 BufferedReader reader = null; 1131 if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key); 1132 1133 try { 1134 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 1135 boolean found = false; 1136 String networkSsid = null; 1137 String value = null; 1138 1139 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1140 if (VDBG) loge(line); 1141 1142 if (line.matches("[ \\t]*network=\\{")) { 1143 found = true; 1144 networkSsid = null; 1145 value = null; 1146 } else if (line.matches("[ \\t]*\\{")) { 1147 found = false; 1148 networkSsid = null; 1149 value = null; 1150 } 1151 1152 if (found) { 1153 int index; 1154 if ((index = line.indexOf("ssid=")) >= 0) { 1155 networkSsid = line.substring(index + 5); 1156 } else if ((index = line.indexOf(key + "=")) >= 0) { 1157 value = line.substring(index + key.length() + 1); 1158 } 1159 1160 if (networkSsid != null && value != null) { 1161 result.put(networkSsid, value); 1162 } 1163 } 1164 } 1165 } catch (FileNotFoundException e) { 1166 if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 1167 } catch (IOException e) { 1168 if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 1169 } finally { 1170 try { 1171 if (reader != null) { 1172 reader.close(); 1173 } 1174 } catch (IOException e) { 1175 // Just ignore the fact that we couldn't close 1176 } 1177 } 1178 1179 return result; 1180 } 1181 1182 private String readNetworkVariableFromSupplicantFile(String ssid, String key) { 1183 Map<String, String> data = readNetworkVariablesFromSupplicantFile(key); 1184 if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key); 1185 return data.get(ssid); 1186 } 1187 1188 /* Mark all networks except specified netId as disabled */ 1189 private void markAllNetworksDisabledExcept(int netId) { 1190 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1191 if(config != null && config.networkId != netId) { 1192 if (config.status != Status.DISABLED) { 1193 config.status = Status.DISABLED; 1194 config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON; 1195 } 1196 } 1197 } 1198 } 1199 1200 private void markAllNetworksDisabled() { 1201 markAllNetworksDisabledExcept(INVALID_NETWORK_ID); 1202 } 1203 1204 boolean needsUnlockedKeyStore() { 1205 1206 // Any network using certificates to authenticate access requires 1207 // unlocked key store; unless the certificates can be stored with 1208 // hardware encryption 1209 1210 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1211 1212 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) 1213 && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 1214 1215 if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) { 1216 return true; 1217 } 1218 } 1219 } 1220 1221 return false; 1222 } 1223 1224 public void writeKnownNetworkHistory() { 1225 if (VDBG) { 1226 loge(" writeKnownNetworkHistory() num networks:" + 1227 Integer.toString(mConfiguredNetworks.size()), true); 1228 } 1229 1230 /* Make a copy */ 1231 final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 1232 for (WifiConfiguration config : mConfiguredNetworks.values()) { 1233 networks.add(new WifiConfiguration(config)); 1234 } 1235 1236 mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() { 1237 public void onWriteCalled(DataOutputStream out) throws IOException { 1238 for (WifiConfiguration config : networks) { 1239 //loge("onWriteCalled write SSID: " + config.SSID); 1240 /* if (config.getLinkProperties() != null) 1241 loge(" lp " + config.getLinkProperties().toString()); 1242 else 1243 loge("attempt config w/o lp"); 1244 */ 1245 1246 if (VDBG) { 1247 int num = 0; 1248 if (config.connectChoices != null) { 1249 num = config.connectChoices.size(); 1250 } 1251 loge("saving network history: " + config.configKey() + " gw: " + 1252 config.defaultGwMacAddress + " autojoin status: " + 1253 config.autoJoinStatus + " ephemeral=" + config.ephemeral 1254 + " choices:" + Integer.toString(num)); 1255 } 1256 if (config.ephemeral == true) 1257 continue; 1258 1259 if (config.isValid() == false) 1260 continue; 1261 1262 if (config.SSID == null) { 1263 if (VDBG) { 1264 loge("writeKnownNetworkHistory trying to write config with null SSID"); 1265 } 1266 continue; 1267 } 1268 1269 out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY); 1270 1271 out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY); 1272 out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY); 1273 1274 out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY); 1275 out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus) 1276 + SEPARATOR_KEY); 1277 out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status) 1278 + SEPARATOR_KEY); 1279 out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY 1280 + Integer.toString(config.disableReason) 1281 + SEPARATOR_KEY); 1282 out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId) 1283 + SEPARATOR_KEY); 1284 out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded) 1285 + SEPARATOR_KEY); 1286 out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd) 1287 + SEPARATOR_KEY); 1288 if (config.peerWifiConfiguration != null) { 1289 out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration 1290 + SEPARATOR_KEY); 1291 } 1292 out.writeUTF(NUM_CONNECTION_FAILURES_KEY 1293 + Integer.toString(config.numConnectionFailures) 1294 + SEPARATOR_KEY); 1295 out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride) 1296 + SEPARATOR_KEY); 1297 out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY 1298 + Integer.toString(config.numScorerOverrideAndSwitchedNetwork) 1299 + SEPARATOR_KEY); 1300 out.writeUTF(NUM_ASSOCIATION_KEY 1301 + Integer.toString(config.numAssociation) 1302 + SEPARATOR_KEY); 1303 out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp) 1304 + SEPARATOR_KEY); 1305 out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid) 1306 + SEPARATOR_KEY); 1307 out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid) 1308 + SEPARATOR_KEY); 1309 out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid) 1310 + SEPARATOR_KEY); 1311 String allowedKeyManagementString = 1312 makeString(config.allowedKeyManagement, 1313 WifiConfiguration.KeyMgmt.strings); 1314 out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY); 1315 1316 if (config.connectChoices != null) { 1317 for (String key : config.connectChoices.keySet()) { 1318 Integer choice = config.connectChoices.get(key); 1319 out.writeUTF(CHOICE_KEY + key + "=" 1320 + choice.toString() + SEPARATOR_KEY); 1321 } 1322 } 1323 if (config.linkedConfigurations != null) { 1324 for (String key : config.linkedConfigurations.keySet()) { 1325 out.writeUTF(LINK_KEY + key + SEPARATOR_KEY); 1326 } 1327 } 1328 1329 String macAddress = config.defaultGwMacAddress; 1330 if (macAddress != null) { 1331 out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY); 1332 } 1333 1334 if (config.scanResultCache != null) { 1335 for (ScanResult result : config.scanResultCache.values()) { 1336 out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY); 1337 1338 out.writeUTF(FREQ_KEY + Integer.toString(result.frequency) 1339 + SEPARATOR_KEY); 1340 1341 out.writeUTF(RSSI_KEY + Integer.toString(result.level) 1342 + SEPARATOR_KEY); 1343 1344 out.writeUTF(BSSID_STATUS_KEY + Integer.toString(result.status) 1345 + SEPARATOR_KEY); 1346 1347 if (result.seen != 0) { 1348 out.writeUTF(MILLI_KEY + Long.toString(result.seen) 1349 + SEPARATOR_KEY); 1350 } 1351 out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY); 1352 } 1353 } 1354 if (config.lastFailure != null) { 1355 out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY); 1356 } 1357 out.writeUTF(SEPARATOR_KEY); 1358 } 1359 } 1360 1361 }); 1362 } 1363 1364 public void setLastSelectedConfiguration(int netId) { 1365 if (DBG) { 1366 loge("setLastSelectedConfiguration " + Integer.toString(netId)); 1367 } 1368 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 1369 lastSelectedConfiguration = null; 1370 } else { 1371 WifiConfiguration selected = getWifiConfiguration(netId); 1372 if (selected == null) { 1373 lastSelectedConfiguration = null; 1374 } else { 1375 lastSelectedConfiguration = selected.configKey(); 1376 if (VDBG) { 1377 loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration); 1378 } 1379 } 1380 } 1381 } 1382 1383 public String getLastSelectedConfiguration() { 1384 return lastSelectedConfiguration; 1385 } 1386 1387 private void readNetworkHistory() { 1388 if (VDBG) { 1389 loge("will readNetworkHistory path:" + networkHistoryConfigFile, true); 1390 } 1391 DataInputStream in = null; 1392 try { 1393 in = new DataInputStream(new BufferedInputStream(new FileInputStream( 1394 networkHistoryConfigFile))); 1395 WifiConfiguration config = null; 1396 while (true) { 1397 int id = -1; 1398 String key = in.readUTF(); 1399 String bssid = null; 1400 String ssid = null; 1401 1402 int freq = 0; 1403 int status = 0; 1404 long seen = 0; 1405 int rssi = WifiConfiguration.INVALID_RSSI; 1406 String caps = null; 1407 if (key.startsWith(CONFIG_KEY)) { 1408 1409 if (config != null) { 1410 config = null; 1411 } 1412 String configKey = key.replace(CONFIG_KEY, ""); 1413 configKey = configKey.replace(SEPARATOR_KEY, ""); 1414 // get the networkId for that config Key 1415 Integer n = mNetworkIds.get(configKey.hashCode()); 1416 // skip reading that configuration data 1417 // since we don't have a corresponding network ID 1418 if (n == null) { 1419 loge("readNetworkHistory didnt find netid for hash=" 1420 + Integer.toString(configKey.hashCode()) 1421 + " key: " + configKey); 1422 continue; 1423 } 1424 config = mConfiguredNetworks.get(n); 1425 if (config == null) { 1426 loge("readNetworkHistory didnt find config for netid=" 1427 + n.toString() 1428 + " key: " + configKey); 1429 } 1430 status = 0; 1431 ssid = null; 1432 bssid = null; 1433 freq = 0; 1434 seen = 0; 1435 rssi = WifiConfiguration.INVALID_RSSI; 1436 caps = null; 1437 1438 } else if (config != null) { 1439 if (key.startsWith(SSID_KEY)) { 1440 ssid = key.replace(SSID_KEY, ""); 1441 ssid = ssid.replace(SEPARATOR_KEY, ""); 1442 if (config.SSID != null && !config.SSID.equals(ssid)) { 1443 loge("Error parsing network history file, mismatched SSIDs"); 1444 config = null; //error 1445 ssid = null; 1446 } else { 1447 config.SSID = ssid; 1448 } 1449 } 1450 1451 if (key.startsWith(FQDN_KEY)) { 1452 String fqdn = key.replace(FQDN_KEY, ""); 1453 fqdn = fqdn.replace(SEPARATOR_KEY, ""); 1454 config.FQDN = fqdn; 1455 } 1456 1457 if (key.startsWith(DEFAULT_GW_KEY)) { 1458 String gateway = key.replace(DEFAULT_GW_KEY, ""); 1459 gateway = gateway.replace(SEPARATOR_KEY, ""); 1460 config.defaultGwMacAddress = gateway; 1461 } 1462 1463 if (key.startsWith(STATUS_KEY)) { 1464 String st = key.replace(STATUS_KEY, ""); 1465 st = st.replace(SEPARATOR_KEY, ""); 1466 config.autoJoinStatus = Integer.parseInt(st); 1467 } 1468 1469 /* 1470 if (key.startsWith(SUPPLICANT_STATUS_KEY)) { 1471 String status = key.replace(SUPPLICANT_STATUS_KEY, ""); 1472 status = status.replace(SEPARATOR_KEY, ""); 1473 config.status = Integer.parseInt(status); 1474 } 1475 1476 if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) { 1477 String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, ""); 1478 reason = reason.replace(SEPARATOR_KEY, ""); 1479 config.disableReason = Integer.parseInt(reason); 1480 }*/ 1481 1482 if (key.startsWith(SELF_ADDED_KEY)) { 1483 String selfAdded = key.replace(SELF_ADDED_KEY, ""); 1484 selfAdded = selfAdded.replace(SEPARATOR_KEY, ""); 1485 config.selfAdded = Boolean.parseBoolean(selfAdded); 1486 } 1487 1488 if (key.startsWith(DID_SELF_ADD_KEY)) { 1489 String didSelfAdd = key.replace(DID_SELF_ADD_KEY, ""); 1490 didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, ""); 1491 config.didSelfAdd = Boolean.parseBoolean(didSelfAdd); 1492 } 1493 1494 if (key.startsWith(CREATOR_UID_KEY)) { 1495 String uid = key.replace(CREATOR_UID_KEY, ""); 1496 uid = uid.replace(SEPARATOR_KEY, ""); 1497 config.creatorUid = Integer.parseInt(uid); 1498 } 1499 1500 if (key.startsWith(BLACKLIST_MILLI_KEY)) { 1501 String milli = key.replace(BLACKLIST_MILLI_KEY, ""); 1502 milli = milli.replace(SEPARATOR_KEY, ""); 1503 config.blackListTimestamp = Long.parseLong(milli); 1504 } 1505 1506 if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) { 1507 String num = key.replace(NUM_CONNECTION_FAILURES_KEY, ""); 1508 num = num.replace(SEPARATOR_KEY, ""); 1509 config.numConnectionFailures = Integer.parseInt(num); 1510 } 1511 1512 if (key.startsWith(SCORER_OVERRIDE_KEY)) { 1513 String num = key.replace(SCORER_OVERRIDE_KEY, ""); 1514 num = num.replace(SEPARATOR_KEY, ""); 1515 config.numScorerOverride = Integer.parseInt(num); 1516 } 1517 1518 if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) { 1519 String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, ""); 1520 num = num.replace(SEPARATOR_KEY, ""); 1521 config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num); 1522 } 1523 1524 if (key.startsWith(NUM_ASSOCIATION_KEY)) { 1525 String num = key.replace(NUM_ASSOCIATION_KEY, ""); 1526 num = num.replace(SEPARATOR_KEY, ""); 1527 config.numAssociation = Integer.parseInt(num); 1528 } 1529 1530 if (key.startsWith(CONNECT_UID_KEY)) { 1531 String uid = key.replace(CONNECT_UID_KEY, ""); 1532 uid = uid.replace(SEPARATOR_KEY, ""); 1533 config.lastConnectUid = Integer.parseInt(uid); 1534 } 1535 1536 if (key.startsWith(UPDATE_UID_KEY)) { 1537 String uid = key.replace(UPDATE_UID_KEY, ""); 1538 uid = uid.replace(SEPARATOR_KEY, ""); 1539 config.lastUpdateUid = Integer.parseInt(uid); 1540 } 1541 1542 if (key.startsWith(FAILURE_KEY)) { 1543 config.lastFailure = key.replace(FAILURE_KEY, ""); 1544 config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, ""); 1545 } 1546 1547 if (key.startsWith(PEER_CONFIGURATION_KEY)) { 1548 config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, ""); 1549 config.peerWifiConfiguration = 1550 config.peerWifiConfiguration.replace(SEPARATOR_KEY, ""); 1551 } 1552 1553 if (key.startsWith(CHOICE_KEY)) { 1554 String choiceStr = key.replace(CHOICE_KEY, ""); 1555 choiceStr = choiceStr.replace(SEPARATOR_KEY, ""); 1556 String configKey = ""; 1557 int choice = 0; 1558 Matcher match = mConnectChoice.matcher(choiceStr); 1559 if (!match.find()) { 1560 if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " + 1561 " Couldnt match pattern : " + choiceStr); 1562 } else { 1563 configKey = match.group(1); 1564 try { 1565 choice = Integer.parseInt(match.group(2)); 1566 } catch (NumberFormatException e) { 1567 choice = 0; 1568 } 1569 if (choice > 0) { 1570 if (config.connectChoices == null) { 1571 config.connectChoices = new HashMap<String, Integer>(); 1572 } 1573 config.connectChoices.put(configKey, choice); 1574 } 1575 } 1576 } 1577 1578 if (key.startsWith(LINK_KEY)) { 1579 String configKey = key.replace(LINK_KEY, ""); 1580 configKey = configKey.replace(SEPARATOR_KEY, ""); 1581 if (config.linkedConfigurations == null) { 1582 config.linkedConfigurations = new HashMap<String, Integer>(); 1583 } 1584 if (config.linkedConfigurations != null) { 1585 config.linkedConfigurations.put(configKey, -1); 1586 } 1587 } 1588 1589 if (key.startsWith(BSSID_KEY)) { 1590 if (key.startsWith(BSSID_KEY)) { 1591 bssid = key.replace(BSSID_KEY, ""); 1592 bssid = bssid.replace(SEPARATOR_KEY, ""); 1593 freq = 0; 1594 seen = 0; 1595 rssi = WifiConfiguration.INVALID_RSSI; 1596 caps = ""; 1597 status = 0; 1598 } 1599 1600 if (key.startsWith(RSSI_KEY)) { 1601 String lvl = key.replace(RSSI_KEY, ""); 1602 lvl = lvl.replace(SEPARATOR_KEY, ""); 1603 rssi = Integer.parseInt(lvl); 1604 } 1605 1606 if (key.startsWith(BSSID_STATUS_KEY)) { 1607 String st = key.replace(BSSID_STATUS_KEY, ""); 1608 st = st.replace(SEPARATOR_KEY, ""); 1609 status = Integer.parseInt(st); 1610 } 1611 1612 if (key.startsWith(FREQ_KEY)) { 1613 String channel = key.replace(FREQ_KEY, ""); 1614 channel = channel.replace(SEPARATOR_KEY, ""); 1615 freq = Integer.parseInt(channel); 1616 } 1617 1618 if (key.startsWith(DATE_KEY)) { 1619 /* 1620 * when reading the configuration from file we don't update the date 1621 * so as to avoid reading back stale or non-sensical data that would 1622 * depend on network time. 1623 * The date of a WifiConfiguration should only come from actual scan result. 1624 * 1625 String s = key.replace(FREQ_KEY, ""); 1626 seen = Integer.getInteger(s); 1627 */ 1628 } 1629 1630 if (key.startsWith(BSSID_KEY_END)) { 1631 1632 if ((bssid != null) && (ssid != null)) { 1633 1634 if (config.scanResultCache == null) { 1635 config.scanResultCache = new HashMap<String, ScanResult>(); 1636 } 1637 WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid); 1638 ScanResult result = new ScanResult(wssid, bssid, 1639 caps, rssi, freq, (long) 0); 1640 result.seen = seen; 1641 config.scanResultCache.put(bssid, result); 1642 result.status = status; 1643 } 1644 } 1645 } 1646 } 1647 } 1648 } catch (EOFException ignore) { 1649 if (in != null) { 1650 try { 1651 in.close(); 1652 } catch (Exception e) { 1653 loge("readNetworkHistory: Error reading file" + e); 1654 } 1655 } 1656 } catch (IOException e) { 1657 loge("readNetworkHistory: No config file, revert to default" + e); 1658 } 1659 1660 if(in!=null) { 1661 try { 1662 in.close(); 1663 } catch (Exception e) { 1664 loge("readNetworkHistory: Error closing file" + e); 1665 } 1666 } 1667 } 1668 1669 private void readAutoJoinConfig() { 1670 BufferedReader reader = null; 1671 try { 1672 1673 reader = new BufferedReader(new FileReader(autoJoinConfigFile)); 1674 1675 for (String key = reader.readLine(); key != null; key = reader.readLine()) { 1676 if (key != null) { 1677 Log.d(TAG, "readAutoJoinConfig line: " + key); 1678 } 1679 if (key.startsWith(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY)) { 1680 String st = key.replace(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY, ""); 1681 st = st.replace(SEPARATOR_KEY, ""); 1682 try { 1683 enableAutoJoinWhileAssociated = Integer.parseInt(st) != 0; 1684 Log.d(TAG,"readAutoJoinConfig: enabled = " + enableAutoJoinWhileAssociated); 1685 } catch (NumberFormatException e) { 1686 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1687 } 1688 } 1689 1690 if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY)) { 1691 String st = 1692 key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, ""); 1693 st = st.replace(SEPARATOR_KEY, ""); 1694 try { 1695 thresholdInitialAutoJoinAttemptMin5RSSI = Integer.parseInt(st); 1696 Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin5RSSI = " 1697 + Integer.toString(thresholdInitialAutoJoinAttemptMin5RSSI)); 1698 } catch (NumberFormatException e) { 1699 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1700 } 1701 } 1702 1703 if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY)) { 1704 String st = 1705 key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, ""); 1706 st = st.replace(SEPARATOR_KEY, ""); 1707 try { 1708 thresholdInitialAutoJoinAttemptMin24RSSI = Integer.parseInt(st); 1709 Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin24RSSI = " 1710 + Integer.toString(thresholdInitialAutoJoinAttemptMin24RSSI)); 1711 } catch (NumberFormatException e) { 1712 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1713 } 1714 } 1715 1716 if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_5G_KEY)) { 1717 String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, ""); 1718 st = st.replace(SEPARATOR_KEY, ""); 1719 try { 1720 thresholdUnblacklistThreshold5Hard = Integer.parseInt(st); 1721 Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Hard = " 1722 + Integer.toString(thresholdUnblacklistThreshold5Hard)); 1723 } catch (NumberFormatException e) { 1724 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1725 } 1726 } 1727 if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY)) { 1728 String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, ""); 1729 st = st.replace(SEPARATOR_KEY, ""); 1730 try { 1731 thresholdUnblacklistThreshold5Soft = Integer.parseInt(st); 1732 Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Soft = " 1733 + Integer.toString(thresholdUnblacklistThreshold5Soft)); 1734 } catch (NumberFormatException e) { 1735 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1736 } 1737 } 1738 if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_24G_KEY)) { 1739 String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, ""); 1740 st = st.replace(SEPARATOR_KEY, ""); 1741 try { 1742 thresholdUnblacklistThreshold24Hard = Integer.parseInt(st); 1743 Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Hard = " 1744 + Integer.toString(thresholdUnblacklistThreshold24Hard)); 1745 } catch (NumberFormatException e) { 1746 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1747 } 1748 } 1749 if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY)) { 1750 String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, ""); 1751 st = st.replace(SEPARATOR_KEY, ""); 1752 try { 1753 thresholdUnblacklistThreshold24Soft = Integer.parseInt(st); 1754 Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Soft = " 1755 + Integer.toString(thresholdUnblacklistThreshold24Soft)); 1756 } catch (NumberFormatException e) { 1757 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1758 } 1759 } 1760 1761 if (key.startsWith(THRESHOLD_GOOD_RSSI_5_KEY)) { 1762 String st = key.replace(THRESHOLD_GOOD_RSSI_5_KEY, ""); 1763 st = st.replace(SEPARATOR_KEY, ""); 1764 try { 1765 thresholdGoodRssi5 = Integer.parseInt(st); 1766 Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi5 = " 1767 + Integer.toString(thresholdGoodRssi5)); 1768 } catch (NumberFormatException e) { 1769 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1770 } 1771 } 1772 if (key.startsWith(THRESHOLD_LOW_RSSI_5_KEY)) { 1773 String st = key.replace(THRESHOLD_LOW_RSSI_5_KEY, ""); 1774 st = st.replace(SEPARATOR_KEY, ""); 1775 try { 1776 thresholdLowRssi5 = Integer.parseInt(st); 1777 Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi5 = " 1778 + Integer.toString(thresholdLowRssi5)); 1779 } catch (NumberFormatException e) { 1780 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1781 } 1782 } 1783 if (key.startsWith(THRESHOLD_BAD_RSSI_5_KEY)) { 1784 String st = key.replace(THRESHOLD_BAD_RSSI_5_KEY, ""); 1785 st = st.replace(SEPARATOR_KEY, ""); 1786 try { 1787 thresholdBadRssi5 = Integer.parseInt(st); 1788 Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi5 = " 1789 + Integer.toString(thresholdBadRssi5)); 1790 } catch (NumberFormatException e) { 1791 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1792 } 1793 } 1794 1795 if (key.startsWith(THRESHOLD_GOOD_RSSI_24_KEY)) { 1796 String st = key.replace(THRESHOLD_GOOD_RSSI_24_KEY, ""); 1797 st = st.replace(SEPARATOR_KEY, ""); 1798 try { 1799 thresholdGoodRssi24 = Integer.parseInt(st); 1800 Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi24 = " 1801 + Integer.toString(thresholdGoodRssi24)); 1802 } catch (NumberFormatException e) { 1803 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1804 } 1805 } 1806 if (key.startsWith(THRESHOLD_LOW_RSSI_24_KEY)) { 1807 String st = key.replace(THRESHOLD_LOW_RSSI_24_KEY, ""); 1808 st = st.replace(SEPARATOR_KEY, ""); 1809 try { 1810 thresholdLowRssi24 = Integer.parseInt(st); 1811 Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi24 = " 1812 + Integer.toString(thresholdLowRssi24)); 1813 } catch (NumberFormatException e) { 1814 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1815 } 1816 } 1817 if (key.startsWith(THRESHOLD_BAD_RSSI_24_KEY)) { 1818 String st = key.replace(THRESHOLD_BAD_RSSI_24_KEY, ""); 1819 st = st.replace(SEPARATOR_KEY, ""); 1820 try { 1821 thresholdBadRssi24 = Integer.parseInt(st); 1822 Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi24 = " 1823 + Integer.toString(thresholdBadRssi24)); 1824 } catch (NumberFormatException e) { 1825 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1826 } 1827 } 1828 1829 if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) { 1830 String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, ""); 1831 st = st.replace(SEPARATOR_KEY, ""); 1832 try { 1833 maxTxPacketForNetworkSwitching = Integer.parseInt(st); 1834 Log.d(TAG,"readAutoJoinConfig: maxTxPacketForNetworkSwitching = " 1835 + Integer.toString(maxTxPacketForNetworkSwitching)); 1836 } catch (NumberFormatException e) { 1837 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1838 } 1839 } 1840 if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) { 1841 String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, ""); 1842 st = st.replace(SEPARATOR_KEY, ""); 1843 try { 1844 maxRxPacketForNetworkSwitching = Integer.parseInt(st); 1845 Log.d(TAG,"readAutoJoinConfig: maxRxPacketForNetworkSwitching = " 1846 + Integer.toString(maxRxPacketForNetworkSwitching)); 1847 } catch (NumberFormatException e) { 1848 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1849 } 1850 } 1851 1852 if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY)) { 1853 String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY, ""); 1854 st = st.replace(SEPARATOR_KEY, ""); 1855 try { 1856 thresholdBandPreferenceLowRssi5 = Integer.parseInt(st); 1857 Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceLowRssi5 = " 1858 + Integer.toString(thresholdBandPreferenceLowRssi5)); 1859 } catch (NumberFormatException e) { 1860 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1861 } 1862 } 1863 if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) { 1864 String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, ""); 1865 st = st.replace(SEPARATOR_KEY, ""); 1866 try { 1867 thresholdBandPreferenceRssi5 = Integer.parseInt(st); 1868 Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi5 = " 1869 + Integer.toString(thresholdBandPreferenceRssi5)); 1870 } catch (NumberFormatException e) { 1871 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1872 } 1873 } 1874 if (key.startsWith(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) { 1875 String st = key.replace(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, ""); 1876 st = st.replace(SEPARATOR_KEY, ""); 1877 try { 1878 thresholdBandPreferenceRssi24 = Integer.parseInt(st); 1879 Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi24 = " 1880 + Integer.toString(thresholdBandPreferenceRssi24)); 1881 } catch (NumberFormatException e) { 1882 Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key); 1883 } 1884 } 1885 } 1886 } catch (EOFException ignore) { 1887 if (reader != null) { 1888 try { 1889 reader.close(); 1890 reader = null; 1891 } catch (Exception e) { 1892 loge("readAutoJoinStatus: Error closing file" + e); 1893 } 1894 } 1895 } catch (IOException e) { 1896 loge("readAutoJoinStatus: Error parsing configuration" + e); 1897 } 1898 1899 if (reader!=null) { 1900 try { 1901 reader.close(); 1902 } catch (Exception e) { 1903 loge("readAutoJoinStatus: Error closing file" + e); 1904 } 1905 } 1906 } 1907 1908 1909 private void writeIpAndProxyConfigurations() { 1910 final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 1911 for(WifiConfiguration config : mConfiguredNetworks.values()) { 1912 if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) { 1913 networks.put(configKey(config), config.getIpConfiguration()); 1914 } 1915 } 1916 1917 super.writeIpAndProxyConfigurations(ipConfigFile, networks); 1918 } 1919 1920 private void readIpAndProxyConfigurations() { 1921 SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile); 1922 1923 if (networks.size() == 0) { 1924 // IpConfigStore.readIpAndProxyConfigurations has already logged an error. 1925 return; 1926 } 1927 1928 for (int i = 0; i < networks.size(); i++) { 1929 int id = networks.keyAt(i); 1930 WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id)); 1931 1932 1933 if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 1934 loge("configuration found for missing network, nid=" + id 1935 +", ignored, networks.size=" + Integer.toString(networks.size())); 1936 } else { 1937 config.setIpConfiguration(networks.valueAt(i)); 1938 } 1939 } 1940 } 1941 1942 /* 1943 * Convert string to Hexadecimal before passing to wifi native layer 1944 * In native function "doCommand()" have trouble in converting Unicode character string to UTF8 1945 * conversion to hex is required because SSIDs can have space characters in them; 1946 * and that can confuses the supplicant because it uses space charaters as delimiters 1947 */ 1948 1949 private String encodeSSID(String str){ 1950 String tmp = removeDoubleQuotes(str); 1951 return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8")))); 1952 } 1953 1954 private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) { 1955 /* 1956 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 1957 * network configuration. Otherwise, the networkId should 1958 * refer to an existing configuration. 1959 */ 1960 1961 if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid()); 1962 1963 int netId = config.networkId; 1964 boolean newNetwork = false; 1965 // networkId of INVALID_NETWORK_ID means we want to create a new network 1966 if (netId == INVALID_NETWORK_ID) { 1967 Integer savedNetId = mNetworkIds.get(configKey(config)); 1968 //paranoia: check if either we have a network Id or a WifiConfiguration 1969 //matching the one we are trying to add. 1970 if (savedNetId == null) { 1971 for (WifiConfiguration test : mConfiguredNetworks.values()) { 1972 if (test.configKey().equals(config.configKey())) { 1973 savedNetId = test.networkId; 1974 loge("addOrUpdateNetworkNative " + config.configKey() 1975 + " was found, but no network Id"); 1976 break; 1977 } 1978 } 1979 } 1980 if (savedNetId != null) { 1981 netId = savedNetId; 1982 } else { 1983 newNetwork = true; 1984 netId = mWifiNative.addNetwork(); 1985 if (netId < 0) { 1986 loge("Failed to add a network!"); 1987 return new NetworkUpdateResult(INVALID_NETWORK_ID); 1988 } else { 1989 loge("addOrUpdateNetworkNative created netId=" + netId); 1990 } 1991 } 1992 } 1993 1994 boolean updateFailed = true; 1995 1996 setVariables: { 1997 1998 if (config.SSID != null && 1999 !mWifiNative.setNetworkVariable( 2000 netId, 2001 WifiConfiguration.ssidVarName, 2002 encodeSSID(config.SSID))) { 2003 loge("failed to set SSID: "+config.SSID); 2004 break setVariables; 2005 } 2006 2007 if (config.BSSID != null) { 2008 loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID); 2009 if (!mWifiNative.setNetworkVariable( 2010 netId, 2011 WifiConfiguration.bssidVarName, 2012 config.BSSID)) { 2013 loge("failed to set BSSID: " + config.BSSID); 2014 break setVariables; 2015 } 2016 } 2017 2018 String allowedKeyManagementString = 2019 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 2020 if (config.allowedKeyManagement.cardinality() != 0 && 2021 !mWifiNative.setNetworkVariable( 2022 netId, 2023 WifiConfiguration.KeyMgmt.varName, 2024 allowedKeyManagementString)) { 2025 loge("failed to set key_mgmt: "+ 2026 allowedKeyManagementString); 2027 break setVariables; 2028 } 2029 2030 String allowedProtocolsString = 2031 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 2032 if (config.allowedProtocols.cardinality() != 0 && 2033 !mWifiNative.setNetworkVariable( 2034 netId, 2035 WifiConfiguration.Protocol.varName, 2036 allowedProtocolsString)) { 2037 loge("failed to set proto: "+ 2038 allowedProtocolsString); 2039 break setVariables; 2040 } 2041 2042 String allowedAuthAlgorithmsString = 2043 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 2044 if (config.allowedAuthAlgorithms.cardinality() != 0 && 2045 !mWifiNative.setNetworkVariable( 2046 netId, 2047 WifiConfiguration.AuthAlgorithm.varName, 2048 allowedAuthAlgorithmsString)) { 2049 loge("failed to set auth_alg: "+ 2050 allowedAuthAlgorithmsString); 2051 break setVariables; 2052 } 2053 2054 String allowedPairwiseCiphersString = 2055 makeString(config.allowedPairwiseCiphers, 2056 WifiConfiguration.PairwiseCipher.strings); 2057 if (config.allowedPairwiseCiphers.cardinality() != 0 && 2058 !mWifiNative.setNetworkVariable( 2059 netId, 2060 WifiConfiguration.PairwiseCipher.varName, 2061 allowedPairwiseCiphersString)) { 2062 loge("failed to set pairwise: "+ 2063 allowedPairwiseCiphersString); 2064 break setVariables; 2065 } 2066 2067 String allowedGroupCiphersString = 2068 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 2069 if (config.allowedGroupCiphers.cardinality() != 0 && 2070 !mWifiNative.setNetworkVariable( 2071 netId, 2072 WifiConfiguration.GroupCipher.varName, 2073 allowedGroupCiphersString)) { 2074 loge("failed to set group: "+ 2075 allowedGroupCiphersString); 2076 break setVariables; 2077 } 2078 2079 // Prevent client screw-up by passing in a WifiConfiguration we gave it 2080 // by preventing "*" as a key. 2081 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 2082 !mWifiNative.setNetworkVariable( 2083 netId, 2084 WifiConfiguration.pskVarName, 2085 config.preSharedKey)) { 2086 loge("failed to set psk"); 2087 break setVariables; 2088 } 2089 2090 boolean hasSetKey = false; 2091 if (config.wepKeys != null) { 2092 for (int i = 0; i < config.wepKeys.length; i++) { 2093 // Prevent client screw-up by passing in a WifiConfiguration we gave it 2094 // by preventing "*" as a key. 2095 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 2096 if (!mWifiNative.setNetworkVariable( 2097 netId, 2098 WifiConfiguration.wepKeyVarNames[i], 2099 config.wepKeys[i])) { 2100 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 2101 break setVariables; 2102 } 2103 hasSetKey = true; 2104 } 2105 } 2106 } 2107 2108 if (hasSetKey) { 2109 if (!mWifiNative.setNetworkVariable( 2110 netId, 2111 WifiConfiguration.wepTxKeyIdxVarName, 2112 Integer.toString(config.wepTxKeyIndex))) { 2113 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 2114 break setVariables; 2115 } 2116 } 2117 2118 if (!mWifiNative.setNetworkVariable( 2119 netId, 2120 WifiConfiguration.priorityVarName, 2121 Integer.toString(config.priority))) { 2122 loge(config.SSID + ": failed to set priority: " 2123 +config.priority); 2124 break setVariables; 2125 } 2126 2127 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 2128 netId, 2129 WifiConfiguration.hiddenSSIDVarName, 2130 Integer.toString(config.hiddenSSID ? 1 : 0))) { 2131 loge(config.SSID + ": failed to set hiddenSSID: "+ 2132 config.hiddenSSID); 2133 break setVariables; 2134 } 2135 2136 if (config.requirePMF && !mWifiNative.setNetworkVariable( 2137 netId, 2138 WifiConfiguration.pmfVarName, 2139 "2")) { 2140 loge(config.SSID + ": failed to set requirePMF: "+ 2141 config.requirePMF); 2142 break setVariables; 2143 } 2144 2145 if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable( 2146 netId, 2147 WifiConfiguration.updateIdentiferVarName, 2148 config.updateIdentifier)) { 2149 loge(config.SSID + ": failed to set updateIdentifier: "+ 2150 config.updateIdentifier); 2151 break setVariables; 2152 } 2153 2154 if (config.enterpriseConfig != null && 2155 config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 2156 2157 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 2158 2159 if (needsKeyStore(enterpriseConfig)) { 2160 /** 2161 * Keyguard settings may eventually be controlled by device policy. 2162 * We check here if keystore is unlocked before installing 2163 * credentials. 2164 * TODO: Do we need a dialog here ? 2165 */ 2166 if (mKeyStore.state() != KeyStore.State.UNLOCKED) { 2167 loge(config.SSID + ": key store is locked"); 2168 break setVariables; 2169 } 2170 2171 try { 2172 /* config passed may include only fields being updated. 2173 * In order to generate the key id, fetch uninitialized 2174 * fields from the currently tracked configuration 2175 */ 2176 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 2177 String keyId = config.getKeyIdForCredentials(currentConfig); 2178 2179 if (!installKeys(enterpriseConfig, keyId)) { 2180 loge(config.SSID + ": failed to install keys"); 2181 break setVariables; 2182 } 2183 } catch (IllegalStateException e) { 2184 loge(config.SSID + " invalid config for key installation"); 2185 break setVariables; 2186 } 2187 } 2188 2189 HashMap<String, String> enterpriseFields = enterpriseConfig.getFields(); 2190 for (String key : enterpriseFields.keySet()) { 2191 String value = enterpriseFields.get(key); 2192 if (key.equals("password") && value != null && value.equals("*")) { 2193 //no need to try to set an obfuscated password, which will fail 2194 continue; 2195 } 2196 if (!mWifiNative.setNetworkVariable( 2197 netId, 2198 key, 2199 value)) { 2200 removeKeys(enterpriseConfig); 2201 loge(config.SSID + ": failed to set " + key + 2202 ": " + value); 2203 break setVariables; 2204 } 2205 } 2206 } 2207 updateFailed = false; 2208 } //end of setVariables 2209 2210 if (updateFailed) { 2211 if (newNetwork) { 2212 mWifiNative.removeNetwork(netId); 2213 loge("Failed to set a network variable, removed network: " + netId); 2214 } 2215 return new NetworkUpdateResult(INVALID_NETWORK_ID); 2216 } 2217 2218 /* An update of the network variables requires reading them 2219 * back from the supplicant to update mConfiguredNetworks. 2220 * This is because some of the variables (SSID, wep keys & 2221 * passphrases) reflect different values when read back than 2222 * when written. For example, wep key is stored as * irrespective 2223 * of the value sent to the supplicant 2224 */ 2225 WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); 2226 if (currentConfig == null) { 2227 currentConfig = new WifiConfiguration(); 2228 currentConfig.setIpAssignment(IpAssignment.DHCP); 2229 currentConfig.setProxySettings(ProxySettings.NONE); 2230 currentConfig.networkId = netId; 2231 if (config != null) { 2232 //carry over the creation parameters 2233 currentConfig.selfAdded = config.selfAdded; 2234 currentConfig.didSelfAdd = config.didSelfAdd; 2235 currentConfig.lastConnectUid = config.lastConnectUid; 2236 currentConfig.lastUpdateUid = config.lastUpdateUid; 2237 currentConfig.creatorUid = config.creatorUid; 2238 currentConfig.peerWifiConfiguration = config.peerWifiConfiguration; 2239 } 2240 if (DBG) { 2241 loge("created new config netId=" + Integer.toString(netId) 2242 + " uid=" + Integer.toString(currentConfig.creatorUid)); 2243 } 2244 } 2245 2246 if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 2247 //make sure the configuration is not deleted anymore since we just 2248 //added or modified it. 2249 currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 2250 currentConfig.selfAdded = false; 2251 currentConfig.didSelfAdd = false; 2252 } 2253 2254 if (DBG) loge("will read network variables netId=" + Integer.toString(netId)); 2255 2256 readNetworkVariables(currentConfig); 2257 2258 mConfiguredNetworks.put(netId, currentConfig); 2259 mNetworkIds.put(configKey(currentConfig), netId); 2260 2261 NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config); 2262 result.setIsNewNetwork(newNetwork); 2263 result.setNetworkId(netId); 2264 return result; 2265 } 2266 2267 2268 public void linkConfiguration(WifiConfiguration config) { 2269 if (config.scanResultCache != null && config.scanResultCache.size() > 6) { 2270 // Ignore configurations with large number of BSSIDs 2271 return; 2272 } 2273 if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 2274 // Only link WPA_PSK config 2275 return; 2276 } 2277 for (WifiConfiguration link : mConfiguredNetworks.values()) { 2278 boolean doLink = false; 2279 2280 if (link.configKey().equals(config.configKey())) { 2281 continue; 2282 } 2283 2284 if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) { 2285 continue; 2286 } 2287 2288 // Autojoin will be allowed to dynamically jump from a linked configuration 2289 // to another, hence only link configurations that have equivalent level of security 2290 if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) { 2291 continue; 2292 } 2293 2294 if (link.scanResultCache != null && link.scanResultCache.size() > 6) { 2295 // Ignore configurations with large number of BSSIDs 2296 continue; 2297 } 2298 2299 if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) { 2300 // If both default GW are known, compare based on RSSI only if the GW is equal 2301 if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) { 2302 if (VDBG) { 2303 loge("linkConfiguration link due to same gw" + link.SSID + 2304 " and " + config.SSID + " GW " + config.defaultGwMacAddress); 2305 } 2306 doLink = true; 2307 } 2308 } else { 2309 // We do not know BOTH default gateways hence we will try to link 2310 // hoping that WifiConfigurations are indeed behind the same gateway. 2311 // once both WifiConfiguration have been tried and thus once both efault gateways 2312 // are known we will revisit the choice of linking them 2313 if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6) 2314 && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) { 2315 for (String abssid : config.scanResultCache.keySet()) { 2316 for (String bbssid : link.scanResultCache.keySet()) { 2317 if (VDBG) { 2318 loge("linkConfiguration try to link due to DBDC BSSID match " 2319 + link.SSID + 2320 " and " + config.SSID + " bssida " + abssid 2321 + " bssidb " + bbssid); 2322 } 2323 if (abssid.regionMatches(true, 0, bbssid, 0, 16)) { 2324 // If first 16 ascii characters of BSSID matches, 2325 // we assume this is a DBDC 2326 doLink = true; 2327 } 2328 } 2329 } 2330 } 2331 } 2332 2333 if (doLink) { 2334 if (VDBG) { 2335 loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID); 2336 } 2337 if (link.linkedConfigurations == null) { 2338 link.linkedConfigurations = new HashMap<String, Integer>(); 2339 } 2340 if (config.linkedConfigurations == null) { 2341 config.linkedConfigurations = new HashMap<String, Integer>(); 2342 } 2343 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 2344 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 2345 } else { 2346 //todo if they are linked, break the link 2347 } 2348 } 2349 } 2350 2351 /* 2352 * We try to link a scan result with a WifiConfiguration for which SSID and 2353 * key management dont match, 2354 * for instance, we try identify the 5GHz SSID of a DBDC AP, 2355 * even though we know only of the 2.4GHz 2356 * 2357 * Obviously, this function is not optimal since it is used to compare every scan 2358 * result with every Saved WifiConfiguration, with a string.equals operation. 2359 * As a speed up, might be better to implement the mConfiguredNetworks store as a 2360 * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object 2361 * so as to speed this up. Also to prevent the tiny probability of hash collision. 2362 * 2363 */ 2364 public WifiConfiguration associateWithConfiguration(ScanResult result) { 2365 String configKey = WifiConfiguration.configKey(result); 2366 if (configKey == null) { 2367 if (DBG) loge("associateWithConfiguration(): no config key " ); 2368 return null; 2369 } 2370 2371 //need to compare with quoted string 2372 String SSID = "\"" + result.SSID + "\""; 2373 2374 WifiConfiguration config = null; 2375 for (WifiConfiguration link : mConfiguredNetworks.values()) { 2376 boolean doLink = false; 2377 2378 if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) { 2379 //make sure we dont associate the scan result to a deleted config 2380 continue; 2381 } 2382 2383 if (configKey.equals(link.configKey())) { 2384 if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey ); 2385 return link; //found it exactly 2386 } 2387 2388 if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) { 2389 String bssid = ""; 2390 for (String key : link.scanResultCache.keySet()) { 2391 bssid = key; 2392 } 2393 2394 if (result.BSSID.regionMatches(true, 0, bssid, 0, 16) 2395 && SSID.regionMatches(false, 0, link.SSID, 0, 3)) { 2396 // if first 16 ascii characters of BSSID matches, and first 3 2397 // characters of SSID match, we assume this is a home setup 2398 // and thus we will try to transfer the password from the known 2399 // BSSID/SSID to the recently found BSSID/SSID 2400 2401 //if (VDBG) 2402 // loge("associateWithConfiguration OK " ); 2403 doLink = true; 2404 } 2405 } 2406 2407 if (doLink) { 2408 //try to make a non verified WifiConfiguration, but only if the original 2409 //configuration was not self already added 2410 if (VDBG) { 2411 loge("associateWithConfiguration: will create " + 2412 result.SSID + " and associate it with: " + link.SSID); 2413 } 2414 config = wifiConfigurationFromScanResult(result); 2415 if (config != null) { 2416 config.selfAdded = true; 2417 config.didSelfAdd = true; 2418 config.peerWifiConfiguration = link.configKey(); 2419 if (config.allowedKeyManagement.equals(link.allowedKeyManagement) && 2420 config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 2421 //transfer the credentials from the configuration we are linking from 2422 String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk"); 2423 if (psk != null) { 2424 config.preSharedKey = psk; 2425 if (VDBG) { 2426 if (config.preSharedKey != null) 2427 loge(" transfer PSK : " + config.preSharedKey); 2428 } 2429 2430 //link configurations 2431 if (link.linkedConfigurations == null) { 2432 link.linkedConfigurations = new HashMap<String, Integer>(); 2433 } 2434 if (config.linkedConfigurations == null) { 2435 config.linkedConfigurations = new HashMap<String, Integer>(); 2436 } 2437 link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1)); 2438 config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1)); 2439 } else { 2440 config = null; 2441 } 2442 } else { 2443 config = null; 2444 } 2445 } 2446 } else { 2447 //todo if they are linked, break the link 2448 } 2449 } 2450 return config; 2451 } 2452 2453 public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) { 2454 if (config == null) 2455 return null; 2456 long now_ms = System.currentTimeMillis(); 2457 2458 HashSet<Integer> channels = new HashSet<Integer>(); 2459 2460 //get channels for this configuration, if there are at least 2 BSSIDs 2461 if (config.scanResultCache == null && config.linkedConfigurations == null) { 2462 return null; 2463 } 2464 2465 if (VDBG) { 2466 StringBuilder dbg = new StringBuilder(); 2467 dbg.append("makeChannelList age=" + Integer.toString(age) 2468 + " for " + config.configKey()); 2469 if (config.scanResultCache != null) { 2470 dbg.append(" bssids=" + config.scanResultCache.size()); 2471 } 2472 if (config.linkedConfigurations != null) { 2473 dbg.append(" linked=" + config.linkedConfigurations.size()); 2474 } 2475 loge(dbg.toString()); 2476 } 2477 2478 if (config.scanResultCache != null && config.scanResultCache.size() > 0) { 2479 for (ScanResult result : config.scanResultCache.values()) { 2480 if (VDBG) { 2481 boolean test = (now_ms - result.seen) < age; 2482 loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency) 2483 + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test); 2484 } 2485 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2486 channels.add(result.frequency); 2487 } 2488 } 2489 } 2490 2491 //get channels for linked configurations 2492 if (config.linkedConfigurations != null) { 2493 for (String key : config.linkedConfigurations.keySet()) { 2494 WifiConfiguration linked = getWifiConfiguration(key); 2495 if (linked == null) 2496 continue; 2497 if (linked.scanResultCache == null) { 2498 continue; 2499 } 2500 for (ScanResult result : linked.scanResultCache.values()) { 2501 if (VDBG) { 2502 loge("has link: " + result.BSSID 2503 + " freq=" + Integer.toString(result.frequency) 2504 + " age=" + Long.toString(now_ms - result.seen)); 2505 } 2506 if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) { 2507 channels.add(result.frequency); 2508 } 2509 } 2510 } 2511 } 2512 return channels; 2513 } 2514 2515 public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) { 2516 WifiConfiguration found = null; 2517 if (scanResult == null) 2518 return found; 2519 2520 //first step, look for this scan Result by SSID + Key Management 2521 String key = WifiConfiguration.configKey(scanResult); 2522 int hash = key.hashCode(); 2523 2524 Integer netId = mNetworkIds.get(hash); 2525 if (netId == null) return null; 2526 WifiConfiguration config = mConfiguredNetworks.get(netId); 2527 if (config != null) { 2528 if (config.scanResultCache == null) { 2529 config.scanResultCache = new HashMap<String, ScanResult>(); 2530 } 2531 if (config.scanResultCache == null) { 2532 return null; 2533 } 2534 //add the scan result to this WifiConfiguration 2535 config.scanResultCache.put(scanResult.BSSID, scanResult); 2536 mConfiguredNetworks.put(netId, config); 2537 linkConfiguration(config); 2538 found = config; 2539 } 2540 2541 if (VDBG) { 2542 config = mConfiguredNetworks.get(netId); 2543 if (config != null) { 2544 if (config.scanResultCache != null) { 2545 String status = ""; 2546 if (scanResult.status > 0) { 2547 status = " status=" + Integer.toString(scanResult.status); 2548 } 2549 loge(" got known scan result " + 2550 scanResult.BSSID + " key : " + key + " num: " + 2551 Integer.toString(config.scanResultCache.size()) 2552 + " rssi=" + Integer.toString(scanResult.level) 2553 + " freq=" + Integer.toString(scanResult.frequency) 2554 + status); 2555 } else { 2556 loge(" got known scan result and no cache" + 2557 scanResult.BSSID + " key : " + key); 2558 } 2559 } 2560 } 2561 return found; 2562 2563 } 2564 2565 /* Compare current and new configuration and write to file on change */ 2566 private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange( 2567 WifiConfiguration currentConfig, 2568 WifiConfiguration newConfig) { 2569 boolean ipChanged = false; 2570 boolean proxyChanged = false; 2571 2572 if (VDBG) { 2573 loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " + 2574 newConfig.SSID + " path: " + ipConfigFile); 2575 } 2576 2577 2578 switch (newConfig.getIpAssignment()) { 2579 case STATIC: 2580 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 2581 ipChanged = true; 2582 } else { 2583 ipChanged = !Objects.equals( 2584 currentConfig.getStaticIpConfiguration(), 2585 newConfig.getStaticIpConfiguration()); 2586 } 2587 break; 2588 case DHCP: 2589 if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) { 2590 ipChanged = true; 2591 } 2592 break; 2593 case UNASSIGNED: 2594 /* Ignore */ 2595 break; 2596 default: 2597 loge("Ignore invalid ip assignment during write"); 2598 break; 2599 } 2600 2601 switch (newConfig.getProxySettings()) { 2602 case STATIC: 2603 case PAC: 2604 ProxyInfo newHttpProxy = newConfig.getHttpProxy(); 2605 ProxyInfo currentHttpProxy = currentConfig.getHttpProxy(); 2606 2607 if (newHttpProxy != null) { 2608 proxyChanged = !newHttpProxy.equals(currentHttpProxy); 2609 } else { 2610 proxyChanged = (currentHttpProxy != null); 2611 } 2612 break; 2613 case NONE: 2614 if (currentConfig.getProxySettings() != newConfig.getProxySettings()) { 2615 proxyChanged = true; 2616 } 2617 break; 2618 case UNASSIGNED: 2619 /* Ignore */ 2620 break; 2621 default: 2622 loge("Ignore invalid proxy configuration during write"); 2623 break; 2624 } 2625 2626 if (ipChanged) { 2627 currentConfig.setIpAssignment(newConfig.getIpAssignment()); 2628 currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration()); 2629 log("IP config changed SSID = " + currentConfig.SSID + 2630 " static configuration: " + currentConfig.getStaticIpConfiguration().toString()); 2631 } 2632 2633 if (proxyChanged) { 2634 currentConfig.setProxySettings(newConfig.getProxySettings()); 2635 currentConfig.setHttpProxy(newConfig.getHttpProxy()); 2636 log("proxy changed SSID = " + currentConfig.SSID); 2637 if (currentConfig.getHttpProxy() != null) { 2638 log(" proxyProperties: " + currentConfig.getHttpProxy().toString()); 2639 } 2640 } 2641 2642 if (ipChanged || proxyChanged) { 2643 writeIpAndProxyConfigurations(); 2644 sendConfiguredNetworksChangedBroadcast(currentConfig, 2645 WifiManager.CHANGE_REASON_CONFIG_CHANGE); 2646 } 2647 return new NetworkUpdateResult(ipChanged, proxyChanged); 2648 } 2649 2650 /** Returns true if a particular config key needs to be quoted when passed to the supplicant. */ 2651 private boolean enterpriseConfigKeyShouldBeQuoted(String key) { 2652 switch (key) { 2653 case WifiEnterpriseConfig.EAP_KEY: 2654 case WifiEnterpriseConfig.ENGINE_KEY: 2655 return false; 2656 default: 2657 return true; 2658 } 2659 } 2660 2661 /** 2662 * Read the variables from the supplicant daemon that are needed to 2663 * fill in the WifiConfiguration object. 2664 * 2665 * @param config the {@link WifiConfiguration} object to be filled in. 2666 */ 2667 private void readNetworkVariables(WifiConfiguration config) { 2668 2669 int netId = config.networkId; 2670 if (netId < 0) 2671 return; 2672 2673 /* 2674 * TODO: maybe should have a native method that takes an array of 2675 * variable names and returns an array of values. But we'd still 2676 * be doing a round trip to the supplicant daemon for each variable. 2677 */ 2678 String value; 2679 2680 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 2681 if (!TextUtils.isEmpty(value)) { 2682 if (value.charAt(0) != '"') { 2683 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 2684 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 2685 //supplicant string 2686 } else { 2687 config.SSID = value; 2688 } 2689 } else { 2690 config.SSID = null; 2691 } 2692 2693 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 2694 if (!TextUtils.isEmpty(value)) { 2695 config.BSSID = value; 2696 } else { 2697 config.BSSID = null; 2698 } 2699 2700 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 2701 config.priority = -1; 2702 if (!TextUtils.isEmpty(value)) { 2703 try { 2704 config.priority = Integer.parseInt(value); 2705 } catch (NumberFormatException ignore) { 2706 } 2707 } 2708 2709 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 2710 config.hiddenSSID = false; 2711 if (!TextUtils.isEmpty(value)) { 2712 try { 2713 config.hiddenSSID = Integer.parseInt(value) != 0; 2714 } catch (NumberFormatException ignore) { 2715 } 2716 } 2717 2718 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 2719 config.wepTxKeyIndex = -1; 2720 if (!TextUtils.isEmpty(value)) { 2721 try { 2722 config.wepTxKeyIndex = Integer.parseInt(value); 2723 } catch (NumberFormatException ignore) { 2724 } 2725 } 2726 2727 for (int i = 0; i < 4; i++) { 2728 value = mWifiNative.getNetworkVariable(netId, 2729 WifiConfiguration.wepKeyVarNames[i]); 2730 if (!TextUtils.isEmpty(value)) { 2731 config.wepKeys[i] = value; 2732 } else { 2733 config.wepKeys[i] = null; 2734 } 2735 } 2736 2737 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 2738 if (!TextUtils.isEmpty(value)) { 2739 config.preSharedKey = value; 2740 } else { 2741 config.preSharedKey = null; 2742 } 2743 2744 value = mWifiNative.getNetworkVariable(config.networkId, 2745 WifiConfiguration.Protocol.varName); 2746 if (!TextUtils.isEmpty(value)) { 2747 String vals[] = value.split(" "); 2748 for (String val : vals) { 2749 int index = 2750 lookupString(val, WifiConfiguration.Protocol.strings); 2751 if (0 <= index) { 2752 config.allowedProtocols.set(index); 2753 } 2754 } 2755 } 2756 2757 value = mWifiNative.getNetworkVariable(config.networkId, 2758 WifiConfiguration.KeyMgmt.varName); 2759 if (!TextUtils.isEmpty(value)) { 2760 String vals[] = value.split(" "); 2761 for (String val : vals) { 2762 int index = 2763 lookupString(val, WifiConfiguration.KeyMgmt.strings); 2764 if (0 <= index) { 2765 config.allowedKeyManagement.set(index); 2766 } 2767 } 2768 } 2769 2770 value = mWifiNative.getNetworkVariable(config.networkId, 2771 WifiConfiguration.AuthAlgorithm.varName); 2772 if (!TextUtils.isEmpty(value)) { 2773 String vals[] = value.split(" "); 2774 for (String val : vals) { 2775 int index = 2776 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 2777 if (0 <= index) { 2778 config.allowedAuthAlgorithms.set(index); 2779 } 2780 } 2781 } 2782 2783 value = mWifiNative.getNetworkVariable(config.networkId, 2784 WifiConfiguration.PairwiseCipher.varName); 2785 if (!TextUtils.isEmpty(value)) { 2786 String vals[] = value.split(" "); 2787 for (String val : vals) { 2788 int index = 2789 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 2790 if (0 <= index) { 2791 config.allowedPairwiseCiphers.set(index); 2792 } 2793 } 2794 } 2795 2796 value = mWifiNative.getNetworkVariable(config.networkId, 2797 WifiConfiguration.GroupCipher.varName); 2798 if (!TextUtils.isEmpty(value)) { 2799 String vals[] = value.split(" "); 2800 for (String val : vals) { 2801 int index = 2802 lookupString(val, WifiConfiguration.GroupCipher.strings); 2803 if (0 <= index) { 2804 config.allowedGroupCiphers.set(index); 2805 } 2806 } 2807 } 2808 2809 if (config.enterpriseConfig == null) { 2810 config.enterpriseConfig = new WifiEnterpriseConfig(); 2811 } 2812 HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields(); 2813 for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) { 2814 value = mWifiNative.getNetworkVariable(netId, key); 2815 if (!TextUtils.isEmpty(value)) { 2816 if (!enterpriseConfigKeyShouldBeQuoted(key)) { 2817 value = removeDoubleQuotes(value); 2818 } 2819 enterpriseFields.put(key, value); 2820 } else { 2821 enterpriseFields.put(key, EMPTY_VALUE); 2822 } 2823 } 2824 2825 if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) { 2826 saveConfig(); 2827 } 2828 2829 migrateCerts(config.enterpriseConfig); 2830 // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore); 2831 } 2832 2833 private static String removeDoubleQuotes(String string) { 2834 int length = string.length(); 2835 if ((length > 1) && (string.charAt(0) == '"') 2836 && (string.charAt(length - 1) == '"')) { 2837 return string.substring(1, length - 1); 2838 } 2839 return string; 2840 } 2841 2842 private static String makeString(BitSet set, String[] strings) { 2843 StringBuffer buf = new StringBuffer(); 2844 int nextSetBit = -1; 2845 2846 /* Make sure all set bits are in [0, strings.length) to avoid 2847 * going out of bounds on strings. (Shouldn't happen, but...) */ 2848 set = set.get(0, strings.length); 2849 2850 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 2851 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 2852 } 2853 2854 // remove trailing space 2855 if (set.cardinality() > 0) { 2856 buf.setLength(buf.length() - 1); 2857 } 2858 2859 return buf.toString(); 2860 } 2861 2862 private int lookupString(String string, String[] strings) { 2863 int size = strings.length; 2864 2865 string = string.replace('-', '_'); 2866 2867 for (int i = 0; i < size; i++) 2868 if (string.equals(strings[i])) 2869 return i; 2870 2871 // if we ever get here, we should probably add the 2872 // value to WifiConfiguration to reflect that it's 2873 // supported by the WPA supplicant 2874 loge("Failed to look-up a string: " + string); 2875 2876 return -1; 2877 } 2878 2879 /* return the allowed key management based on a scan result */ 2880 2881 public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 2882 WifiConfiguration config = new WifiConfiguration(); 2883 2884 config.SSID = "\"" + result.SSID + "\""; 2885 2886 if (VDBG) { 2887 loge("WifiConfiguration from scan results " + 2888 config.SSID + " cap " + result.capabilities); 2889 } 2890 if (result.capabilities.contains("WEP")) { 2891 config.allowedKeyManagement.set(KeyMgmt.NONE); 2892 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //? 2893 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 2894 } 2895 2896 if (result.capabilities.contains("PSK")) { 2897 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 2898 } 2899 2900 if (result.capabilities.contains("EAP")) { 2901 //this is probably wrong, as we don't have a way to enter the enterprise config 2902 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 2903 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 2904 } 2905 2906 config.scanResultCache = new HashMap<String, ScanResult>(); 2907 if (config.scanResultCache == null) 2908 return null; 2909 config.scanResultCache.put(result.BSSID, result); 2910 2911 return config; 2912 } 2913 2914 2915 /* Returns a unique for a given configuration */ 2916 private static int configKey(WifiConfiguration config) { 2917 String key = config.configKey(); 2918 return key.hashCode(); 2919 } 2920 2921 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2922 pw.println("WifiConfigStore"); 2923 pw.println("mLastPriority " + mLastPriority); 2924 pw.println("Configured networks"); 2925 for (WifiConfiguration conf : getConfiguredNetworks()) { 2926 pw.println(conf); 2927 } 2928 pw.println(); 2929 2930 if (mLocalLog != null) { 2931 pw.println("WifiConfigStore - Log Begin ----"); 2932 mLocalLog.dump(fd, pw, args); 2933 pw.println("WifiConfigStore - Log End ----"); 2934 } 2935 } 2936 2937 public String getConfigFile() { 2938 return ipConfigFile; 2939 } 2940 2941 protected void loge(String s) { 2942 loge(s, false); 2943 } 2944 2945 protected void loge(String s, boolean stack) { 2946 if (stack) { 2947 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2948 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 2949 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 2950 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 2951 } else { 2952 Log.e(TAG, s); 2953 } 2954 } 2955 2956 protected void log(String s) { 2957 Log.d(TAG, s); 2958 } 2959 2960 private void localLog(String s) { 2961 if (mLocalLog != null) { 2962 mLocalLog.log(s); 2963 } 2964 } 2965 2966 private void localLog(String s, int netId) { 2967 if (mLocalLog == null) { 2968 return; 2969 } 2970 2971 WifiConfiguration config; 2972 synchronized(mConfiguredNetworks) { 2973 config = mConfiguredNetworks.get(netId); 2974 } 2975 2976 if (config != null) { 2977 mLocalLog.log(s + " " + config.getPrintableSsid()); 2978 } else { 2979 mLocalLog.log(s + " " + netId); 2980 } 2981 } 2982 2983 // Certificate and private key management for EnterpriseConfig 2984 static boolean needsKeyStore(WifiEnterpriseConfig config) { 2985 // Has no keys to be installed 2986 if (config.getClientCertificate() == null && config.getCaCertificate() == null) 2987 return false; 2988 return true; 2989 } 2990 2991 static boolean isHardwareBackedKey(PrivateKey key) { 2992 return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); 2993 } 2994 2995 static boolean hasHardwareBackedKey(Certificate certificate) { 2996 return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); 2997 } 2998 2999 static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 3000 String client = config.getClientCertificateAlias(); 3001 if (!TextUtils.isEmpty(client)) { 3002 // a valid client certificate is configured 3003 3004 // BUGBUG: keyStore.get() never returns certBytes; because it is not 3005 // taking WIFI_UID as a parameter. It always looks for certificate 3006 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 3007 // all certificates need software keystore until we get the get() API 3008 // fixed. 3009 3010 return true; 3011 } 3012 3013 /* 3014 try { 3015 3016 if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials 3017 .USER_CERTIFICATE + client); 3018 3019 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 3020 if (factory == null) { 3021 Slog.e(TAG, "Error getting certificate factory"); 3022 return; 3023 } 3024 3025 byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); 3026 if (certBytes != null) { 3027 Certificate cert = (X509Certificate) factory.generateCertificate( 3028 new ByteArrayInputStream(certBytes)); 3029 3030 if (cert != null) { 3031 mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); 3032 3033 if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials 3034 .USER_CERTIFICATE + client); 3035 if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : 3036 "does not need" ) + " software key store"); 3037 } else { 3038 Slog.d(TAG, "could not generate certificate"); 3039 } 3040 } else { 3041 Slog.e(TAG, "Could not load client certificate " + Credentials 3042 .USER_CERTIFICATE + client); 3043 mNeedsSoftwareKeystore = true; 3044 } 3045 3046 } catch(CertificateException e) { 3047 Slog.e(TAG, "Could not read certificates"); 3048 mCaCert = null; 3049 mClientCertificate = null; 3050 } 3051 */ 3052 3053 return false; 3054 } 3055 3056 /** called when CS ask WiFistateMachine to disconnect the current network 3057 * because the score is bad. 3058 */ 3059 void handleBadNetworkDisconnectReport(int netId, WifiInfo info) { 3060 /* TODO verify the bad network is current */ 3061 WifiConfiguration config = mConfiguredNetworks.get(netId); 3062 if (config != null) { 3063 if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24 3064 && info.is24GHz()) || (info.getRssi() < 3065 WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) { 3066 //we got disconnected and RSSI was bad, so disable light 3067 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED 3068 + WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP); 3069 loge("handleBadNetworkDisconnectReport (+4) " 3070 + Integer.toString(netId) + " " + info); 3071 } else { 3072 //we got disabled but RSSI is good, so disable hard 3073 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED 3074 + WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP); 3075 loge("handleBadNetworkDisconnectReport (+8) " 3076 + Integer.toString(netId) + " " + info); 3077 } 3078 } 3079 } 3080 3081 boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) { 3082 boolean found = false; 3083 if (BSSID == null) 3084 return found; 3085 3086 // Look for the BSSID in our config store 3087 for (WifiConfiguration config : mConfiguredNetworks.values()) { 3088 if (config.scanResultCache != null) { 3089 for (ScanResult result: config.scanResultCache.values()) { 3090 if (result.BSSID.equals(BSSID)) { 3091 if (enable) { 3092 result.status = ScanResult.ENABLED; 3093 } else { 3094 // Black list the BSSID we we were trying to join 3095 // so as the Roam state machine 3096 // doesn't pick it up over and over 3097 result.status = ScanResult.AUTO_ROAM_DISABLED; 3098 found = true; 3099 } 3100 } 3101 } 3102 } 3103 } 3104 return found; 3105 } 3106 3107 int getMaxDhcpRetries() { 3108 return Settings.Global.getInt(mContext.getContentResolver(), 3109 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 3110 DEFAULT_MAX_DHCP_RETRIES); 3111 } 3112 3113 void handleSSIDStateChange(int netId, boolean enabled, String message) { 3114 WifiConfiguration config = mConfiguredNetworks.get(netId); 3115 if (config != null) { 3116 if (enabled) { 3117 loge("SSID re-enabled for " + config.configKey() + 3118 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 3119 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 3120 //TODO: http://b/16381983 Fix Wifi Network Blacklisting 3121 //TODO: really I don't know if re-enabling is right but we 3122 //TODO: should err on the side of trying to connect 3123 //TODO: even if the attempt will fail 3124 if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 3125 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED); 3126 } 3127 } else { 3128 loge("SSID temp disabled for " + config.configKey() + 3129 " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus) 3130 + " self added " + config.selfAdded + " ephemeral " + config.ephemeral); 3131 if (message != null) { 3132 loge(" message=" + message); 3133 } 3134 if (config.selfAdded && config.lastConnected == 0) { 3135 // This is a network we self added, and we never succeeded, 3136 // the user did not create this network and never entered its credentials, 3137 // so we want to be very aggressive in disabling it completely. 3138 disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE); 3139 config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 3140 config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE; 3141 } else { 3142 if (message != null) { 3143 if (message.contains("WRONG_KEY") 3144 || message.contains("AUTH_FAILED")) { 3145 // This configuration has received an auth failure, so disable it 3146 // temporarily because we don't want auto-join to try it out. 3147 // this network may be re-enabled by the "usual" 3148 // enableAllNetwork function 3149 //TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE: 3150 //TODO: if we could count on the wrong_ley or auth_failure 3151 //TODO: message to be correct 3152 //TODO: then we could just mark the configuration as 3153 //TODO: DISABLED_ON_AUTH_FAILURE 3154 //TODO: and the configuration will stay there until 3155 //TODO: user enter new credentials 3156 //TODO: It is not the case however, so instead of disabling, let's 3157 //TODO: start blacklisting hard 3158 //TODO: http://b/16381983 Fix Wifi Network Blacklisting 3159 if (config.autoJoinStatus <= 3160 WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) { 3161 // 4 auth failure will reach 128 and disable permanently 3162 // autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128 3163 config.setAutoJoinStatus(4 + config.autoJoinStatus * 4); 3164 if (config.autoJoinStatus > 3165 WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) 3166 config.setAutoJoinStatus 3167 (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 3168 } 3169 if (DBG) { 3170 loge("blacklisted " + config.configKey() + " to " 3171 + Integer.toString(config.autoJoinStatus)); 3172 } 3173 } else if (message.contains("DHCP FAILURE")) { 3174 config.numConnectionFailures++; 3175 config.lastConnectionFailure = System.currentTimeMillis(); 3176 int maxRetries = getMaxDhcpRetries(); 3177 // maxRetries == 0 means keep trying forever 3178 if (maxRetries > 0 && config.numConnectionFailures > maxRetries) { 3179 /** 3180 * If we've exceeded the maximum number of retries for DHCP 3181 * to a given network, disable the network 3182 */ 3183 config.setAutoJoinStatus 3184 (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE); 3185 disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE); 3186 } 3187 if (DBG) { 3188 loge("blacklisted " + config.configKey() + " to " 3189 + config.autoJoinStatus 3190 + " due to DHCP failure, count=" 3191 + config.numConnectionFailures); 3192 } 3193 } 3194 message.replace("\n", ""); 3195 message.replace("\r", ""); 3196 config.lastFailure = message; 3197 } 3198 } 3199 } 3200 } 3201 } 3202 3203 boolean installKeys(WifiEnterpriseConfig config, String name) { 3204 boolean ret = true; 3205 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 3206 String userCertName = Credentials.USER_CERTIFICATE + name; 3207 String caCertName = Credentials.CA_CERTIFICATE + name; 3208 if (config.getClientCertificate() != null) { 3209 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 3210 if (isHardwareBackedKey(config.getClientPrivateKey())) { 3211 // Hardware backed key store is secure enough to store keys un-encrypted, this 3212 // removes the need for user to punch a PIN to get access to these keys 3213 if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store"); 3214 ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID, 3215 KeyStore.FLAG_NONE); 3216 } else { 3217 // Software backed key store is NOT secure enough to store keys un-encrypted. 3218 // Save keys encrypted so they are protected with user's PIN. User will 3219 // have to unlock phone before being able to use these keys and connect to 3220 // networks. 3221 if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store"); 3222 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 3223 KeyStore.FLAG_ENCRYPTED); 3224 } 3225 if (ret == false) { 3226 return ret; 3227 } 3228 3229 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 3230 if (ret == false) { 3231 // Remove private key installed 3232 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 3233 return ret; 3234 } 3235 } 3236 3237 if (config.getCaCertificate() != null) { 3238 ret = putCertInKeyStore(caCertName, config.getCaCertificate()); 3239 if (ret == false) { 3240 if (config.getClientCertificate() != null) { 3241 // Remove client key+cert 3242 mKeyStore.delKey(privKeyName, Process.WIFI_UID); 3243 mKeyStore.delete(userCertName, Process.WIFI_UID); 3244 } 3245 return ret; 3246 } 3247 } 3248 3249 // Set alias names 3250 if (config.getClientCertificate() != null) { 3251 config.setClientCertificateAlias(name); 3252 config.resetClientKeyEntry(); 3253 } 3254 3255 if (config.getCaCertificate() != null) { 3256 config.setCaCertificateAlias(name); 3257 config.resetCaCertificate(); 3258 } 3259 3260 return ret; 3261 } 3262 3263 private boolean putCertInKeyStore(String name, Certificate cert) { 3264 try { 3265 byte[] certData = Credentials.convertToPem(cert); 3266 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 3267 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 3268 3269 } catch (IOException e1) { 3270 return false; 3271 } catch (CertificateException e2) { 3272 return false; 3273 } 3274 } 3275 3276 void removeKeys(WifiEnterpriseConfig config) { 3277 String client = config.getClientCertificateAlias(); 3278 // a valid client certificate is configured 3279 if (!TextUtils.isEmpty(client)) { 3280 if (DBG) Log.d(TAG, "removing client private key and user cert"); 3281 mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 3282 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 3283 } 3284 3285 String ca = config.getCaCertificateAlias(); 3286 // a valid ca certificate is configured 3287 if (!TextUtils.isEmpty(ca)) { 3288 if (DBG) Log.d(TAG, "removing CA cert"); 3289 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 3290 } 3291 } 3292 3293 3294 /** Migrates the old style TLS config to the new config style. This should only be used 3295 * when restoring an old wpa_supplicant.conf or upgrading from a previous 3296 * platform version. 3297 * @return true if the config was updated 3298 * @hide 3299 */ 3300 boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) { 3301 String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); 3302 /* 3303 * If the old configuration value is not present, then there is nothing 3304 * to do. 3305 */ 3306 if (TextUtils.isEmpty(oldPrivateKey)) { 3307 return false; 3308 } else { 3309 // Also ignore it if it's empty quotes. 3310 oldPrivateKey = removeDoubleQuotes(oldPrivateKey); 3311 if (TextUtils.isEmpty(oldPrivateKey)) { 3312 return false; 3313 } 3314 } 3315 3316 config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE); 3317 config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, 3318 WifiEnterpriseConfig.ENGINE_ID_KEYSTORE); 3319 3320 /* 3321 * The old key started with the keystore:// URI prefix, but we don't 3322 * need that anymore. Trim it off if it exists. 3323 */ 3324 final String keyName; 3325 if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) { 3326 keyName = new String( 3327 oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length())); 3328 } else { 3329 keyName = oldPrivateKey; 3330 } 3331 config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName); 3332 3333 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY, 3334 config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, "")); 3335 3336 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY, 3337 config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, "")); 3338 3339 mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, 3340 config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, "")); 3341 3342 // Remove old private_key string so we don't run this again. 3343 mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); 3344 3345 return true; 3346 } 3347 3348 /** Migrate certs from global pool to wifi UID if not already done */ 3349 void migrateCerts(WifiEnterpriseConfig config) { 3350 String client = config.getClientCertificateAlias(); 3351 // a valid client certificate is configured 3352 if (!TextUtils.isEmpty(client)) { 3353 if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { 3354 mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, 3355 Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 3356 mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, 3357 Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 3358 } 3359 } 3360 3361 String ca = config.getCaCertificateAlias(); 3362 // a valid ca certificate is configured 3363 if (!TextUtils.isEmpty(ca)) { 3364 if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { 3365 mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, 3366 Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 3367 } 3368 } 3369 } 3370 3371} 3372