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