WifiConfigStore.java revision 82c1e5cda881e7c9c6074f1769e002945e508d65
1/* 2 * Copyright (C) 2016 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.net.IpConfiguration.IpAssignment; 21import android.net.IpConfiguration.ProxySettings; 22import android.net.wifi.WifiConfiguration; 23import android.net.wifi.WifiConfiguration.Status; 24import android.net.wifi.WifiEnterpriseConfig; 25import android.net.wifi.WifiSsid; 26import android.net.wifi.WpsInfo; 27import android.net.wifi.WpsResult; 28import android.os.FileObserver; 29import android.os.Process; 30import android.security.Credentials; 31import android.security.KeyChain; 32import android.security.KeyStore; 33import android.text.TextUtils; 34import android.util.ArraySet; 35import android.util.LocalLog; 36import android.util.Log; 37import android.util.SparseArray; 38 39import com.android.server.wifi.hotspot2.Utils; 40import com.android.server.wifi.util.TelephonyUtil; 41 42import org.json.JSONException; 43import org.json.JSONObject; 44 45import java.io.BufferedReader; 46import java.io.File; 47import java.io.FileNotFoundException; 48import java.io.FileReader; 49import java.io.IOException; 50import java.net.URLDecoder; 51import java.nio.charset.StandardCharsets; 52import java.security.PrivateKey; 53import java.security.cert.Certificate; 54import java.security.cert.CertificateException; 55import java.security.cert.X509Certificate; 56import java.util.ArrayList; 57import java.util.Arrays; 58import java.util.BitSet; 59import java.util.Collection; 60import java.util.HashMap; 61import java.util.HashSet; 62import java.util.List; 63import java.util.Map; 64import java.util.Set; 65 66/** 67 * This class provides the API's to save/load/modify network configurations from a persistent 68 * config database. 69 * We use wpa_supplicant as our config database currently, but will be migrating to a different 70 * one sometime in the future. 71 * We use keystore for certificate/key management operations. 72 * 73 * NOTE: This class should only be used from WifiConfigManager!!! 74 */ 75public class WifiConfigStore { 76 77 public static final String TAG = "WifiConfigStore"; 78 // This is the only variable whose contents will not be interpreted by wpa_supplicant. We use it 79 // to store metadata that allows us to correlate a wpa_supplicant.conf entry with additional 80 // information about the same network stored in other files. The metadata is stored as a 81 // serialized JSON dictionary. 82 public static final String ID_STRING_VAR_NAME = "id_str"; 83 public static final String ID_STRING_KEY_FQDN = "fqdn"; 84 public static final String ID_STRING_KEY_CREATOR_UID = "creatorUid"; 85 public static final String ID_STRING_KEY_CONFIG_KEY = "configKey"; 86 public static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf"; 87 public static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp"; 88 89 // Value stored by supplicant to requirePMF 90 public static final int STORED_VALUE_FOR_REQUIRE_PMF = 2; 91 92 private static final boolean DBG = true; 93 private static boolean VDBG = false; 94 95 private final LocalLog mLocalLog; 96 private final WpaConfigFileObserver mFileObserver; 97 private final Context mContext; 98 private final WifiNative mWifiNative; 99 private final KeyStore mKeyStore; 100 private final boolean mShowNetworks; 101 private final HashSet<String> mBssidBlacklist = new HashSet<String>(); 102 103 private final BackupManagerProxy mBackupManagerProxy; 104 105 WifiConfigStore(Context context, WifiNative wifiNative, KeyStore keyStore, LocalLog localLog, 106 boolean showNetworks, boolean verboseDebug) { 107 mContext = context; 108 mWifiNative = wifiNative; 109 mKeyStore = keyStore; 110 mShowNetworks = showNetworks; 111 mBackupManagerProxy = new BackupManagerProxy(); 112 113 if (mShowNetworks) { 114 mLocalLog = localLog; 115 mFileObserver = new WpaConfigFileObserver(); 116 mFileObserver.startWatching(); 117 } else { 118 mLocalLog = null; 119 mFileObserver = null; 120 } 121 VDBG = verboseDebug; 122 } 123 124 private static String removeDoubleQuotes(String string) { 125 int length = string.length(); 126 if ((length > 1) && (string.charAt(0) == '"') 127 && (string.charAt(length - 1) == '"')) { 128 return string.substring(1, length - 1); 129 } 130 return string; 131 } 132 133 /** 134 * Generate a string to be used as a key value by wpa_supplicant from 135 * 'set', within the set of strings from 'strings' for the variable concatenated. 136 * Also transform the internal string format that uses _ (for bewildering 137 * reasons) into a wpa_supplicant adjusted value, that uses - as a separator 138 * (most of the time at least...). 139 * @param set a bit set with a one for each corresponding string to be included from strings. 140 * @param strings the set of string literals to concatenate strinfs from. 141 * @return A wpa_supplicant formatted value. 142 */ 143 private static String makeString(BitSet set, String[] strings) { 144 return makeStringWithException(set, strings, null); 145 } 146 147 /** 148 * Same as makeString with an exclusion parameter. 149 * @param set a bit set with a one for each corresponding string to be included from strings. 150 * @param strings the set of string literals to concatenate strinfs from. 151 * @param exception literal string to be excluded from the _ to - transformation. 152 * @return A wpa_supplicant formatted value. 153 */ 154 private static String makeStringWithException(BitSet set, String[] strings, String exception) { 155 StringBuilder result = new StringBuilder(); 156 157 /* Make sure all set bits are in [0, strings.length) to avoid 158 * going out of bounds on strings. (Shouldn't happen, but...) */ 159 BitSet trimmedSet = set.get(0, strings.length); 160 161 List<String> valueSet = new ArrayList<>(); 162 for (int bit = trimmedSet.nextSetBit(0); 163 bit >= 0; 164 bit = trimmedSet.nextSetBit(bit+1)) { 165 String currentName = strings[bit]; 166 if (exception != null && currentName.equals(exception)) { 167 valueSet.add(currentName); 168 } else { 169 // Most wpa_supplicant strings use a dash whereas (for some bizarre 170 // reason) the strings are defined with underscore in the code... 171 valueSet.add(currentName.replace('_', '-')); 172 } 173 } 174 return TextUtils.join(" ", valueSet); 175 } 176 177 /* 178 * Convert string to Hexadecimal before passing to wifi native layer 179 * In native function "doCommand()" have trouble in converting Unicode character string to UTF8 180 * conversion to hex is required because SSIDs can have space characters in them; 181 * and that can confuses the supplicant because it uses space charaters as delimiters 182 */ 183 private static String encodeSSID(String str) { 184 return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8)); 185 } 186 187 // Certificate and private key management for EnterpriseConfig 188 private static boolean needsKeyStore(WifiEnterpriseConfig config) { 189 return (!(config.getClientCertificate() == null && config.getCaCertificate() == null)); 190 } 191 192 private static boolean isHardwareBackedKey(PrivateKey key) { 193 return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); 194 } 195 196 private static boolean hasHardwareBackedKey(Certificate certificate) { 197 return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); 198 } 199 200 private static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { 201 java.lang.String client = config.getClientCertificateAlias(); 202 if (!TextUtils.isEmpty(client)) { 203 // a valid client certificate is configured 204 205 // BUGBUG: keyStore.get() never returns certBytes; because it is not 206 // taking WIFI_UID as a parameter. It always looks for certificate 207 // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that 208 // all certificates need software keystore until we get the get() API 209 // fixed. 210 return true; 211 } 212 return false; 213 } 214 215 private int lookupString(String string, String[] strings) { 216 int size = strings.length; 217 218 string = string.replace('-', '_'); 219 220 for (int i = 0; i < size; i++) { 221 if (string.equals(strings[i])) { 222 return i; 223 } 224 } 225 loge("Failed to look-up a string: " + string); 226 return -1; 227 } 228 229 private void readNetworkBitsetVariable(int netId, BitSet variable, String varName, 230 String[] strings) { 231 String value = mWifiNative.getNetworkVariable(netId, varName); 232 if (!TextUtils.isEmpty(value)) { 233 variable.clear(); 234 String[] vals = value.split(" "); 235 for (String val : vals) { 236 int index = lookupString(val, strings); 237 if (0 <= index) { 238 variable.set(index); 239 } 240 } 241 } 242 } 243 244 /** 245 * Read the variables from the supplicant daemon that are needed to 246 * fill in the WifiConfiguration object. 247 * 248 * @param config the {@link WifiConfiguration} object to be filled in. 249 */ 250 public void readNetworkVariables(WifiConfiguration config) { 251 if (config == null) { 252 return; 253 } 254 if (VDBG) localLog("readNetworkVariables: " + config.networkId); 255 int netId = config.networkId; 256 if (netId < 0) { 257 return; 258 } 259 /* 260 * TODO: maybe should have a native method that takes an array of 261 * variable names and returns an array of values. But we'd still 262 * be doing a round trip to the supplicant daemon for each variable. 263 */ 264 String value; 265 266 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName); 267 if (!TextUtils.isEmpty(value)) { 268 if (value.charAt(0) != '"') { 269 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\""; 270 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted 271 //supplicant string 272 } else { 273 config.SSID = value; 274 } 275 } else { 276 config.SSID = null; 277 } 278 279 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName); 280 if (!TextUtils.isEmpty(value)) { 281 config.getNetworkSelectionStatus().setNetworkSelectionBSSID(value); 282 } else { 283 config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null); 284 } 285 286 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); 287 config.priority = -1; 288 if (!TextUtils.isEmpty(value)) { 289 try { 290 config.priority = Integer.parseInt(value); 291 } catch (NumberFormatException ignore) { 292 } 293 } 294 295 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); 296 config.hiddenSSID = false; 297 if (!TextUtils.isEmpty(value)) { 298 try { 299 config.hiddenSSID = Integer.parseInt(value) != 0; 300 } catch (NumberFormatException ignore) { 301 } 302 } 303 304 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pmfVarName); 305 config.requirePMF = false; 306 if (!TextUtils.isEmpty(value)) { 307 try { 308 config.requirePMF = Integer.parseInt(value) == STORED_VALUE_FOR_REQUIRE_PMF; 309 } catch (NumberFormatException ignore) { 310 } 311 } 312 313 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); 314 config.wepTxKeyIndex = -1; 315 if (!TextUtils.isEmpty(value)) { 316 try { 317 config.wepTxKeyIndex = Integer.parseInt(value); 318 } catch (NumberFormatException ignore) { 319 } 320 } 321 322 for (int i = 0; i < 4; i++) { 323 value = mWifiNative.getNetworkVariable(netId, 324 WifiConfiguration.wepKeyVarNames[i]); 325 if (!TextUtils.isEmpty(value)) { 326 config.wepKeys[i] = value; 327 } else { 328 config.wepKeys[i] = null; 329 } 330 } 331 332 value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName); 333 if (!TextUtils.isEmpty(value)) { 334 config.preSharedKey = value; 335 } else { 336 config.preSharedKey = null; 337 } 338 339 readNetworkBitsetVariable(config.networkId, config.allowedProtocols, 340 WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings); 341 342 readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement, 343 WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings); 344 // The FT flags should not be exposed to external apps. 345 config.allowedKeyManagement = removeFastTransitionFlags(config.allowedKeyManagement); 346 347 readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms, 348 WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings); 349 350 readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers, 351 WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings); 352 353 readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers, 354 WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings); 355 356 if (config.enterpriseConfig == null) { 357 config.enterpriseConfig = new WifiEnterpriseConfig(); 358 } 359 config.enterpriseConfig.loadFromSupplicant(new SupplicantLoader(netId)); 360 } 361 362 /** 363 * Load all the configured networks from wpa_supplicant. 364 * 365 * @param configs Map of configuration key to configuration objects corresponding to all 366 * the networks. 367 * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf 368 * @return Max priority of all the configs. 369 */ 370 public int loadNetworks(Map<String, WifiConfiguration> configs, 371 SparseArray<Map<String, String>> networkExtras) { 372 int lastPriority = 0; 373 int last_id = -1; 374 boolean done = false; 375 while (!done) { 376 String listStr = mWifiNative.listNetworks(last_id); 377 if (listStr == null) { 378 return lastPriority; 379 } 380 String[] lines = listStr.split("\n"); 381 if (mShowNetworks) { 382 localLog("loadNetworks: "); 383 for (String net : lines) { 384 localLog(net); 385 } 386 } 387 // Skip the first line, which is a header 388 for (int i = 1; i < lines.length; i++) { 389 String[] result = lines[i].split("\t"); 390 // network-id | ssid | bssid | flags 391 WifiConfiguration config = new WifiConfiguration(); 392 try { 393 config.networkId = Integer.parseInt(result[0]); 394 last_id = config.networkId; 395 } catch (NumberFormatException e) { 396 loge("Failed to read network-id '" + result[0] + "'"); 397 continue; 398 } 399 // Ignore the supplicant status, start all networks disabled. 400 config.status = WifiConfiguration.Status.DISABLED; 401 readNetworkVariables(config); 402 // Parse the serialized JSON dictionary in ID_STRING_VAR_NAME once and cache the 403 // result for efficiency. 404 Map<String, String> extras = mWifiNative.getNetworkExtra(config.networkId, 405 ID_STRING_VAR_NAME); 406 if (extras == null) { 407 extras = new HashMap<String, String>(); 408 // If ID_STRING_VAR_NAME did not contain a dictionary, assume that it contains 409 // just a quoted FQDN. This is the legacy format that was used in Marshmallow. 410 final String fqdn = Utils.unquote(mWifiNative.getNetworkVariable( 411 config.networkId, ID_STRING_VAR_NAME)); 412 if (fqdn != null) { 413 extras.put(ID_STRING_KEY_FQDN, fqdn); 414 config.FQDN = fqdn; 415 // Mark the configuration as a Hotspot 2.0 network. 416 config.providerFriendlyName = ""; 417 } 418 } 419 networkExtras.put(config.networkId, extras); 420 421 if (config.priority > lastPriority) { 422 lastPriority = config.priority; 423 } 424 config.setIpAssignment(IpAssignment.DHCP); 425 config.setProxySettings(ProxySettings.NONE); 426 // The configKey is explicitly stored in wpa_supplicant.conf, because config does 427 // not contain sufficient information to compute it at this point. 428 String configKey = extras.get(ID_STRING_KEY_CONFIG_KEY); 429 if (configKey == null) { 430 // Handle the legacy case where the configKey is not stored in 431 // wpa_supplicant.conf but can be computed straight away. 432 // Force an update of this legacy network configuration by writing 433 // the configKey for this network into wpa_supplicant.conf. 434 configKey = config.configKey(); 435 saveNetworkMetadata(config); 436 } 437 final WifiConfiguration duplicateConfig = configs.put(configKey, config); 438 if (duplicateConfig != null) { 439 // The network is already known. Overwrite the duplicate entry. 440 if (mShowNetworks) { 441 localLog("Replacing duplicate network " + duplicateConfig.networkId 442 + " with " + config.networkId + "."); 443 } 444 // This can happen after the user manually connected to an AP and tried to use 445 // WPS to connect the AP later. In this case, the supplicant will create a new 446 // network for the AP although there is an existing network already. 447 mWifiNative.removeNetwork(duplicateConfig.networkId); 448 } 449 } 450 done = (lines.length == 1); 451 } 452 return lastPriority; 453 } 454 455 /** 456 * Install keys for given enterprise network. 457 * 458 * @param existingConfig Existing config corresponding to the network already stored in our 459 * database. This maybe null if it's a new network. 460 * @param config Config corresponding to the network. 461 * @return true if successful, false otherwise. 462 */ 463 private boolean installKeys(WifiEnterpriseConfig existingConfig, WifiEnterpriseConfig config, 464 String name) { 465 boolean ret = true; 466 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 467 String userCertName = Credentials.USER_CERTIFICATE + name; 468 if (config.getClientCertificate() != null) { 469 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 470 if (DBG) { 471 if (isHardwareBackedKey(config.getClientPrivateKey())) { 472 Log.d(TAG, "importing keys " + name + " in hardware backed store"); 473 } else { 474 Log.d(TAG, "importing keys " + name + " in software backed store"); 475 } 476 } 477 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 478 KeyStore.FLAG_NONE); 479 480 if (!ret) { 481 return ret; 482 } 483 484 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 485 if (!ret) { 486 // Remove private key installed 487 mKeyStore.delete(privKeyName, Process.WIFI_UID); 488 return ret; 489 } 490 } 491 492 X509Certificate[] caCertificates = config.getCaCertificates(); 493 Set<String> oldCaCertificatesToRemove = new ArraySet<String>(); 494 if (existingConfig != null && existingConfig.getCaCertificateAliases() != null) { 495 oldCaCertificatesToRemove.addAll( 496 Arrays.asList(existingConfig.getCaCertificateAliases())); 497 } 498 List<String> caCertificateAliases = null; 499 if (caCertificates != null) { 500 caCertificateAliases = new ArrayList<String>(); 501 for (int i = 0; i < caCertificates.length; i++) { 502 String alias = caCertificates.length == 1 ? name 503 : String.format("%s_%d", name, i); 504 505 oldCaCertificatesToRemove.remove(alias); 506 ret = putCertInKeyStore(Credentials.CA_CERTIFICATE + alias, caCertificates[i]); 507 if (!ret) { 508 // Remove client key+cert 509 if (config.getClientCertificate() != null) { 510 mKeyStore.delete(privKeyName, Process.WIFI_UID); 511 mKeyStore.delete(userCertName, Process.WIFI_UID); 512 } 513 // Remove added CA certs. 514 for (String addedAlias : caCertificateAliases) { 515 mKeyStore.delete(Credentials.CA_CERTIFICATE + addedAlias, Process.WIFI_UID); 516 } 517 return ret; 518 } else { 519 caCertificateAliases.add(alias); 520 } 521 } 522 } 523 // Remove old CA certs. 524 for (String oldAlias : oldCaCertificatesToRemove) { 525 mKeyStore.delete(Credentials.CA_CERTIFICATE + oldAlias, Process.WIFI_UID); 526 } 527 // Set alias names 528 if (config.getClientCertificate() != null) { 529 config.setClientCertificateAlias(name); 530 config.resetClientKeyEntry(); 531 } 532 533 if (caCertificates != null) { 534 config.setCaCertificateAliases( 535 caCertificateAliases.toArray(new String[caCertificateAliases.size()])); 536 config.resetCaCertificate(); 537 } 538 return ret; 539 } 540 541 private boolean putCertInKeyStore(String name, Certificate cert) { 542 try { 543 byte[] certData = Credentials.convertToPem(cert); 544 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 545 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 546 547 } catch (IOException e1) { 548 return false; 549 } catch (CertificateException e2) { 550 return false; 551 } 552 } 553 554 /** 555 * Remove enterprise keys from the network config. 556 * 557 * @param config Config corresponding to the network. 558 */ 559 private void removeKeys(WifiEnterpriseConfig config) { 560 String client = config.getClientCertificateAlias(); 561 // a valid client certificate is configured 562 if (!TextUtils.isEmpty(client)) { 563 if (DBG) Log.d(TAG, "removing client private key and user cert"); 564 mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 565 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 566 } 567 568 String[] aliases = config.getCaCertificateAliases(); 569 // a valid ca certificate is configured 570 if (aliases != null) { 571 for (String ca : aliases) { 572 if (!TextUtils.isEmpty(ca)) { 573 if (DBG) Log.d(TAG, "removing CA cert: " + ca); 574 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 575 } 576 } 577 } 578 } 579 580 /** 581 * Update the network metadata info stored in wpa_supplicant network extra field. 582 * @param config Config corresponding to the network. 583 * @return true if successful, false otherwise. 584 */ 585 public boolean saveNetworkMetadata(WifiConfiguration config) { 586 final Map<String, String> metadata = new HashMap<String, String>(); 587 if (config.isPasspoint()) { 588 metadata.put(ID_STRING_KEY_FQDN, config.FQDN); 589 } 590 metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey()); 591 metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid)); 592 if (!mWifiNative.setNetworkExtra(config.networkId, ID_STRING_VAR_NAME, metadata)) { 593 loge("failed to set id_str: " + metadata.toString()); 594 return false; 595 } 596 return true; 597 } 598 599 private BitSet addFastTransitionFlags(BitSet keyManagementFlags) { 600 BitSet modifiedFlags = keyManagementFlags; 601 if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 602 modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_PSK); 603 } 604 if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { 605 modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_EAP); 606 } 607 return modifiedFlags; 608 } 609 610 private BitSet removeFastTransitionFlags(BitSet keyManagementFlags) { 611 BitSet modifiedFlags = keyManagementFlags; 612 modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_PSK); 613 modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_EAP); 614 return modifiedFlags; 615 } 616 617 /** 618 * Save an entire network configuration to wpa_supplicant. 619 * 620 * @param config Config corresponding to the network. 621 * @param netId Net Id of the network. 622 * @param addFastTransitionFlags Add the BSS fast transition(80211r) flags to the network. 623 * @return true if successful, false otherwise. 624 */ 625 private boolean saveNetwork(WifiConfiguration config, int netId, 626 boolean addFastTransitionFlags) { 627 if (config == null) { 628 return false; 629 } 630 if (VDBG) localLog("saveNetwork: " + netId); 631 if (config.SSID != null && !mWifiNative.setNetworkVariable( 632 netId, 633 WifiConfiguration.ssidVarName, 634 encodeSSID(config.SSID))) { 635 loge("failed to set SSID: " + config.SSID); 636 return false; 637 } 638 if (!saveNetworkMetadata(config)) { 639 return false; 640 } 641 //set selected BSSID to supplicant 642 if (config.getNetworkSelectionStatus().getNetworkSelectionBSSID() != null) { 643 String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID(); 644 if (!mWifiNative.setNetworkVariable(netId, WifiConfiguration.bssidVarName, bssid)) { 645 loge("failed to set BSSID: " + bssid); 646 return false; 647 } 648 } 649 BitSet allowedKeyManagement = config.allowedKeyManagement; 650 if (addFastTransitionFlags) { 651 allowedKeyManagement = addFastTransitionFlags(config.allowedKeyManagement); 652 } 653 String allowedKeyManagementString = 654 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 655 if (config.allowedKeyManagement.cardinality() != 0 && !mWifiNative.setNetworkVariable( 656 netId, 657 WifiConfiguration.KeyMgmt.varName, 658 allowedKeyManagementString)) { 659 loge("failed to set key_mgmt: " + allowedKeyManagementString); 660 return false; 661 } 662 String allowedProtocolsString = 663 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 664 if (config.allowedProtocols.cardinality() != 0 && !mWifiNative.setNetworkVariable( 665 netId, 666 WifiConfiguration.Protocol.varName, 667 allowedProtocolsString)) { 668 loge("failed to set proto: " + allowedProtocolsString); 669 return false; 670 } 671 String allowedAuthAlgorithmsString = 672 makeString(config.allowedAuthAlgorithms, 673 WifiConfiguration.AuthAlgorithm.strings); 674 if (config.allowedAuthAlgorithms.cardinality() != 0 && !mWifiNative.setNetworkVariable( 675 netId, 676 WifiConfiguration.AuthAlgorithm.varName, 677 allowedAuthAlgorithmsString)) { 678 loge("failed to set auth_alg: " + allowedAuthAlgorithmsString); 679 return false; 680 } 681 String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers, 682 WifiConfiguration.PairwiseCipher.strings); 683 if (config.allowedPairwiseCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable( 684 netId, 685 WifiConfiguration.PairwiseCipher.varName, 686 allowedPairwiseCiphersString)) { 687 loge("failed to set pairwise: " + allowedPairwiseCiphersString); 688 return false; 689 } 690 // Make sure that the string "GTK_NOT_USED" is /not/ transformed - wpa_supplicant 691 // uses this literal value and not the 'dashed' version. 692 String allowedGroupCiphersString = 693 makeStringWithException(config.allowedGroupCiphers, 694 WifiConfiguration.GroupCipher.strings, 695 WifiConfiguration.GroupCipher 696 .strings[WifiConfiguration.GroupCipher.GTK_NOT_USED]); 697 if (config.allowedGroupCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable( 698 netId, 699 WifiConfiguration.GroupCipher.varName, 700 allowedGroupCiphersString)) { 701 loge("failed to set group: " + allowedGroupCiphersString); 702 return false; 703 } 704 // Prevent client screw-up by passing in a WifiConfiguration we gave it 705 // by preventing "*" as a key. 706 if (config.preSharedKey != null && !config.preSharedKey.equals("*") 707 && !mWifiNative.setNetworkVariable( 708 netId, 709 WifiConfiguration.pskVarName, 710 config.preSharedKey)) { 711 loge("failed to set psk"); 712 return false; 713 } 714 boolean hasSetKey = false; 715 if (config.wepKeys != null) { 716 for (int i = 0; i < config.wepKeys.length; i++) { 717 // Prevent client screw-up by passing in a WifiConfiguration we gave it 718 // by preventing "*" as a key. 719 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 720 if (!mWifiNative.setNetworkVariable( 721 netId, 722 WifiConfiguration.wepKeyVarNames[i], 723 config.wepKeys[i])) { 724 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 725 return false; 726 } 727 hasSetKey = true; 728 } 729 } 730 } 731 if (hasSetKey) { 732 if (!mWifiNative.setNetworkVariable( 733 netId, 734 WifiConfiguration.wepTxKeyIdxVarName, 735 Integer.toString(config.wepTxKeyIndex))) { 736 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 737 return false; 738 } 739 } 740 if (!mWifiNative.setNetworkVariable( 741 netId, 742 WifiConfiguration.priorityVarName, 743 Integer.toString(config.priority))) { 744 loge(config.SSID + ": failed to set priority: " + config.priority); 745 return false; 746 } 747 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 748 netId, 749 WifiConfiguration.hiddenSSIDVarName, 750 Integer.toString(config.hiddenSSID ? 1 : 0))) { 751 loge(config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID); 752 return false; 753 } 754 if (config.requirePMF && !mWifiNative.setNetworkVariable( 755 netId, 756 WifiConfiguration.pmfVarName, 757 Integer.toString(STORED_VALUE_FOR_REQUIRE_PMF))) { 758 loge(config.SSID + ": failed to set requirePMF: " + config.requirePMF); 759 return false; 760 } 761 if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable( 762 netId, 763 WifiConfiguration.updateIdentiferVarName, 764 config.updateIdentifier)) { 765 loge(config.SSID + ": failed to set updateIdentifier: " + config.updateIdentifier); 766 return false; 767 } 768 return true; 769 } 770 771 /** 772 * Update/Install keys for given enterprise network. 773 * 774 * @param config Config corresponding to the network. 775 * @param existingConfig Existing config corresponding to the network already stored in our 776 * database. This maybe null if it's a new network. 777 * @return true if successful, false otherwise. 778 */ 779 private boolean updateNetworkKeys(WifiConfiguration config, WifiConfiguration existingConfig) { 780 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 781 if (needsKeyStore(enterpriseConfig)) { 782 try { 783 /* config passed may include only fields being updated. 784 * In order to generate the key id, fetch uninitialized 785 * fields from the currently tracked configuration 786 */ 787 String keyId = config.getKeyIdForCredentials(existingConfig); 788 789 if (!installKeys(existingConfig != null 790 ? existingConfig.enterpriseConfig : null, enterpriseConfig, keyId)) { 791 loge(config.SSID + ": failed to install keys"); 792 return false; 793 } 794 } catch (IllegalStateException e) { 795 loge(config.SSID + " invalid config for key installation: " + e.getMessage()); 796 return false; 797 } 798 } 799 if (!enterpriseConfig.saveToSupplicant( 800 new SupplicantSaver(config.networkId, config.SSID))) { 801 removeKeys(enterpriseConfig); 802 return false; 803 } 804 return true; 805 } 806 807 /** 808 * Add or update a network configuration to wpa_supplicant. 809 * 810 * @param config Config corresponding to the network. 811 * @param existingConfig Existing config corresponding to the network saved in our database. 812 * @param addFastTransitionFlags Add the BSS fast transition(80211r) flags to the network. 813 * @return true if successful, false otherwise. 814 */ 815 public boolean addOrUpdateNetwork(WifiConfiguration config, WifiConfiguration existingConfig, 816 boolean addFastTransitionFlags) { 817 if (config == null) { 818 return false; 819 } 820 if (VDBG) localLog("addOrUpdateNetwork: " + config.networkId); 821 int netId = config.networkId; 822 boolean newNetwork = false; 823 /* 824 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 825 * network configuration. Otherwise, the networkId should 826 * refer to an existing configuration. 827 */ 828 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 829 newNetwork = true; 830 netId = mWifiNative.addNetwork(); 831 if (netId < 0) { 832 loge("Failed to add a network!"); 833 return false; 834 } else { 835 logi("addOrUpdateNetwork created netId=" + netId); 836 } 837 // Save the new network ID to the config 838 config.networkId = netId; 839 } 840 if (!saveNetwork(config, netId, addFastTransitionFlags)) { 841 if (newNetwork) { 842 mWifiNative.removeNetwork(netId); 843 loge("Failed to set a network variable, removed network: " + netId); 844 } 845 return false; 846 } 847 if (config.enterpriseConfig != null 848 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 849 return updateNetworkKeys(config, existingConfig); 850 } 851 // Stage the backup of the SettingsProvider package which backs this up 852 mBackupManagerProxy.notifyDataChanged(); 853 return true; 854 } 855 856 /** 857 * Remove the specified network and save config 858 * 859 * @param config Config corresponding to the network. 860 * @return {@code true} if it succeeds, {@code false} otherwise 861 */ 862 public boolean removeNetwork(WifiConfiguration config) { 863 if (config == null) { 864 return false; 865 } 866 if (VDBG) localLog("removeNetwork: " + config.networkId); 867 if (!mWifiNative.removeNetwork(config.networkId)) { 868 loge("Remove network in wpa_supplicant failed on " + config.networkId); 869 return false; 870 } 871 // Remove any associated keys 872 if (config.enterpriseConfig != null) { 873 removeKeys(config.enterpriseConfig); 874 } 875 // Stage the backup of the SettingsProvider package which backs this up 876 mBackupManagerProxy.notifyDataChanged(); 877 return true; 878 } 879 880 /** 881 * Select a network in wpa_supplicant. 882 * 883 * @param config Config corresponding to the network. 884 * @return true if successful, false otherwise. 885 */ 886 public boolean selectNetwork(WifiConfiguration config, Collection<WifiConfiguration> configs) { 887 if (config == null) { 888 return false; 889 } 890 if (VDBG) localLog("selectNetwork: " + config.networkId); 891 if (!mWifiNative.selectNetwork(config.networkId)) { 892 loge("Select network in wpa_supplicant failed on " + config.networkId); 893 return false; 894 } 895 config.status = Status.ENABLED; 896 markAllNetworksDisabledExcept(config.networkId, configs); 897 return true; 898 } 899 900 /** 901 * Disable a network in wpa_supplicant. 902 * 903 * @param config Config corresponding to the network. 904 * @return true if successful, false otherwise. 905 */ 906 boolean disableNetwork(WifiConfiguration config) { 907 if (config == null) { 908 return false; 909 } 910 if (VDBG) localLog("disableNetwork: " + config.networkId); 911 if (!mWifiNative.disableNetwork(config.networkId)) { 912 loge("Disable network in wpa_supplicant failed on " + config.networkId); 913 return false; 914 } 915 config.status = Status.DISABLED; 916 return true; 917 } 918 919 /** 920 * Set priority for a network in wpa_supplicant. 921 * 922 * @param config Config corresponding to the network. 923 * @return true if successful, false otherwise. 924 */ 925 public boolean setNetworkPriority(WifiConfiguration config, int priority) { 926 if (config == null) { 927 return false; 928 } 929 if (VDBG) localLog("setNetworkPriority: " + config.networkId); 930 if (!mWifiNative.setNetworkVariable(config.networkId, 931 WifiConfiguration.priorityVarName, Integer.toString(priority))) { 932 loge("Set priority of network in wpa_supplicant failed on " + config.networkId); 933 return false; 934 } 935 config.priority = priority; 936 return true; 937 } 938 939 /** 940 * Set SSID for a network in wpa_supplicant. 941 * 942 * @param config Config corresponding to the network. 943 * @return true if successful, false otherwise. 944 */ 945 public boolean setNetworkSSID(WifiConfiguration config, String ssid) { 946 if (config == null) { 947 return false; 948 } 949 if (VDBG) localLog("setNetworkSSID: " + config.networkId); 950 if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.ssidVarName, 951 encodeSSID(ssid))) { 952 loge("Set SSID of network in wpa_supplicant failed on " + config.networkId); 953 return false; 954 } 955 config.SSID = ssid; 956 return true; 957 } 958 959 /** 960 * Set BSSID for a network in wpa_supplicant from network selection. 961 * 962 * @param config Config corresponding to the network. 963 * @param bssid BSSID to be set. 964 * @return true if successful, false otherwise. 965 */ 966 public boolean setNetworkBSSID(WifiConfiguration config, String bssid) { 967 // Sanity check the config is valid 968 if (config == null 969 || (config.networkId == WifiConfiguration.INVALID_NETWORK_ID 970 && config.SSID == null)) { 971 return false; 972 } 973 if (VDBG) localLog("setNetworkBSSID: " + config.networkId); 974 if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.bssidVarName, 975 bssid)) { 976 loge("Set BSSID of network in wpa_supplicant failed on " + config.networkId); 977 return false; 978 } 979 config.getNetworkSelectionStatus().setNetworkSelectionBSSID(bssid); 980 return true; 981 } 982 983 /** 984 * Enable/Disable HS20 parameter in wpa_supplicant. 985 * 986 * @param enable Enable/Disable the parameter. 987 */ 988 public void enableHS20(boolean enable) { 989 mWifiNative.setHs20(enable); 990 } 991 992 /** 993 * Disables all the networks in the provided list in wpa_supplicant. 994 * 995 * @param configs Collection of configs which needs to be enabled. 996 * @return true if successful, false otherwise. 997 */ 998 public boolean disableAllNetworks(Collection<WifiConfiguration> configs) { 999 if (VDBG) localLog("disableAllNetworks"); 1000 boolean networkDisabled = false; 1001 for (WifiConfiguration enabled : configs) { 1002 if (disableNetwork(enabled)) { 1003 networkDisabled = true; 1004 } 1005 } 1006 saveConfig(); 1007 return networkDisabled; 1008 } 1009 1010 /** 1011 * Save the current configuration to wpa_supplicant.conf. 1012 */ 1013 public boolean saveConfig() { 1014 return mWifiNative.saveConfig(); 1015 } 1016 1017 /** 1018 * Read network variables from wpa_supplicant.conf. 1019 * 1020 * @param key The parameter to be parsed. 1021 * @return Map of corresponding configKey to the value of the param requested. 1022 */ 1023 public Map<String, String> readNetworkVariablesFromSupplicantFile(String key) { 1024 Map<String, String> result = new HashMap<>(); 1025 BufferedReader reader = null; 1026 try { 1027 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 1028 result = readNetworkVariablesFromReader(reader, key); 1029 } catch (FileNotFoundException e) { 1030 if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 1031 } catch (IOException e) { 1032 if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 1033 } finally { 1034 try { 1035 if (reader != null) { 1036 reader.close(); 1037 } 1038 } catch (IOException e) { 1039 if (VDBG) { 1040 loge("Could not close reader for " + SUPPLICANT_CONFIG_FILE + ", " + e); 1041 } 1042 } 1043 } 1044 return result; 1045 } 1046 1047 /** 1048 * Read network variables from a given reader. This method is separate from 1049 * readNetworkVariablesFromSupplicantFile() for testing. 1050 * 1051 * @param reader The reader to read the network variables from. 1052 * @param key The parameter to be parsed. 1053 * @return Map of corresponding configKey to the value of the param requested. 1054 */ 1055 public Map<String, String> readNetworkVariablesFromReader(BufferedReader reader, String key) 1056 throws IOException { 1057 Map<String, String> result = new HashMap<>(); 1058 if (VDBG) localLog("readNetworkVariablesFromReader key=" + key); 1059 boolean found = false; 1060 String configKey = null; 1061 String value = null; 1062 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1063 if (line.matches("[ \\t]*network=\\{")) { 1064 found = true; 1065 configKey = null; 1066 value = null; 1067 } else if (line.matches("[ \\t]*\\}")) { 1068 found = false; 1069 configKey = null; 1070 value = null; 1071 } 1072 if (found) { 1073 String trimmedLine = line.trim(); 1074 if (trimmedLine.startsWith(ID_STRING_VAR_NAME + "=")) { 1075 try { 1076 // Trim the quotes wrapping the id_str value. 1077 final String encodedExtras = trimmedLine.substring( 1078 8, trimmedLine.length() -1); 1079 final JSONObject json = 1080 new JSONObject(URLDecoder.decode(encodedExtras, "UTF-8")); 1081 if (json.has(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY)) { 1082 final Object configKeyFromJson = 1083 json.get(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY); 1084 if (configKeyFromJson instanceof String) { 1085 configKey = (String) configKeyFromJson; 1086 } 1087 } 1088 } catch (JSONException e) { 1089 if (VDBG) { 1090 loge("Could not get "+ WifiConfigStore.ID_STRING_KEY_CONFIG_KEY 1091 + ", " + e); 1092 } 1093 } 1094 } 1095 if (trimmedLine.startsWith(key + "=")) { 1096 value = trimmedLine.substring(key.length() + 1); 1097 } 1098 if (configKey != null && value != null) { 1099 result.put(configKey, value); 1100 } 1101 } 1102 } 1103 return result; 1104 } 1105 1106 /** 1107 * Resets all sim networks from the provided network list. 1108 * 1109 * @param configs List of all the networks. 1110 */ 1111 public void resetSimNetworks(Collection<WifiConfiguration> configs) { 1112 if (VDBG) localLog("resetSimNetworks"); 1113 for (WifiConfiguration config : configs) { 1114 if (TelephonyUtil.isSimConfig(config)) { 1115 String currentIdentity = TelephonyUtil.getSimIdentity(mContext, 1116 config.enterpriseConfig.getEapMethod()); 1117 String supplicantIdentity = 1118 mWifiNative.getNetworkVariable(config.networkId, "identity"); 1119 if(supplicantIdentity != null) { 1120 supplicantIdentity = removeDoubleQuotes(supplicantIdentity); 1121 } 1122 if (currentIdentity == null || !currentIdentity.equals(supplicantIdentity)) { 1123 // Identity differs so update the identity 1124 mWifiNative.setNetworkVariable(config.networkId, 1125 WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.EMPTY_VALUE); 1126 // This configuration may have cached Pseudonym IDs; lets remove them 1127 mWifiNative.setNetworkVariable(config.networkId, 1128 WifiEnterpriseConfig.ANON_IDENTITY_KEY, 1129 WifiEnterpriseConfig.EMPTY_VALUE); 1130 } 1131 // Update the loaded config 1132 config.enterpriseConfig.setIdentity(currentIdentity); 1133 config.enterpriseConfig.setAnonymousIdentity(""); 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Clear BSSID blacklist in wpa_supplicant. 1140 */ 1141 public void clearBssidBlacklist() { 1142 if (VDBG) localLog("clearBlacklist"); 1143 mBssidBlacklist.clear(); 1144 mWifiNative.clearBlacklist(); 1145 mWifiNative.setBssidBlacklist(null); 1146 } 1147 1148 /** 1149 * Add a BSSID to the blacklist. 1150 * 1151 * @param bssid bssid to be added. 1152 */ 1153 public void blackListBssid(String bssid) { 1154 if (bssid == null) { 1155 return; 1156 } 1157 if (VDBG) localLog("blackListBssid: " + bssid); 1158 mBssidBlacklist.add(bssid); 1159 // Blacklist at wpa_supplicant 1160 mWifiNative.addToBlacklist(bssid); 1161 // Blacklist at firmware 1162 String[] list = mBssidBlacklist.toArray(new String[mBssidBlacklist.size()]); 1163 mWifiNative.setBssidBlacklist(list); 1164 } 1165 1166 /** 1167 * Checks if the provided bssid is blacklisted or not. 1168 * 1169 * @param bssid bssid to be checked. 1170 * @return true if present, false otherwise. 1171 */ 1172 public boolean isBssidBlacklisted(String bssid) { 1173 return mBssidBlacklist.contains(bssid); 1174 } 1175 1176 /* Mark all networks except specified netId as disabled */ 1177 private void markAllNetworksDisabledExcept(int netId, Collection<WifiConfiguration> configs) { 1178 for (WifiConfiguration config : configs) { 1179 if (config != null && config.networkId != netId) { 1180 if (config.status != Status.DISABLED) { 1181 config.status = Status.DISABLED; 1182 } 1183 } 1184 } 1185 } 1186 1187 private void markAllNetworksDisabled(Collection<WifiConfiguration> configs) { 1188 markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, configs); 1189 } 1190 1191 /** 1192 * Start WPS pin method configuration with pin obtained 1193 * from the access point 1194 * 1195 * @param config WPS configuration 1196 * @return Wps result containing status and pin 1197 */ 1198 public WpsResult startWpsWithPinFromAccessPoint(WpsInfo config, 1199 Collection<WifiConfiguration> configs) { 1200 WpsResult result = new WpsResult(); 1201 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 1202 /* WPS leaves all networks disabled */ 1203 markAllNetworksDisabled(configs); 1204 result.status = WpsResult.Status.SUCCESS; 1205 } else { 1206 loge("Failed to start WPS pin method configuration"); 1207 result.status = WpsResult.Status.FAILURE; 1208 } 1209 return result; 1210 } 1211 1212 /** 1213 * Start WPS pin method configuration with obtained 1214 * from the device 1215 * 1216 * @return WpsResult indicating status and pin 1217 */ 1218 public WpsResult startWpsWithPinFromDevice(WpsInfo config, 1219 Collection<WifiConfiguration> configs) { 1220 WpsResult result = new WpsResult(); 1221 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 1222 /* WPS leaves all networks disabled */ 1223 if (!TextUtils.isEmpty(result.pin)) { 1224 markAllNetworksDisabled(configs); 1225 result.status = WpsResult.Status.SUCCESS; 1226 } else { 1227 loge("Failed to start WPS pin method configuration"); 1228 result.status = WpsResult.Status.FAILURE; 1229 } 1230 return result; 1231 } 1232 1233 /** 1234 * Start WPS push button configuration 1235 * 1236 * @param config WPS configuration 1237 * @return WpsResult indicating status and pin 1238 */ 1239 public WpsResult startWpsPbc(WpsInfo config, 1240 Collection<WifiConfiguration> configs) { 1241 WpsResult result = new WpsResult(); 1242 if (mWifiNative.startWpsPbc(config.BSSID)) { 1243 /* WPS leaves all networks disabled */ 1244 markAllNetworksDisabled(configs); 1245 result.status = WpsResult.Status.SUCCESS; 1246 } else { 1247 loge("Failed to start WPS push button configuration"); 1248 result.status = WpsResult.Status.FAILURE; 1249 } 1250 return result; 1251 } 1252 1253 protected void logd(String s) { 1254 Log.d(TAG, s); 1255 } 1256 1257 protected void logi(String s) { 1258 Log.i(TAG, s); 1259 } 1260 1261 protected void loge(String s) { 1262 loge(s, false); 1263 } 1264 1265 protected void loge(String s, boolean stack) { 1266 if (stack) { 1267 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 1268 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 1269 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 1270 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 1271 } else { 1272 Log.e(TAG, s); 1273 } 1274 } 1275 1276 protected void log(String s) { 1277 Log.d(TAG, s); 1278 } 1279 1280 private void localLog(String s) { 1281 if (mLocalLog != null) { 1282 mLocalLog.log(TAG + ": " + s); 1283 } 1284 } 1285 1286 private void localLogAndLogcat(String s) { 1287 localLog(s); 1288 Log.d(TAG, s); 1289 } 1290 1291 private class SupplicantSaver implements WifiEnterpriseConfig.SupplicantSaver { 1292 private final int mNetId; 1293 private final String mSetterSSID; 1294 1295 SupplicantSaver(int netId, String setterSSID) { 1296 mNetId = netId; 1297 mSetterSSID = setterSSID; 1298 } 1299 1300 @Override 1301 public boolean saveValue(String key, String value) { 1302 if (key.equals(WifiEnterpriseConfig.PASSWORD_KEY) 1303 && value != null && value.equals("*")) { 1304 // No need to try to set an obfuscated password, which will fail 1305 return true; 1306 } 1307 if (key.equals(WifiEnterpriseConfig.REALM_KEY) 1308 || key.equals(WifiEnterpriseConfig.PLMN_KEY)) { 1309 // No need to save realm or PLMN in supplicant 1310 return true; 1311 } 1312 // TODO: We need a way to clear values in wpa_supplicant as opposed to 1313 // mapping unset values to empty strings. 1314 if (value == null) { 1315 value = "\"\""; 1316 } 1317 if (!mWifiNative.setNetworkVariable(mNetId, key, value)) { 1318 loge(mSetterSSID + ": failed to set " + key + ": " + value); 1319 return false; 1320 } 1321 return true; 1322 } 1323 } 1324 1325 private class SupplicantLoader implements WifiEnterpriseConfig.SupplicantLoader { 1326 private final int mNetId; 1327 1328 SupplicantLoader(int netId) { 1329 mNetId = netId; 1330 } 1331 1332 @Override 1333 public String loadValue(String key) { 1334 String value = mWifiNative.getNetworkVariable(mNetId, key); 1335 if (!TextUtils.isEmpty(value)) { 1336 if (!enterpriseConfigKeyShouldBeQuoted(key)) { 1337 value = removeDoubleQuotes(value); 1338 } 1339 return value; 1340 } else { 1341 return null; 1342 } 1343 } 1344 1345 /** 1346 * Returns true if a particular config key needs to be quoted when passed to the supplicant. 1347 */ 1348 private boolean enterpriseConfigKeyShouldBeQuoted(String key) { 1349 switch (key) { 1350 case WifiEnterpriseConfig.EAP_KEY: 1351 case WifiEnterpriseConfig.ENGINE_KEY: 1352 return false; 1353 default: 1354 return true; 1355 } 1356 } 1357 } 1358 1359 // TODO(rpius): Remove this. 1360 private class WpaConfigFileObserver extends FileObserver { 1361 1362 WpaConfigFileObserver() { 1363 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 1364 } 1365 1366 @Override 1367 public void onEvent(int event, String path) { 1368 if (event == CLOSE_WRITE) { 1369 File file = new File(SUPPLICANT_CONFIG_FILE); 1370 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 1371 } 1372 } 1373 } 1374} 1375