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