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