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