1 /* 2 * Copyright (C) 2013 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 package android.net.wifi; 17 18 import android.os.Parcel; 19 import android.os.Parcelable; 20 import android.security.Credentials; 21 import android.text.TextUtils; 22 23 import java.io.ByteArrayInputStream; 24 import java.security.KeyFactory; 25 import java.security.NoSuchAlgorithmException; 26 import java.security.PrivateKey; 27 import java.security.cert.CertificateEncodingException; 28 import java.security.cert.CertificateException; 29 import java.security.cert.CertificateFactory; 30 import java.security.cert.X509Certificate; 31 import java.security.spec.InvalidKeySpecException; 32 import java.security.spec.PKCS8EncodedKeySpec; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 /** 37 * Enterprise configuration details for Wi-Fi. Stores details about the EAP method 38 * and any associated credentials. 39 */ 40 public class WifiEnterpriseConfig implements Parcelable { 41 42 /** @hide */ 43 public static final String EMPTY_VALUE = "NULL"; 44 /** @hide */ 45 public static final String EAP_KEY = "eap"; 46 /** @hide */ 47 public static final String PHASE2_KEY = "phase2"; 48 /** @hide */ 49 public static final String IDENTITY_KEY = "identity"; 50 /** @hide */ 51 public static final String ANON_IDENTITY_KEY = "anonymous_identity"; 52 /** @hide */ 53 public static final String PASSWORD_KEY = "password"; 54 /** @hide */ 55 public static final String SUBJECT_MATCH_KEY = "subject_match"; 56 /** @hide */ 57 public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match"; 58 /** @hide */ 59 public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match"; 60 /** @hide */ 61 public static final String OPP_KEY_CACHING = "proactive_key_caching"; 62 /** 63 * String representing the keystore OpenSSL ENGINE's ID. 64 * @hide 65 */ 66 public static final String ENGINE_ID_KEYSTORE = "keystore"; 67 68 /** 69 * String representing the keystore URI used for wpa_supplicant. 70 * @hide 71 */ 72 public static final String KEYSTORE_URI = "keystore://"; 73 74 /** 75 * String to set the engine value to when it should be enabled. 76 * @hide 77 */ 78 public static final String ENGINE_ENABLE = "1"; 79 80 /** 81 * String to set the engine value to when it should be disabled. 82 * @hide 83 */ 84 public static final String ENGINE_DISABLE = "0"; 85 86 /** @hide */ 87 public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; 88 /** @hide */ 89 public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE; 90 /** @hide */ 91 public static final String CLIENT_CERT_KEY = "client_cert"; 92 /** @hide */ 93 public static final String CA_CERT_KEY = "ca_cert"; 94 /** @hide */ 95 public static final String ENGINE_KEY = "engine"; 96 /** @hide */ 97 public static final String ENGINE_ID_KEY = "engine_id"; 98 /** @hide */ 99 public static final String PRIVATE_KEY_ID_KEY = "key_id"; 100 /** @hide */ 101 public static final String REALM_KEY = "realm"; 102 /** @hide */ 103 public static final String PLMN_KEY = "plmn"; 104 /** @hide */ 105 public static final String PHASE1_KEY = "phase1"; 106 107 /** {@hide} */ 108 public static final String ENABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=0\""; 109 /** {@hide} */ 110 public static final String DISABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=1\""; 111 112 private HashMap<String, String> mFields = new HashMap<String, String>(); 113 //By default, we enable TLS1.2. However, due to a known bug on some radius, we may disable it to 114 // fall back to TLS 1.1. 115 private boolean mTls12Enable = true; 116 private X509Certificate mCaCert; 117 private PrivateKey mClientPrivateKey; 118 private X509Certificate mClientCertificate; 119 WifiEnterpriseConfig()120 public WifiEnterpriseConfig() { 121 // Do not set defaults so that the enterprise fields that are not changed 122 // by API are not changed underneath 123 // This is essential because an app may not have all fields like password 124 // available. It allows modification of subset of fields. 125 126 } 127 128 /** Copy constructor */ WifiEnterpriseConfig(WifiEnterpriseConfig source)129 public WifiEnterpriseConfig(WifiEnterpriseConfig source) { 130 for (String key : source.mFields.keySet()) { 131 mFields.put(key, source.mFields.get(key)); 132 } 133 } 134 135 @Override describeContents()136 public int describeContents() { 137 return 0; 138 } 139 140 @Override writeToParcel(Parcel dest, int flags)141 public void writeToParcel(Parcel dest, int flags) { 142 dest.writeInt(mFields.size()); 143 for (Map.Entry<String, String> entry : mFields.entrySet()) { 144 dest.writeString(entry.getKey()); 145 dest.writeString(entry.getValue()); 146 } 147 148 writeCertificate(dest, mCaCert); 149 150 if (mClientPrivateKey != null) { 151 String algorithm = mClientPrivateKey.getAlgorithm(); 152 byte[] userKeyBytes = mClientPrivateKey.getEncoded(); 153 dest.writeInt(userKeyBytes.length); 154 dest.writeByteArray(userKeyBytes); 155 dest.writeString(algorithm); 156 } else { 157 dest.writeInt(0); 158 } 159 160 writeCertificate(dest, mClientCertificate); 161 dest.writeInt(mTls12Enable ? 1: 0); 162 } 163 writeCertificate(Parcel dest, X509Certificate cert)164 private void writeCertificate(Parcel dest, X509Certificate cert) { 165 if (cert != null) { 166 try { 167 byte[] certBytes = cert.getEncoded(); 168 dest.writeInt(certBytes.length); 169 dest.writeByteArray(certBytes); 170 } catch (CertificateEncodingException e) { 171 dest.writeInt(0); 172 } 173 } else { 174 dest.writeInt(0); 175 } 176 } 177 178 public static final Creator<WifiEnterpriseConfig> CREATOR = 179 new Creator<WifiEnterpriseConfig>() { 180 public WifiEnterpriseConfig createFromParcel(Parcel in) { 181 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 182 int count = in.readInt(); 183 for (int i = 0; i < count; i++) { 184 String key = in.readString(); 185 String value = in.readString(); 186 enterpriseConfig.mFields.put(key, value); 187 } 188 189 enterpriseConfig.mCaCert = readCertificate(in); 190 191 PrivateKey userKey = null; 192 int len = in.readInt(); 193 if (len > 0) { 194 try { 195 byte[] bytes = new byte[len]; 196 in.readByteArray(bytes); 197 String algorithm = in.readString(); 198 KeyFactory keyFactory = KeyFactory.getInstance(algorithm); 199 userKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); 200 } catch (NoSuchAlgorithmException e) { 201 userKey = null; 202 } catch (InvalidKeySpecException e) { 203 userKey = null; 204 } 205 } 206 207 enterpriseConfig.mClientPrivateKey = userKey; 208 enterpriseConfig.mClientCertificate = readCertificate(in); 209 enterpriseConfig.mTls12Enable = (in.readInt() == 1); 210 return enterpriseConfig; 211 } 212 213 private X509Certificate readCertificate(Parcel in) { 214 X509Certificate cert = null; 215 int len = in.readInt(); 216 if (len > 0) { 217 try { 218 byte[] bytes = new byte[len]; 219 in.readByteArray(bytes); 220 CertificateFactory cFactory = CertificateFactory.getInstance("X.509"); 221 cert = (X509Certificate) cFactory 222 .generateCertificate(new ByteArrayInputStream(bytes)); 223 } catch (CertificateException e) { 224 cert = null; 225 } 226 } 227 return cert; 228 } 229 230 public WifiEnterpriseConfig[] newArray(int size) { 231 return new WifiEnterpriseConfig[size]; 232 } 233 }; 234 235 /** The Extensible Authentication Protocol method used */ 236 public static final class Eap { 237 /** No EAP method used. Represents an empty config */ 238 public static final int NONE = -1; 239 /** Protected EAP */ 240 public static final int PEAP = 0; 241 /** EAP-Transport Layer Security */ 242 public static final int TLS = 1; 243 /** EAP-Tunneled Transport Layer Security */ 244 public static final int TTLS = 2; 245 /** EAP-Password */ 246 public static final int PWD = 3; 247 /** EAP-Subscriber Identity Module */ 248 public static final int SIM = 4; 249 /** EAP-Authentication and Key Agreement */ 250 public static final int AKA = 5; 251 /** EAP-Authentication and Key Agreement Prime */ 252 public static final int AKA_PRIME = 6; 253 /** @hide */ 254 public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'" }; 255 256 /** Prevent initialization */ Eap()257 private Eap() {} 258 } 259 260 /** The inner authentication method used */ 261 public static final class Phase2 { 262 public static final int NONE = 0; 263 /** Password Authentication Protocol */ 264 public static final int PAP = 1; 265 /** Microsoft Challenge Handshake Authentication Protocol */ 266 public static final int MSCHAP = 2; 267 /** Microsoft Challenge Handshake Authentication Protocol v2 */ 268 public static final int MSCHAPV2 = 3; 269 /** Generic Token Card */ 270 public static final int GTC = 4; 271 private static final String PREFIX = "auth="; 272 /** @hide */ 273 public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", 274 "MSCHAPV2", "GTC" }; 275 276 /** Prevent initialization */ Phase2()277 private Phase2() {} 278 } 279 280 /** Internal use only 281 * @hide 282 */ getFields()283 public HashMap<String, String> getFields() { 284 return mFields; 285 } 286 287 /** 288 * Set the EAP authentication method. 289 * @param eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or 290 * {@link Eap#PWD} 291 * @throws IllegalArgumentException on an invalid eap method 292 */ setEapMethod(int eapMethod)293 public void setEapMethod(int eapMethod) { 294 switch (eapMethod) { 295 /** Valid methods */ 296 case Eap.TLS: 297 setPhase2Method(Phase2.NONE); 298 /* fall through */ 299 case Eap.PEAP: 300 case Eap.PWD: 301 case Eap.TTLS: 302 case Eap.SIM: 303 case Eap.AKA: 304 case Eap.AKA_PRIME: 305 mFields.put(EAP_KEY, Eap.strings[eapMethod]); 306 mFields.put(OPP_KEY_CACHING, "1"); 307 break; 308 default: 309 throw new IllegalArgumentException("Unknown EAP method"); 310 } 311 } 312 313 /** 314 * Set the TLS version 315 * @param enable: true -- enable TLS1.2 false -- disable TLS1.2 316 * @hide 317 */ setTls12Enable(boolean enable)318 public void setTls12Enable(boolean enable) { 319 mTls12Enable = enable; 320 mFields.put(PHASE1_KEY, 321 enable ? ENABLE_TLS_1_2 : DISABLE_TLS_1_2); 322 } 323 324 /** 325 * Get the TLS1.2 enabled or not 326 * @return eap method configured 327 * @hide 328 */ getTls12Enable()329 public boolean getTls12Enable() { 330 return mTls12Enable; 331 } 332 333 /** 334 * Get the eap method. 335 * @return eap method configured 336 */ getEapMethod()337 public int getEapMethod() { 338 String eapMethod = mFields.get(EAP_KEY); 339 return getStringIndex(Eap.strings, eapMethod, Eap.NONE); 340 } 341 342 /** 343 * Set Phase 2 authentication method. Sets the inner authentication method to be used in 344 * phase 2 after setting up a secure channel 345 * @param phase2Method is the inner authentication method and can be one of {@link Phase2#NONE}, 346 * {@link Phase2#PAP}, {@link Phase2#MSCHAP}, {@link Phase2#MSCHAPV2}, 347 * {@link Phase2#GTC} 348 * @throws IllegalArgumentException on an invalid phase2 method 349 * 350 */ setPhase2Method(int phase2Method)351 public void setPhase2Method(int phase2Method) { 352 switch (phase2Method) { 353 case Phase2.NONE: 354 mFields.put(PHASE2_KEY, EMPTY_VALUE); 355 break; 356 /** Valid methods */ 357 case Phase2.PAP: 358 case Phase2.MSCHAP: 359 case Phase2.MSCHAPV2: 360 case Phase2.GTC: 361 mFields.put(PHASE2_KEY, convertToQuotedString( 362 Phase2.PREFIX + Phase2.strings[phase2Method])); 363 break; 364 default: 365 throw new IllegalArgumentException("Unknown Phase 2 method"); 366 } 367 } 368 369 /** 370 * Get the phase 2 authentication method. 371 * @return a phase 2 method defined at {@link Phase2} 372 * */ getPhase2Method()373 public int getPhase2Method() { 374 String phase2Method = removeDoubleQuotes(mFields.get(PHASE2_KEY)); 375 // Remove auth= prefix 376 if (phase2Method.startsWith(Phase2.PREFIX)) { 377 phase2Method = phase2Method.substring(Phase2.PREFIX.length()); 378 } 379 return getStringIndex(Phase2.strings, phase2Method, Phase2.NONE); 380 } 381 382 /** 383 * Set the identity 384 * @param identity 385 */ setIdentity(String identity)386 public void setIdentity(String identity) { 387 setFieldValue(IDENTITY_KEY, identity, ""); 388 } 389 390 /** 391 * Get the identity 392 * @return the identity 393 */ getIdentity()394 public String getIdentity() { 395 return getFieldValue(IDENTITY_KEY, ""); 396 } 397 398 /** 399 * Set anonymous identity. This is used as the unencrypted identity with 400 * certain EAP types 401 * @param anonymousIdentity the anonymous identity 402 */ setAnonymousIdentity(String anonymousIdentity)403 public void setAnonymousIdentity(String anonymousIdentity) { 404 setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, ""); 405 } 406 407 /** Get the anonymous identity 408 * @return anonymous identity 409 */ getAnonymousIdentity()410 public String getAnonymousIdentity() { 411 return getFieldValue(ANON_IDENTITY_KEY, ""); 412 } 413 414 /** 415 * Set the password. 416 * @param password the password 417 */ setPassword(String password)418 public void setPassword(String password) { 419 setFieldValue(PASSWORD_KEY, password, ""); 420 } 421 422 /** 423 * Get the password. 424 * 425 * Returns locally set password value. For networks fetched from 426 * framework, returns "*". 427 */ getPassword()428 public String getPassword() { 429 return getFieldValue(PASSWORD_KEY, ""); 430 } 431 432 /** 433 * Set CA certificate alias. 434 * 435 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 436 * a certificate 437 * </p> 438 * @param alias identifies the certificate 439 * @hide 440 */ setCaCertificateAlias(String alias)441 public void setCaCertificateAlias(String alias) { 442 setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); 443 } 444 445 /** 446 * Get CA certificate alias 447 * @return alias to the CA certificate 448 * @hide 449 */ getCaCertificateAlias()450 public String getCaCertificateAlias() { 451 return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); 452 } 453 454 /** 455 * Specify a X.509 certificate that identifies the server. 456 * 457 * <p>A default name is automatically assigned to the certificate and used 458 * with this configuration. The framework takes care of installing the 459 * certificate when the config is saved and removing the certificate when 460 * the config is removed. 461 * 462 * @param cert X.509 CA certificate 463 * @throws IllegalArgumentException if not a CA certificate 464 */ setCaCertificate(X509Certificate cert)465 public void setCaCertificate(X509Certificate cert) { 466 if (cert != null) { 467 if (cert.getBasicConstraints() >= 0) { 468 mCaCert = cert; 469 } else { 470 throw new IllegalArgumentException("Not a CA certificate"); 471 } 472 } else { 473 mCaCert = null; 474 } 475 } 476 477 /** 478 * Get CA certificate 479 * @return X.509 CA certificate 480 */ getCaCertificate()481 public X509Certificate getCaCertificate() { 482 return mCaCert; 483 } 484 485 /** 486 * @hide 487 */ resetCaCertificate()488 public void resetCaCertificate() { 489 mCaCert = null; 490 } 491 492 /** Set Client certificate alias. 493 * 494 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 495 * a certificate 496 * </p> 497 * @param alias identifies the certificate 498 * @hide 499 */ setClientCertificateAlias(String alias)500 public void setClientCertificateAlias(String alias) { 501 setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); 502 setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); 503 // Also, set engine parameters 504 if (TextUtils.isEmpty(alias)) { 505 mFields.put(ENGINE_KEY, ENGINE_DISABLE); 506 mFields.put(ENGINE_ID_KEY, EMPTY_VALUE); 507 } else { 508 mFields.put(ENGINE_KEY, ENGINE_ENABLE); 509 mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); 510 } 511 } 512 513 /** 514 * Get client certificate alias 515 * @return alias to the client certificate 516 * @hide 517 */ getClientCertificateAlias()518 public String getClientCertificateAlias() { 519 return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); 520 } 521 522 /** 523 * Specify a private key and client certificate for client authorization. 524 * 525 * <p>A default name is automatically assigned to the key entry and used 526 * with this configuration. The framework takes care of installing the 527 * key entry when the config is saved and removing the key entry when 528 * the config is removed. 529 530 * @param privateKey 531 * @param clientCertificate 532 * @throws IllegalArgumentException for an invalid key or certificate. 533 */ setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate)534 public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { 535 if (clientCertificate != null) { 536 if (clientCertificate.getBasicConstraints() != -1) { 537 throw new IllegalArgumentException("Cannot be a CA certificate"); 538 } 539 if (privateKey == null) { 540 throw new IllegalArgumentException("Client cert without a private key"); 541 } 542 if (privateKey.getEncoded() == null) { 543 throw new IllegalArgumentException("Private key cannot be encoded"); 544 } 545 } 546 547 mClientPrivateKey = privateKey; 548 mClientCertificate = clientCertificate; 549 } 550 551 /** 552 * Get client certificate 553 * 554 * @return X.509 client certificate 555 */ getClientCertificate()556 public X509Certificate getClientCertificate() { 557 return mClientCertificate; 558 } 559 560 /** 561 * @hide 562 */ resetClientKeyEntry()563 public void resetClientKeyEntry() { 564 mClientPrivateKey = null; 565 mClientCertificate = null; 566 } 567 568 /** 569 * @hide 570 */ getClientPrivateKey()571 public PrivateKey getClientPrivateKey() { 572 return mClientPrivateKey; 573 } 574 575 /** 576 * Set subject match (deprecated). This is the substring to be matched against the subject of 577 * the authentication server certificate. 578 * @param subjectMatch substring to be matched 579 * @deprecated in favor of altSubjectMatch 580 */ setSubjectMatch(String subjectMatch)581 public void setSubjectMatch(String subjectMatch) { 582 setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, ""); 583 } 584 585 /** 586 * Get subject match (deprecated) 587 * @return the subject match string 588 * @deprecated in favor of altSubjectMatch 589 */ getSubjectMatch()590 public String getSubjectMatch() { 591 return getFieldValue(SUBJECT_MATCH_KEY, ""); 592 } 593 594 /** 595 * Set alternate subject match. This is the substring to be matched against the 596 * alternate subject of the authentication server certificate. 597 * @param altSubjectMatch substring to be matched, for example 598 * DNS:server.example.com;EMAIL:server@example.com 599 */ setAltSubjectMatch(String altSubjectMatch)600 public void setAltSubjectMatch(String altSubjectMatch) { 601 setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch, ""); 602 } 603 604 /** 605 * Get alternate subject match 606 * @return the alternate subject match string 607 */ getAltSubjectMatch()608 public String getAltSubjectMatch() { 609 return getFieldValue(ALTSUBJECT_MATCH_KEY, ""); 610 } 611 612 /** 613 * Set the domain_suffix_match directive on wpa_supplicant. This is the parameter to use 614 * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2, 615 * second paragraph. 616 * 617 * From wpa_supplicant documentation: 618 * Constraint for server domain name. If set, this FQDN is used as a suffix match requirement 619 * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is 620 * found, this constraint is met. If no dNSName values are present, this constraint is matched 621 * against SubjectName CN using same suffix match comparison. 622 * Suffix match here means that the host/domain name is compared one label at a time starting 623 * from the top-level domain and all the labels in domain_suffix_match shall be included in the 624 * certificate. The certificate may include additional sub-level labels in addition to the 625 * required labels. 626 * For example, domain_suffix_match=example.com would match test.example.com but would not 627 * match test-example.com. 628 * @param domain The domain value 629 */ setDomainSuffixMatch(String domain)630 public void setDomainSuffixMatch(String domain) { 631 setFieldValue(DOM_SUFFIX_MATCH_KEY, domain); 632 } 633 634 /** 635 * Get the domain_suffix_match value. See setDomSuffixMatch. 636 * @return The domain value. 637 */ getDomainSuffixMatch()638 public String getDomainSuffixMatch() { 639 return getFieldValue(DOM_SUFFIX_MATCH_KEY, ""); 640 } 641 642 /** 643 * Set realm for passpoint credential; realm identifies a set of networks where your 644 * passpoint credential can be used 645 * @param realm the realm 646 */ setRealm(String realm)647 public void setRealm(String realm) { 648 setFieldValue(REALM_KEY, realm, ""); 649 } 650 651 /** 652 * Get realm for passpoint credential; see {@link #setRealm(String)} for more information 653 * @return the realm 654 */ getRealm()655 public String getRealm() { 656 return getFieldValue(REALM_KEY, ""); 657 } 658 659 /** 660 * Set plmn (Public Land Mobile Network) of the provider of passpoint credential 661 * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code) 662 */ setPlmn(String plmn)663 public void setPlmn(String plmn) { 664 setFieldValue(PLMN_KEY, plmn, ""); 665 } 666 667 /** 668 * Get plmn (Public Land Mobile Network) for passpoint credential; see {@link #setPlmn 669 * (String)} for more information 670 * @return the plmn 671 */ getPlmn()672 public String getPlmn() { 673 return getFieldValue(PLMN_KEY, ""); 674 } 675 676 /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ getKeyId(WifiEnterpriseConfig current)677 String getKeyId(WifiEnterpriseConfig current) { 678 String eap = mFields.get(EAP_KEY); 679 String phase2 = mFields.get(PHASE2_KEY); 680 681 // If either eap or phase2 are not initialized, use current config details 682 if (TextUtils.isEmpty((eap))) { 683 eap = current.mFields.get(EAP_KEY); 684 } 685 if (TextUtils.isEmpty(phase2)) { 686 phase2 = current.mFields.get(PHASE2_KEY); 687 } 688 return eap + "_" + phase2; 689 } 690 removeDoubleQuotes(String string)691 private String removeDoubleQuotes(String string) { 692 if (TextUtils.isEmpty(string)) return ""; 693 int length = string.length(); 694 if ((length > 1) && (string.charAt(0) == '"') 695 && (string.charAt(length - 1) == '"')) { 696 return string.substring(1, length - 1); 697 } 698 return string; 699 } 700 convertToQuotedString(String string)701 private String convertToQuotedString(String string) { 702 return "\"" + string + "\""; 703 } 704 705 /** Returns the index at which the toBeFound string is found in the array. 706 * @param arr array of strings 707 * @param toBeFound string to be found 708 * @param defaultIndex default index to be returned when string is not found 709 * @return the index into array 710 */ getStringIndex(String arr[], String toBeFound, int defaultIndex)711 private int getStringIndex(String arr[], String toBeFound, int defaultIndex) { 712 if (TextUtils.isEmpty(toBeFound)) return defaultIndex; 713 for (int i = 0; i < arr.length; i++) { 714 if (toBeFound.equals(arr[i])) return i; 715 } 716 return defaultIndex; 717 } 718 719 /** Returns the field value for the key. 720 * @param key into the hash 721 * @param prefix is the prefix that the value may have 722 * @return value 723 * @hide 724 */ getFieldValue(String key, String prefix)725 public String getFieldValue(String key, String prefix) { 726 String value = mFields.get(key); 727 // Uninitialized or known to be empty after reading from supplicant 728 if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return ""; 729 730 value = removeDoubleQuotes(value); 731 if (value.startsWith(prefix)) { 732 return value.substring(prefix.length()); 733 } else { 734 return value; 735 } 736 } 737 738 /** Set a value with an optional prefix at key 739 * @param key into the hash 740 * @param value to be set 741 * @param prefix an optional value to be prefixed to actual value 742 * @hide 743 */ setFieldValue(String key, String value, String prefix)744 public void setFieldValue(String key, String value, String prefix) { 745 if (TextUtils.isEmpty(value)) { 746 mFields.put(key, EMPTY_VALUE); 747 } else { 748 mFields.put(key, convertToQuotedString(prefix + value)); 749 } 750 } 751 752 753 /** Set a value with an optional prefix at key 754 * @param key into the hash 755 * @param value to be set 756 * @param prefix an optional value to be prefixed to actual value 757 * @hide 758 */ setFieldValue(String key, String value)759 public void setFieldValue(String key, String value) { 760 if (TextUtils.isEmpty(value)) { 761 mFields.put(key, EMPTY_VALUE); 762 } else { 763 mFields.put(key, convertToQuotedString(value)); 764 } 765 } 766 767 @Override toString()768 public String toString() { 769 StringBuffer sb = new StringBuffer(); 770 for (String key : mFields.keySet()) { 771 // Don't display password in toString(). 772 String value = PASSWORD_KEY.equals(key) ? "<removed>" : mFields.get(key); 773 sb.append(key).append(" ").append(value).append("\n"); 774 } 775 return sb.toString(); 776 } 777 } 778