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