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 17 package com.android.server.wifi; 18 19 import android.content.Context; 20 import android.net.IpConfiguration.IpAssignment; 21 import android.net.IpConfiguration.ProxySettings; 22 import android.net.wifi.WifiConfiguration; 23 import android.net.wifi.WifiConfiguration.Status; 24 import android.net.wifi.WifiEnterpriseConfig; 25 import android.net.wifi.WifiSsid; 26 import android.net.wifi.WpsInfo; 27 import android.net.wifi.WpsResult; 28 import android.os.FileObserver; 29 import android.os.Process; 30 import android.security.Credentials; 31 import android.security.KeyChain; 32 import android.security.KeyStore; 33 import android.text.TextUtils; 34 import android.util.ArraySet; 35 import android.util.LocalLog; 36 import android.util.Log; 37 import android.util.SparseArray; 38 39 import com.android.server.wifi.hotspot2.Utils; 40 import com.android.server.wifi.util.TelephonyUtil; 41 42 import org.json.JSONException; 43 import org.json.JSONObject; 44 45 import java.io.BufferedReader; 46 import java.io.File; 47 import java.io.FileNotFoundException; 48 import java.io.FileReader; 49 import java.io.IOException; 50 import java.net.URLDecoder; 51 import java.nio.charset.StandardCharsets; 52 import java.security.PrivateKey; 53 import java.security.cert.Certificate; 54 import java.security.cert.CertificateException; 55 import java.security.cert.X509Certificate; 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.BitSet; 59 import java.util.Collection; 60 import java.util.HashMap; 61 import java.util.HashSet; 62 import java.util.List; 63 import java.util.Map; 64 import 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 */ 75 public 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 345 readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms, 346 WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings); 347 348 readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers, 349 WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings); 350 351 readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers, 352 WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings); 353 354 if (config.enterpriseConfig == null) { 355 config.enterpriseConfig = new WifiEnterpriseConfig(); 356 } 357 config.enterpriseConfig.loadFromSupplicant(new SupplicantLoader(netId)); 358 } 359 360 /** 361 * Load all the configured networks from wpa_supplicant. 362 * 363 * @param configs Map of configuration key to configuration objects corresponding to all 364 * the networks. 365 * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf 366 * @return Max priority of all the configs. 367 */ 368 public int loadNetworks(Map<String, WifiConfiguration> configs, 369 SparseArray<Map<String, String>> networkExtras) { 370 int lastPriority = 0; 371 int last_id = -1; 372 boolean done = false; 373 while (!done) { 374 String listStr = mWifiNative.listNetworks(last_id); 375 if (listStr == null) { 376 return lastPriority; 377 } 378 String[] lines = listStr.split("\n"); 379 if (mShowNetworks) { 380 localLog("loadNetworks: "); 381 for (String net : lines) { 382 localLog(net); 383 } 384 } 385 // Skip the first line, which is a header 386 for (int i = 1; i < lines.length; i++) { 387 String[] result = lines[i].split("\t"); 388 // network-id | ssid | bssid | flags 389 WifiConfiguration config = new WifiConfiguration(); 390 try { 391 config.networkId = Integer.parseInt(result[0]); 392 last_id = config.networkId; 393 } catch (NumberFormatException e) { 394 loge("Failed to read network-id '" + result[0] + "'"); 395 continue; 396 } 397 // Ignore the supplicant status, start all networks disabled. 398 config.status = WifiConfiguration.Status.DISABLED; 399 readNetworkVariables(config); 400 // Parse the serialized JSON dictionary in ID_STRING_VAR_NAME once and cache the 401 // result for efficiency. 402 Map<String, String> extras = mWifiNative.getNetworkExtra(config.networkId, 403 ID_STRING_VAR_NAME); 404 if (extras == null) { 405 extras = new HashMap<String, String>(); 406 // If ID_STRING_VAR_NAME did not contain a dictionary, assume that it contains 407 // just a quoted FQDN. This is the legacy format that was used in Marshmallow. 408 final String fqdn = Utils.unquote(mWifiNative.getNetworkVariable( 409 config.networkId, ID_STRING_VAR_NAME)); 410 if (fqdn != null) { 411 extras.put(ID_STRING_KEY_FQDN, fqdn); 412 config.FQDN = fqdn; 413 // Mark the configuration as a Hotspot 2.0 network. 414 config.providerFriendlyName = ""; 415 } 416 } 417 networkExtras.put(config.networkId, extras); 418 419 if (config.priority > lastPriority) { 420 lastPriority = config.priority; 421 } 422 config.setIpAssignment(IpAssignment.DHCP); 423 config.setProxySettings(ProxySettings.NONE); 424 if (!WifiServiceImpl.isValid(config)) { 425 if (mShowNetworks) { 426 localLog("Ignoring network " + config.networkId + " because configuration " 427 + "loaded from wpa_supplicant.conf is not valid."); 428 } 429 continue; 430 } 431 // The configKey is explicitly stored in wpa_supplicant.conf, because config does 432 // not contain sufficient information to compute it at this point. 433 String configKey = extras.get(ID_STRING_KEY_CONFIG_KEY); 434 if (configKey == null) { 435 // Handle the legacy case where the configKey is not stored in 436 // wpa_supplicant.conf but can be computed straight away. 437 // Force an update of this legacy network configuration by writing 438 // the configKey for this network into wpa_supplicant.conf. 439 configKey = config.configKey(); 440 saveNetworkMetadata(config); 441 } 442 final WifiConfiguration duplicateConfig = configs.put(configKey, config); 443 if (duplicateConfig != null) { 444 // The network is already known. Overwrite the duplicate entry. 445 if (mShowNetworks) { 446 localLog("Replacing duplicate network " + duplicateConfig.networkId 447 + " with " + config.networkId + "."); 448 } 449 // This can happen after the user manually connected to an AP and tried to use 450 // WPS to connect the AP later. In this case, the supplicant will create a new 451 // network for the AP although there is an existing network already. 452 mWifiNative.removeNetwork(duplicateConfig.networkId); 453 } 454 } 455 done = (lines.length == 1); 456 } 457 return lastPriority; 458 } 459 460 /** 461 * Install keys for given enterprise network. 462 * 463 * @param existingConfig Existing config corresponding to the network already stored in our 464 * database. This maybe null if it's a new network. 465 * @param config Config corresponding to the network. 466 * @return true if successful, false otherwise. 467 */ 468 private boolean installKeys(WifiEnterpriseConfig existingConfig, WifiEnterpriseConfig config, 469 String name) { 470 boolean ret = true; 471 String privKeyName = Credentials.USER_PRIVATE_KEY + name; 472 String userCertName = Credentials.USER_CERTIFICATE + name; 473 if (config.getClientCertificate() != null) { 474 byte[] privKeyData = config.getClientPrivateKey().getEncoded(); 475 if (DBG) { 476 if (isHardwareBackedKey(config.getClientPrivateKey())) { 477 Log.d(TAG, "importing keys " + name + " in hardware backed store"); 478 } else { 479 Log.d(TAG, "importing keys " + name + " in software backed store"); 480 } 481 } 482 ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, 483 KeyStore.FLAG_NONE); 484 485 if (!ret) { 486 return ret; 487 } 488 489 ret = putCertInKeyStore(userCertName, config.getClientCertificate()); 490 if (!ret) { 491 // Remove private key installed 492 mKeyStore.delete(privKeyName, Process.WIFI_UID); 493 return ret; 494 } 495 } 496 497 X509Certificate[] caCertificates = config.getCaCertificates(); 498 Set<String> oldCaCertificatesToRemove = new ArraySet<String>(); 499 if (existingConfig != null && existingConfig.getCaCertificateAliases() != null) { 500 oldCaCertificatesToRemove.addAll( 501 Arrays.asList(existingConfig.getCaCertificateAliases())); 502 } 503 List<String> caCertificateAliases = null; 504 if (caCertificates != null) { 505 caCertificateAliases = new ArrayList<String>(); 506 for (int i = 0; i < caCertificates.length; i++) { 507 String alias = caCertificates.length == 1 ? name 508 : String.format("%s_%d", name, i); 509 510 oldCaCertificatesToRemove.remove(alias); 511 ret = putCertInKeyStore(Credentials.CA_CERTIFICATE + alias, caCertificates[i]); 512 if (!ret) { 513 // Remove client key+cert 514 if (config.getClientCertificate() != null) { 515 mKeyStore.delete(privKeyName, Process.WIFI_UID); 516 mKeyStore.delete(userCertName, Process.WIFI_UID); 517 } 518 // Remove added CA certs. 519 for (String addedAlias : caCertificateAliases) { 520 mKeyStore.delete(Credentials.CA_CERTIFICATE + addedAlias, Process.WIFI_UID); 521 } 522 return ret; 523 } else { 524 caCertificateAliases.add(alias); 525 } 526 } 527 } 528 // Remove old CA certs. 529 for (String oldAlias : oldCaCertificatesToRemove) { 530 mKeyStore.delete(Credentials.CA_CERTIFICATE + oldAlias, Process.WIFI_UID); 531 } 532 // Set alias names 533 if (config.getClientCertificate() != null) { 534 config.setClientCertificateAlias(name); 535 config.resetClientKeyEntry(); 536 } 537 538 if (caCertificates != null) { 539 config.setCaCertificateAliases( 540 caCertificateAliases.toArray(new String[caCertificateAliases.size()])); 541 config.resetCaCertificate(); 542 } 543 return ret; 544 } 545 546 private boolean putCertInKeyStore(String name, Certificate cert) { 547 try { 548 byte[] certData = Credentials.convertToPem(cert); 549 if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); 550 return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); 551 552 } catch (IOException e1) { 553 return false; 554 } catch (CertificateException e2) { 555 return false; 556 } 557 } 558 559 /** 560 * Remove enterprise keys from the network config. 561 * 562 * @param config Config corresponding to the network. 563 */ 564 private void removeKeys(WifiEnterpriseConfig config) { 565 String client = config.getClientCertificateAlias(); 566 // a valid client certificate is configured 567 if (!TextUtils.isEmpty(client)) { 568 if (DBG) Log.d(TAG, "removing client private key and user cert"); 569 mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); 570 mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); 571 } 572 573 String[] aliases = config.getCaCertificateAliases(); 574 // a valid ca certificate is configured 575 if (aliases != null) { 576 for (String ca : aliases) { 577 if (!TextUtils.isEmpty(ca)) { 578 if (DBG) Log.d(TAG, "removing CA cert: " + ca); 579 mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); 580 } 581 } 582 } 583 } 584 585 /** 586 * Update the network metadata info stored in wpa_supplicant network extra field. 587 * @param config Config corresponding to the network. 588 * @return true if successful, false otherwise. 589 */ 590 public boolean saveNetworkMetadata(WifiConfiguration config) { 591 final Map<String, String> metadata = new HashMap<String, String>(); 592 if (config.isPasspoint()) { 593 metadata.put(ID_STRING_KEY_FQDN, config.FQDN); 594 } 595 metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey()); 596 metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid)); 597 if (!mWifiNative.setNetworkExtra(config.networkId, ID_STRING_VAR_NAME, metadata)) { 598 loge("failed to set id_str: " + metadata.toString()); 599 return false; 600 } 601 return true; 602 } 603 604 /** 605 * Save an entire network configuration to wpa_supplicant. 606 * 607 * @param config Config corresponding to the network. 608 * @param netId Net Id of the network. 609 * @return true if successful, false otherwise. 610 */ 611 private boolean saveNetwork(WifiConfiguration config, int netId) { 612 if (config == null) { 613 return false; 614 } 615 if (VDBG) localLog("saveNetwork: " + netId); 616 if (config.SSID != null && !mWifiNative.setNetworkVariable( 617 netId, 618 WifiConfiguration.ssidVarName, 619 encodeSSID(config.SSID))) { 620 loge("failed to set SSID: " + config.SSID); 621 return false; 622 } 623 if (!saveNetworkMetadata(config)) { 624 return false; 625 } 626 //set selected BSSID to supplicant 627 if (config.getNetworkSelectionStatus().getNetworkSelectionBSSID() != null) { 628 String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID(); 629 if (!mWifiNative.setNetworkVariable(netId, WifiConfiguration.bssidVarName, bssid)) { 630 loge("failed to set BSSID: " + bssid); 631 return false; 632 } 633 } 634 String allowedKeyManagementString = 635 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 636 if (config.allowedKeyManagement.cardinality() != 0 && !mWifiNative.setNetworkVariable( 637 netId, 638 WifiConfiguration.KeyMgmt.varName, 639 allowedKeyManagementString)) { 640 loge("failed to set key_mgmt: " + allowedKeyManagementString); 641 return false; 642 } 643 String allowedProtocolsString = 644 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 645 if (config.allowedProtocols.cardinality() != 0 && !mWifiNative.setNetworkVariable( 646 netId, 647 WifiConfiguration.Protocol.varName, 648 allowedProtocolsString)) { 649 loge("failed to set proto: " + allowedProtocolsString); 650 return false; 651 } 652 String allowedAuthAlgorithmsString = 653 makeString(config.allowedAuthAlgorithms, 654 WifiConfiguration.AuthAlgorithm.strings); 655 if (config.allowedAuthAlgorithms.cardinality() != 0 && !mWifiNative.setNetworkVariable( 656 netId, 657 WifiConfiguration.AuthAlgorithm.varName, 658 allowedAuthAlgorithmsString)) { 659 loge("failed to set auth_alg: " + allowedAuthAlgorithmsString); 660 return false; 661 } 662 String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers, 663 WifiConfiguration.PairwiseCipher.strings); 664 if (config.allowedPairwiseCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable( 665 netId, 666 WifiConfiguration.PairwiseCipher.varName, 667 allowedPairwiseCiphersString)) { 668 loge("failed to set pairwise: " + allowedPairwiseCiphersString); 669 return false; 670 } 671 // Make sure that the string "GTK_NOT_USED" is /not/ transformed - wpa_supplicant 672 // uses this literal value and not the 'dashed' version. 673 String allowedGroupCiphersString = 674 makeStringWithException(config.allowedGroupCiphers, 675 WifiConfiguration.GroupCipher.strings, 676 WifiConfiguration.GroupCipher 677 .strings[WifiConfiguration.GroupCipher.GTK_NOT_USED]); 678 if (config.allowedGroupCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable( 679 netId, 680 WifiConfiguration.GroupCipher.varName, 681 allowedGroupCiphersString)) { 682 loge("failed to set group: " + allowedGroupCiphersString); 683 return false; 684 } 685 // Prevent client screw-up by passing in a WifiConfiguration we gave it 686 // by preventing "*" as a key. 687 if (config.preSharedKey != null && !config.preSharedKey.equals("*") 688 && !mWifiNative.setNetworkVariable( 689 netId, 690 WifiConfiguration.pskVarName, 691 config.preSharedKey)) { 692 loge("failed to set psk"); 693 return false; 694 } 695 boolean hasSetKey = false; 696 if (config.wepKeys != null) { 697 for (int i = 0; i < config.wepKeys.length; i++) { 698 // Prevent client screw-up by passing in a WifiConfiguration we gave it 699 // by preventing "*" as a key. 700 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 701 if (!mWifiNative.setNetworkVariable( 702 netId, 703 WifiConfiguration.wepKeyVarNames[i], 704 config.wepKeys[i])) { 705 loge("failed to set wep_key" + i + ": " + config.wepKeys[i]); 706 return false; 707 } 708 hasSetKey = true; 709 } 710 } 711 } 712 if (hasSetKey) { 713 if (!mWifiNative.setNetworkVariable( 714 netId, 715 WifiConfiguration.wepTxKeyIdxVarName, 716 Integer.toString(config.wepTxKeyIndex))) { 717 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex); 718 return false; 719 } 720 } 721 if (!mWifiNative.setNetworkVariable( 722 netId, 723 WifiConfiguration.priorityVarName, 724 Integer.toString(config.priority))) { 725 loge(config.SSID + ": failed to set priority: " + config.priority); 726 return false; 727 } 728 if (config.hiddenSSID && !mWifiNative.setNetworkVariable( 729 netId, 730 WifiConfiguration.hiddenSSIDVarName, 731 Integer.toString(config.hiddenSSID ? 1 : 0))) { 732 loge(config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID); 733 return false; 734 } 735 if (config.requirePMF && !mWifiNative.setNetworkVariable( 736 netId, 737 WifiConfiguration.pmfVarName, 738 Integer.toString(STORED_VALUE_FOR_REQUIRE_PMF))) { 739 loge(config.SSID + ": failed to set requirePMF: " + config.requirePMF); 740 return false; 741 } 742 if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable( 743 netId, 744 WifiConfiguration.updateIdentiferVarName, 745 config.updateIdentifier)) { 746 loge(config.SSID + ": failed to set updateIdentifier: " + config.updateIdentifier); 747 return false; 748 } 749 return true; 750 } 751 752 /** 753 * Update/Install keys for given enterprise network. 754 * 755 * @param config Config corresponding to the network. 756 * @param existingConfig Existing config corresponding to the network already stored in our 757 * database. This maybe null if it's a new network. 758 * @return true if successful, false otherwise. 759 */ 760 private boolean updateNetworkKeys(WifiConfiguration config, WifiConfiguration existingConfig) { 761 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 762 if (needsKeyStore(enterpriseConfig)) { 763 try { 764 /* config passed may include only fields being updated. 765 * In order to generate the key id, fetch uninitialized 766 * fields from the currently tracked configuration 767 */ 768 String keyId = config.getKeyIdForCredentials(existingConfig); 769 770 if (!installKeys(existingConfig != null 771 ? existingConfig.enterpriseConfig : null, enterpriseConfig, keyId)) { 772 loge(config.SSID + ": failed to install keys"); 773 return false; 774 } 775 } catch (IllegalStateException e) { 776 loge(config.SSID + " invalid config for key installation: " + e.getMessage()); 777 return false; 778 } 779 } 780 if (!enterpriseConfig.saveToSupplicant( 781 new SupplicantSaver(config.networkId, config.SSID))) { 782 removeKeys(enterpriseConfig); 783 return false; 784 } 785 return true; 786 } 787 788 /** 789 * Add or update a network configuration to wpa_supplicant. 790 * 791 * @param config Config corresponding to the network. 792 * @param existingConfig Existing config corresponding to the network saved in our database. 793 * @return true if successful, false otherwise. 794 */ 795 public boolean addOrUpdateNetwork(WifiConfiguration config, WifiConfiguration existingConfig) { 796 if (config == null) { 797 return false; 798 } 799 if (VDBG) localLog("addOrUpdateNetwork: " + config.networkId); 800 int netId = config.networkId; 801 boolean newNetwork = false; 802 /* 803 * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty 804 * network configuration. Otherwise, the networkId should 805 * refer to an existing configuration. 806 */ 807 if (netId == WifiConfiguration.INVALID_NETWORK_ID) { 808 newNetwork = true; 809 netId = mWifiNative.addNetwork(); 810 if (netId < 0) { 811 loge("Failed to add a network!"); 812 return false; 813 } else { 814 logi("addOrUpdateNetwork created netId=" + netId); 815 } 816 // Save the new network ID to the config 817 config.networkId = netId; 818 } 819 if (!saveNetwork(config, netId)) { 820 if (newNetwork) { 821 mWifiNative.removeNetwork(netId); 822 loge("Failed to set a network variable, removed network: " + netId); 823 } 824 return false; 825 } 826 if (config.enterpriseConfig != null 827 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 828 return updateNetworkKeys(config, existingConfig); 829 } 830 // Stage the backup of the SettingsProvider package which backs this up 831 mBackupManagerProxy.notifyDataChanged(); 832 return true; 833 } 834 835 /** 836 * Remove the specified network and save config 837 * 838 * @param config Config corresponding to the network. 839 * @return {@code true} if it succeeds, {@code false} otherwise 840 */ 841 public boolean removeNetwork(WifiConfiguration config) { 842 if (config == null) { 843 return false; 844 } 845 if (VDBG) localLog("removeNetwork: " + config.networkId); 846 if (!mWifiNative.removeNetwork(config.networkId)) { 847 loge("Remove network in wpa_supplicant failed on " + config.networkId); 848 return false; 849 } 850 // Remove any associated keys 851 if (config.enterpriseConfig != null) { 852 removeKeys(config.enterpriseConfig); 853 } 854 // Stage the backup of the SettingsProvider package which backs this up 855 mBackupManagerProxy.notifyDataChanged(); 856 return true; 857 } 858 859 /** 860 * Select a network in wpa_supplicant. 861 * 862 * @param config Config corresponding to the network. 863 * @return true if successful, false otherwise. 864 */ 865 public boolean selectNetwork(WifiConfiguration config, Collection<WifiConfiguration> configs) { 866 if (config == null) { 867 return false; 868 } 869 if (VDBG) localLog("selectNetwork: " + config.networkId); 870 if (!mWifiNative.selectNetwork(config.networkId)) { 871 loge("Select network in wpa_supplicant failed on " + config.networkId); 872 return false; 873 } 874 config.status = Status.ENABLED; 875 markAllNetworksDisabledExcept(config.networkId, configs); 876 return true; 877 } 878 879 /** 880 * Disable a network in wpa_supplicant. 881 * 882 * @param config Config corresponding to the network. 883 * @return true if successful, false otherwise. 884 */ 885 boolean disableNetwork(WifiConfiguration config) { 886 if (config == null) { 887 return false; 888 } 889 if (VDBG) localLog("disableNetwork: " + config.networkId); 890 if (!mWifiNative.disableNetwork(config.networkId)) { 891 loge("Disable network in wpa_supplicant failed on " + config.networkId); 892 return false; 893 } 894 config.status = Status.DISABLED; 895 return true; 896 } 897 898 /** 899 * Set priority for a network in wpa_supplicant. 900 * 901 * @param config Config corresponding to the network. 902 * @return true if successful, false otherwise. 903 */ 904 public boolean setNetworkPriority(WifiConfiguration config, int priority) { 905 if (config == null) { 906 return false; 907 } 908 if (VDBG) localLog("setNetworkPriority: " + config.networkId); 909 if (!mWifiNative.setNetworkVariable(config.networkId, 910 WifiConfiguration.priorityVarName, Integer.toString(priority))) { 911 loge("Set priority of network in wpa_supplicant failed on " + config.networkId); 912 return false; 913 } 914 config.priority = priority; 915 return true; 916 } 917 918 /** 919 * Set SSID for a network in wpa_supplicant. 920 * 921 * @param config Config corresponding to the network. 922 * @return true if successful, false otherwise. 923 */ 924 public boolean setNetworkSSID(WifiConfiguration config, String ssid) { 925 if (config == null) { 926 return false; 927 } 928 if (VDBG) localLog("setNetworkSSID: " + config.networkId); 929 if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.ssidVarName, 930 encodeSSID(ssid))) { 931 loge("Set SSID of network in wpa_supplicant failed on " + config.networkId); 932 return false; 933 } 934 config.SSID = ssid; 935 return true; 936 } 937 938 /** 939 * Set BSSID for a network in wpa_supplicant from network selection. 940 * 941 * @param config Config corresponding to the network. 942 * @param bssid BSSID to be set. 943 * @return true if successful, false otherwise. 944 */ 945 public boolean setNetworkBSSID(WifiConfiguration config, String bssid) { 946 // Sanity check the config is valid 947 if (config == null 948 || (config.networkId == WifiConfiguration.INVALID_NETWORK_ID 949 && config.SSID == null)) { 950 return false; 951 } 952 if (VDBG) localLog("setNetworkBSSID: " + config.networkId); 953 if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.bssidVarName, 954 bssid)) { 955 loge("Set BSSID of network in wpa_supplicant failed on " + config.networkId); 956 return false; 957 } 958 config.getNetworkSelectionStatus().setNetworkSelectionBSSID(bssid); 959 return true; 960 } 961 962 /** 963 * Enable/Disable HS20 parameter in wpa_supplicant. 964 * 965 * @param enable Enable/Disable the parameter. 966 */ 967 public void enableHS20(boolean enable) { 968 mWifiNative.setHs20(enable); 969 } 970 971 /** 972 * Disables all the networks in the provided list in wpa_supplicant. 973 * 974 * @param configs Collection of configs which needs to be enabled. 975 * @return true if successful, false otherwise. 976 */ 977 public boolean disableAllNetworks(Collection<WifiConfiguration> configs) { 978 if (VDBG) localLog("disableAllNetworks"); 979 boolean networkDisabled = false; 980 for (WifiConfiguration enabled : configs) { 981 if (disableNetwork(enabled)) { 982 networkDisabled = true; 983 } 984 } 985 saveConfig(); 986 return networkDisabled; 987 } 988 989 /** 990 * Save the current configuration to wpa_supplicant.conf. 991 */ 992 public boolean saveConfig() { 993 return mWifiNative.saveConfig(); 994 } 995 996 /** 997 * Read network variables from wpa_supplicant.conf. 998 * 999 * @param key The parameter to be parsed. 1000 * @return Map of corresponding configKey to the value of the param requested. 1001 */ 1002 public Map<String, String> readNetworkVariablesFromSupplicantFile(String key) { 1003 Map<String, String> result = new HashMap<>(); 1004 BufferedReader reader = null; 1005 try { 1006 reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE)); 1007 result = readNetworkVariablesFromReader(reader, key); 1008 } catch (FileNotFoundException e) { 1009 if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e); 1010 } catch (IOException e) { 1011 if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e); 1012 } finally { 1013 try { 1014 if (reader != null) { 1015 reader.close(); 1016 } 1017 } catch (IOException e) { 1018 if (VDBG) { 1019 loge("Could not close reader for " + SUPPLICANT_CONFIG_FILE + ", " + e); 1020 } 1021 } 1022 } 1023 return result; 1024 } 1025 1026 /** 1027 * Read network variables from a given reader. This method is separate from 1028 * readNetworkVariablesFromSupplicantFile() for testing. 1029 * 1030 * @param reader The reader to read the network variables from. 1031 * @param key The parameter to be parsed. 1032 * @return Map of corresponding configKey to the value of the param requested. 1033 */ 1034 public Map<String, String> readNetworkVariablesFromReader(BufferedReader reader, String key) 1035 throws IOException { 1036 Map<String, String> result = new HashMap<>(); 1037 if (VDBG) localLog("readNetworkVariablesFromReader key=" + key); 1038 boolean found = false; 1039 String configKey = null; 1040 String value = null; 1041 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 1042 if (line.matches("[ \\t]*network=\\{")) { 1043 found = true; 1044 configKey = null; 1045 value = null; 1046 } else if (line.matches("[ \\t]*\\}")) { 1047 found = false; 1048 configKey = null; 1049 value = null; 1050 } 1051 if (found) { 1052 String trimmedLine = line.trim(); 1053 if (trimmedLine.startsWith(ID_STRING_VAR_NAME + "=")) { 1054 try { 1055 // Trim the quotes wrapping the id_str value. 1056 final String encodedExtras = trimmedLine.substring( 1057 8, trimmedLine.length() -1); 1058 final JSONObject json = 1059 new JSONObject(URLDecoder.decode(encodedExtras, "UTF-8")); 1060 if (json.has(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY)) { 1061 final Object configKeyFromJson = 1062 json.get(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY); 1063 if (configKeyFromJson instanceof String) { 1064 configKey = (String) configKeyFromJson; 1065 } 1066 } 1067 } catch (JSONException e) { 1068 if (VDBG) { 1069 loge("Could not get "+ WifiConfigStore.ID_STRING_KEY_CONFIG_KEY 1070 + ", " + e); 1071 } 1072 } 1073 } 1074 if (trimmedLine.startsWith(key + "=")) { 1075 value = trimmedLine.substring(key.length() + 1); 1076 } 1077 if (configKey != null && value != null) { 1078 result.put(configKey, value); 1079 } 1080 } 1081 } 1082 return result; 1083 } 1084 1085 /** 1086 * Resets all sim networks from the provided network list. 1087 * 1088 * @param configs List of all the networks. 1089 */ 1090 public void resetSimNetworks(Collection<WifiConfiguration> configs) { 1091 if (VDBG) localLog("resetSimNetworks"); 1092 for (WifiConfiguration config : configs) { 1093 if (TelephonyUtil.isSimConfig(config)) { 1094 String currentIdentity = TelephonyUtil.getSimIdentity(mContext, 1095 config.enterpriseConfig.getEapMethod()); 1096 String supplicantIdentity = 1097 mWifiNative.getNetworkVariable(config.networkId, "identity"); 1098 if(supplicantIdentity != null) { 1099 supplicantIdentity = removeDoubleQuotes(supplicantIdentity); 1100 } 1101 if (currentIdentity == null || !currentIdentity.equals(supplicantIdentity)) { 1102 // Identity differs so update the identity 1103 mWifiNative.setNetworkVariable(config.networkId, 1104 WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.EMPTY_VALUE); 1105 // This configuration may have cached Pseudonym IDs; lets remove them 1106 mWifiNative.setNetworkVariable(config.networkId, 1107 WifiEnterpriseConfig.ANON_IDENTITY_KEY, 1108 WifiEnterpriseConfig.EMPTY_VALUE); 1109 } 1110 // Update the loaded config 1111 config.enterpriseConfig.setIdentity(currentIdentity); 1112 config.enterpriseConfig.setAnonymousIdentity(""); 1113 } 1114 } 1115 } 1116 1117 /** 1118 * Clear BSSID blacklist in wpa_supplicant. 1119 */ 1120 public void clearBssidBlacklist() { 1121 if (VDBG) localLog("clearBlacklist"); 1122 mBssidBlacklist.clear(); 1123 mWifiNative.clearBlacklist(); 1124 mWifiNative.setBssidBlacklist(null); 1125 } 1126 1127 /** 1128 * Add a BSSID to the blacklist. 1129 * 1130 * @param bssid bssid to be added. 1131 */ 1132 public void blackListBssid(String bssid) { 1133 if (bssid == null) { 1134 return; 1135 } 1136 if (VDBG) localLog("blackListBssid: " + bssid); 1137 mBssidBlacklist.add(bssid); 1138 // Blacklist at wpa_supplicant 1139 mWifiNative.addToBlacklist(bssid); 1140 // Blacklist at firmware 1141 String[] list = mBssidBlacklist.toArray(new String[mBssidBlacklist.size()]); 1142 mWifiNative.setBssidBlacklist(list); 1143 } 1144 1145 /** 1146 * Checks if the provided bssid is blacklisted or not. 1147 * 1148 * @param bssid bssid to be checked. 1149 * @return true if present, false otherwise. 1150 */ 1151 public boolean isBssidBlacklisted(String bssid) { 1152 return mBssidBlacklist.contains(bssid); 1153 } 1154 1155 /* Mark all networks except specified netId as disabled */ 1156 private void markAllNetworksDisabledExcept(int netId, Collection<WifiConfiguration> configs) { 1157 for (WifiConfiguration config : configs) { 1158 if (config != null && config.networkId != netId) { 1159 if (config.status != Status.DISABLED) { 1160 config.status = Status.DISABLED; 1161 } 1162 } 1163 } 1164 } 1165 1166 private void markAllNetworksDisabled(Collection<WifiConfiguration> configs) { 1167 markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, configs); 1168 } 1169 1170 /** 1171 * Start WPS pin method configuration with pin obtained 1172 * from the access point 1173 * 1174 * @param config WPS configuration 1175 * @return Wps result containing status and pin 1176 */ 1177 public WpsResult startWpsWithPinFromAccessPoint(WpsInfo config, 1178 Collection<WifiConfiguration> configs) { 1179 WpsResult result = new WpsResult(); 1180 if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) { 1181 /* WPS leaves all networks disabled */ 1182 markAllNetworksDisabled(configs); 1183 result.status = WpsResult.Status.SUCCESS; 1184 } else { 1185 loge("Failed to start WPS pin method configuration"); 1186 result.status = WpsResult.Status.FAILURE; 1187 } 1188 return result; 1189 } 1190 1191 /** 1192 * Start WPS pin method configuration with obtained 1193 * from the device 1194 * 1195 * @return WpsResult indicating status and pin 1196 */ 1197 public WpsResult startWpsWithPinFromDevice(WpsInfo config, 1198 Collection<WifiConfiguration> configs) { 1199 WpsResult result = new WpsResult(); 1200 result.pin = mWifiNative.startWpsPinDisplay(config.BSSID); 1201 /* WPS leaves all networks disabled */ 1202 if (!TextUtils.isEmpty(result.pin)) { 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 push button configuration 1214 * 1215 * @param config WPS configuration 1216 * @return WpsResult indicating status and pin 1217 */ 1218 public WpsResult startWpsPbc(WpsInfo config, 1219 Collection<WifiConfiguration> configs) { 1220 WpsResult result = new WpsResult(); 1221 if (mWifiNative.startWpsPbc(config.BSSID)) { 1222 /* WPS leaves all networks disabled */ 1223 markAllNetworksDisabled(configs); 1224 result.status = WpsResult.Status.SUCCESS; 1225 } else { 1226 loge("Failed to start WPS push button configuration"); 1227 result.status = WpsResult.Status.FAILURE; 1228 } 1229 return result; 1230 } 1231 1232 protected void logd(String s) { 1233 Log.d(TAG, s); 1234 } 1235 1236 protected void logi(String s) { 1237 Log.i(TAG, s); 1238 } 1239 1240 protected void loge(String s) { 1241 loge(s, false); 1242 } 1243 1244 protected void loge(String s, boolean stack) { 1245 if (stack) { 1246 Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 1247 + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() 1248 + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() 1249 + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); 1250 } else { 1251 Log.e(TAG, s); 1252 } 1253 } 1254 1255 protected void log(String s) { 1256 Log.d(TAG, s); 1257 } 1258 1259 private void localLog(String s) { 1260 if (mLocalLog != null) { 1261 mLocalLog.log(TAG + ": " + s); 1262 } 1263 } 1264 1265 private void localLogAndLogcat(String s) { 1266 localLog(s); 1267 Log.d(TAG, s); 1268 } 1269 1270 private class SupplicantSaver implements WifiEnterpriseConfig.SupplicantSaver { 1271 private final int mNetId; 1272 private final String mSetterSSID; 1273 1274 SupplicantSaver(int netId, String setterSSID) { 1275 mNetId = netId; 1276 mSetterSSID = setterSSID; 1277 } 1278 1279 @Override 1280 public boolean saveValue(String key, String value) { 1281 if (key.equals(WifiEnterpriseConfig.PASSWORD_KEY) 1282 && value != null && value.equals("*")) { 1283 // No need to try to set an obfuscated password, which will fail 1284 return true; 1285 } 1286 if (key.equals(WifiEnterpriseConfig.REALM_KEY) 1287 || key.equals(WifiEnterpriseConfig.PLMN_KEY)) { 1288 // No need to save realm or PLMN in supplicant 1289 return true; 1290 } 1291 // TODO: We need a way to clear values in wpa_supplicant as opposed to 1292 // mapping unset values to empty strings. 1293 if (value == null) { 1294 value = "\"\""; 1295 } 1296 if (!mWifiNative.setNetworkVariable(mNetId, key, value)) { 1297 loge(mSetterSSID + ": failed to set " + key + ": " + value); 1298 return false; 1299 } 1300 return true; 1301 } 1302 } 1303 1304 private class SupplicantLoader implements WifiEnterpriseConfig.SupplicantLoader { 1305 private final int mNetId; 1306 1307 SupplicantLoader(int netId) { 1308 mNetId = netId; 1309 } 1310 1311 @Override 1312 public String loadValue(String key) { 1313 String value = mWifiNative.getNetworkVariable(mNetId, key); 1314 if (!TextUtils.isEmpty(value)) { 1315 if (!enterpriseConfigKeyShouldBeQuoted(key)) { 1316 value = removeDoubleQuotes(value); 1317 } 1318 return value; 1319 } else { 1320 return null; 1321 } 1322 } 1323 1324 /** 1325 * Returns true if a particular config key needs to be quoted when passed to the supplicant. 1326 */ 1327 private boolean enterpriseConfigKeyShouldBeQuoted(String key) { 1328 switch (key) { 1329 case WifiEnterpriseConfig.EAP_KEY: 1330 case WifiEnterpriseConfig.ENGINE_KEY: 1331 return false; 1332 default: 1333 return true; 1334 } 1335 } 1336 } 1337 1338 // TODO(rpius): Remove this. 1339 private class WpaConfigFileObserver extends FileObserver { 1340 1341 WpaConfigFileObserver() { 1342 super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE); 1343 } 1344 1345 @Override 1346 public void onEvent(int event, String path) { 1347 if (event == CLOSE_WRITE) { 1348 File file = new File(SUPPLICANT_CONFIG_FILE); 1349 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length()); 1350 } 1351 } 1352 } 1353 } 1354