Provider.java revision c3a9db83a352d92d5a6e0098f22bde07e34a1d3b
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27package java.security; 28 29import java.io.*; 30import java.util.*; 31import java.util.concurrent.atomic.AtomicBoolean; 32 33import static java.util.Locale.ENGLISH; 34 35import java.lang.ref.*; 36import java.lang.reflect.*; 37import java.security.Security; 38import java.security.cert.CertStoreParameters; 39import java.util.function.BiConsumer; 40 41import javax.security.auth.login.Configuration; 42 43/** 44 * This class represents a "provider" for the 45 * Java Security API, where a provider implements some or all parts of 46 * Java Security. Services that a provider may implement include: 47 * 48 * <ul> 49 * 50 * <li>Algorithms (such as DSA, RSA, MD5 or SHA-1). 51 * 52 * <li>Key generation, conversion, and management facilities (such as for 53 * algorithm-specific keys). 54 * 55 *</ul> 56 * 57 * <p>Each provider has a name and a version number, and is configured 58 * in each runtime it is installed in. 59 * 60 * <p>See <a href = 61 * "../../../technotes/guides/security/crypto/CryptoSpec.html#Provider">The Provider Class</a> 62 * in the "Java Cryptography Architecture API Specification & Reference" 63 * for information about how a particular type of provider, the 64 * cryptographic service provider, works and is installed. However, 65 * please note that a provider can be used to implement any security 66 * service in Java that uses a pluggable architecture with a choice 67 * of implementations that fit underneath. 68 * 69 * <p>Some provider implementations may encounter unrecoverable internal 70 * errors during their operation, for example a failure to communicate with a 71 * security token. A {@link ProviderException} should be used to indicate 72 * such errors. 73 * 74 * <p>The service type <code>Provider</code> is reserved for use by the 75 * security framework. Services of this type cannot be added, removed, 76 * or modified by applications. 77 * The following attributes are automatically placed in each Provider object: 78 * <table cellspacing=4> 79 * <tr><th>Name</th><th>Value</th> 80 * <tr><td><code>Provider.id name</code></td> 81 * <td><code>String.valueOf(provider.getName())</code></td> 82 * <tr><td><code>Provider.id version</code></td> 83 * <td><code>String.valueOf(provider.getVersion())</code></td> 84 * <tr><td><code>Provider.id info</code></td> 85 <td><code>String.valueOf(provider.getInfo())</code></td> 86 * <tr><td><code>Provider.id className</code></td> 87 * <td><code>provider.getClass().getName()</code></td> 88 * </table> 89 * 90 * @author Benjamin Renaud 91 * @author Andreas Sterbenz 92 */ 93public abstract class Provider extends Properties { 94 95 // Declare serialVersionUID to be compatible with JDK1.1 96 static final long serialVersionUID = -4298000515446427739L; 97 98 private volatile boolean registered = false; 99 100 private static final sun.security.util.Debug debug = 101 sun.security.util.Debug.getInstance 102 ("provider", "Provider"); 103 104 /** 105 * The provider name. 106 * 107 * @serial 108 */ 109 private String name; 110 111 /** 112 * A description of the provider and its services. 113 * 114 * @serial 115 */ 116 private String info; 117 118 /** 119 * The provider version number. 120 * 121 * @serial 122 */ 123 private double version; 124 125 126 private transient Set<Map.Entry<Object,Object>> entrySet = null; 127 private transient int entrySetCallCount = 0; 128 129 private transient boolean initialized; 130 131 /** 132 * Constructs a provider with the specified name, version number, 133 * and information. 134 * 135 * @param name the provider name. 136 * 137 * @param version the provider version number. 138 * 139 * @param info a description of the provider and its services. 140 */ 141 protected Provider(String name, double version, String info) { 142 this.name = name; 143 this.version = version; 144 this.info = info; 145 putId(); 146 initialized = true; 147 } 148 149 /** 150 * Returns the name of this provider. 151 * 152 * @return the name of this provider. 153 */ 154 public String getName() { 155 return name; 156 } 157 158 /** 159 * Returns the version number for this provider. 160 * 161 * @return the version number for this provider. 162 */ 163 public double getVersion() { 164 return version; 165 } 166 167 /** 168 * Returns a human-readable description of the provider and its 169 * services. This may return an HTML page, with relevant links. 170 * 171 * @return a description of the provider and its services. 172 */ 173 public String getInfo() { 174 return info; 175 } 176 177 /** 178 * Returns a string with the name and the version number 179 * of this provider. 180 * 181 * @return the string with the name and the version number 182 * for this provider. 183 */ 184 public String toString() { 185 return name + " version " + version; 186 } 187 188 /* 189 * override the following methods to ensure that provider 190 * information can only be changed if the caller has the appropriate 191 * permissions. 192 */ 193 194 /** 195 * Clears this provider so that it no longer contains the properties 196 * used to look up facilities implemented by the provider. 197 * 198 * <p>First, if there is a security manager, its 199 * <code>checkSecurityAccess</code> method is called with the string 200 * <code>"clearProviderProperties."+name</code> (where <code>name</code> 201 * is the provider name) to see if it's ok to clear this provider. 202 * If the default implementation of <code>checkSecurityAccess</code> 203 * is used (that is, that method is not overriden), then this results in 204 * a call to the security manager's <code>checkPermission</code> method 205 * with a <code>SecurityPermission("clearProviderProperties."+name)</code> 206 * permission. 207 * 208 * @throws SecurityException 209 * if a security manager exists and its <code>{@link 210 * java.lang.SecurityManager#checkSecurityAccess}</code> method 211 * denies access to clear this provider 212 * 213 * @since 1.2 214 */ 215 public synchronized void clear() { 216 check("clearProviderProperties."+name); 217 if (debug != null) { 218 debug.println("Remove " + name + " provider properties"); 219 } 220 implClear(); 221 } 222 223 /** 224 * Reads a property list (key and element pairs) from the input stream. 225 * 226 * @param inStream the input stream. 227 * @exception IOException if an error occurred when reading from the 228 * input stream. 229 * @see java.util.Properties#load 230 */ 231 public synchronized void load(InputStream inStream) throws IOException { 232 check("putProviderProperty."+name); 233 if (debug != null) { 234 debug.println("Load " + name + " provider properties"); 235 } 236 Properties tempProperties = new Properties(); 237 tempProperties.load(inStream); 238 implPutAll(tempProperties); 239 } 240 241 /** 242 * Copies all of the mappings from the specified Map to this provider. 243 * These mappings will replace any properties that this provider had 244 * for any of the keys currently in the specified Map. 245 * 246 * @since 1.2 247 */ 248 public synchronized void putAll(Map<?,?> t) { 249 check("putProviderProperty."+name); 250 if (debug != null) { 251 debug.println("Put all " + name + " provider properties"); 252 } 253 implPutAll(t); 254 } 255 256 /** 257 * Returns an unmodifiable Set view of the property entries contained 258 * in this Provider. 259 * 260 * @see java.util.Map.Entry 261 * @since 1.2 262 */ 263 public synchronized Set<Map.Entry<Object,Object>> entrySet() { 264 checkInitialized(); 265 if (entrySet == null) { 266 if (entrySetCallCount++ == 0) // Initial call 267 entrySet = Collections.unmodifiableMap(this).entrySet(); 268 else 269 return super.entrySet(); // Recursive call 270 } 271 272 // This exception will be thrown if the implementation of 273 // Collections.unmodifiableMap.entrySet() is changed such that it 274 // no longer calls entrySet() on the backing Map. (Provider's 275 // entrySet implementation depends on this "implementation detail", 276 // which is unlikely to change. 277 if (entrySetCallCount != 2) 278 throw new RuntimeException("Internal error."); 279 280 return entrySet; 281 } 282 283 /** 284 * Returns an unmodifiable Set view of the property keys contained in 285 * this provider. 286 * 287 * @since 1.2 288 */ 289 public Set<Object> keySet() { 290 checkInitialized(); 291 return Collections.unmodifiableSet(super.keySet()); 292 } 293 294 /** 295 * Returns an unmodifiable Collection view of the property values 296 * contained in this provider. 297 * 298 * @since 1.2 299 */ 300 public Collection<Object> values() { 301 checkInitialized(); 302 return Collections.unmodifiableCollection(super.values()); 303 } 304 305 /** 306 * Sets the <code>key</code> property to have the specified 307 * <code>value</code>. 308 * 309 * <p>First, if there is a security manager, its 310 * <code>checkSecurityAccess</code> method is called with the string 311 * <code>"putProviderProperty."+name</code>, where <code>name</code> is the 312 * provider name, to see if it's ok to set this provider's property values. 313 * If the default implementation of <code>checkSecurityAccess</code> 314 * is used (that is, that method is not overriden), then this results in 315 * a call to the security manager's <code>checkPermission</code> method 316 * with a <code>SecurityPermission("putProviderProperty."+name)</code> 317 * permission. 318 * 319 * @param key the property key. 320 * 321 * @param value the property value. 322 * 323 * @return the previous value of the specified property 324 * (<code>key</code>), or null if it did not have one. 325 * 326 * @throws SecurityException 327 * if a security manager exists and its <code>{@link 328 * java.lang.SecurityManager#checkSecurityAccess}</code> method 329 * denies access to set property values. 330 * 331 * @since 1.2 332 */ 333 public synchronized Object put(Object key, Object value) { 334 check("putProviderProperty."+name); 335 if (debug != null) { 336 debug.println("Set " + name + " provider property [" + 337 key + "/" + value +"]"); 338 } 339 return implPut(key, value); 340 } 341 342 /** 343 * Removes the <code>key</code> property (and its corresponding 344 * <code>value</code>). 345 * 346 * <p>First, if there is a security manager, its 347 * <code>checkSecurityAccess</code> method is called with the string 348 * <code>"removeProviderProperty."+name</code>, where <code>name</code> is 349 * the provider name, to see if it's ok to remove this provider's 350 * properties. If the default implementation of 351 * <code>checkSecurityAccess</code> is used (that is, that method is not 352 * overriden), then this results in a call to the security manager's 353 * <code>checkPermission</code> method with a 354 * <code>SecurityPermission("removeProviderProperty."+name)</code> 355 * permission. 356 * 357 * @param key the key for the property to be removed. 358 * 359 * @return the value to which the key had been mapped, 360 * or null if the key did not have a mapping. 361 * 362 * @throws SecurityException 363 * if a security manager exists and its <code>{@link 364 * java.lang.SecurityManager#checkSecurityAccess}</code> method 365 * denies access to remove this provider's properties. 366 * 367 * @since 1.2 368 */ 369 public synchronized Object remove(Object key) { 370 check("removeProviderProperty."+name); 371 if (debug != null) { 372 debug.println("Remove " + name + " provider property " + key); 373 } 374 return implRemove(key); 375 } 376 377 // let javadoc show doc from superclass 378 public Object get(Object key) { 379 checkInitialized(); 380 return super.get(key); 381 } 382 383 /** 384 * @since 1.8 385 */ 386 @Override 387 public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) { 388 checkInitialized(); 389 super.forEach(action); 390 } 391 392 // let javadoc show doc from superclass 393 public Enumeration<Object> keys() { 394 checkInitialized(); 395 return super.keys(); 396 } 397 398 // let javadoc show doc from superclass 399 public Enumeration<Object> elements() { 400 checkInitialized(); 401 return super.elements(); 402 } 403 404 // let javadoc show doc from superclass 405 public String getProperty(String key) { 406 checkInitialized(); 407 return super.getProperty(key); 408 } 409 410 private void checkInitialized() { 411 if (!initialized) { 412 throw new IllegalStateException(); 413 } 414 } 415 416 private void check(String directive) { 417 checkInitialized(); 418 SecurityManager security = System.getSecurityManager(); 419 if (security != null) { 420 security.checkSecurityAccess(directive); 421 } 422 } 423 424 // legacy properties changed since last call to any services method? 425 private transient boolean legacyChanged; 426 // serviceMap changed since last call to getServices() 427 private transient boolean servicesChanged; 428 429 // Map<String,String> 430 private transient Map<String,String> legacyStrings; 431 432 // Map<ServiceKey,Service> 433 // used for services added via putService(), initialized on demand 434 private transient Map<ServiceKey,Service> serviceMap; 435 436 // Map<ServiceKey,Service> 437 // used for services added via legacy methods, init on demand 438 private transient Map<ServiceKey,Service> legacyMap; 439 440 // Set<Service> 441 // Unmodifiable set of all services. Initialized on demand. 442 private transient Set<Service> serviceSet; 443 444 // register the id attributes for this provider 445 // this is to ensure that equals() and hashCode() do not incorrectly 446 // report to different provider objects as the same 447 private void putId() { 448 // note: name and info may be null 449 super.put("Provider.id name", String.valueOf(name)); 450 super.put("Provider.id version", String.valueOf(version)); 451 super.put("Provider.id info", String.valueOf(info)); 452 super.put("Provider.id className", this.getClass().getName()); 453 } 454 455 private void readObject(ObjectInputStream in) 456 throws IOException, ClassNotFoundException { 457 registered = false; 458 Map<Object,Object> copy = new HashMap<>(); 459 for (Map.Entry<Object,Object> entry : super.entrySet()) { 460 copy.put(entry.getKey(), entry.getValue()); 461 } 462 defaults = null; 463 in.defaultReadObject(); 464 implClear(); 465 initialized = true; 466 putAll(copy); 467 } 468 469 /** 470 * Copies all of the mappings from the specified Map to this provider. 471 * Internal method to be called AFTER the security check has been 472 * performed. 473 */ 474 private void implPutAll(Map t) { 475 for (Map.Entry e : ((Map<?,?>)t).entrySet()) { 476 implPut(e.getKey(), e.getValue()); 477 } 478 if (registered) { 479 Security.increaseVersion(); 480 } 481 } 482 483 private Object implRemove(Object key) { 484 if (registered) { 485 Security.increaseVersion(); 486 } 487 if (key instanceof String) { 488 String keyString = (String)key; 489 if (keyString.startsWith("Provider.")) { 490 return null; 491 } 492 legacyChanged = true; 493 if (legacyStrings == null) { 494 legacyStrings = new LinkedHashMap<String,String>(); 495 } 496 legacyStrings.remove(keyString); 497 } 498 return super.remove(key); 499 } 500 501 private Object implPut(Object key, Object value) { 502 if ((key instanceof String) && (value instanceof String)) { 503 String keyString = (String)key; 504 if (keyString.startsWith("Provider.")) { 505 return null; 506 } 507 if (registered) { 508 Security.increaseVersion(); 509 } 510 legacyChanged = true; 511 if (legacyStrings == null) { 512 legacyStrings = new LinkedHashMap<String,String>(); 513 } 514 legacyStrings.put(keyString, (String)value); 515 } 516 return super.put(key, value); 517 } 518 519 private void implClear() { 520 if (legacyStrings != null) { 521 legacyStrings.clear(); 522 } 523 if (legacyMap != null) { 524 legacyMap.clear(); 525 } 526 if (serviceMap != null) { 527 serviceMap.clear(); 528 } 529 legacyChanged = false; 530 servicesChanged = false; 531 serviceSet = null; 532 super.clear(); 533 putId(); 534 if (registered) { 535 Security.increaseVersion(); 536 } 537 } 538 539 // used as key in the serviceMap and legacyMap HashMaps 540 private static class ServiceKey { 541 private final String type; 542 private final String algorithm; 543 private final String originalAlgorithm; 544 private ServiceKey(String type, String algorithm, boolean intern) { 545 this.type = type; 546 this.originalAlgorithm = algorithm; 547 algorithm = algorithm.toUpperCase(ENGLISH); 548 this.algorithm = intern ? algorithm.intern() : algorithm; 549 } 550 public int hashCode() { 551 return type.hashCode() + algorithm.hashCode(); 552 } 553 public boolean equals(Object obj) { 554 if (this == obj) { 555 return true; 556 } 557 if (obj instanceof ServiceKey == false) { 558 return false; 559 } 560 ServiceKey other = (ServiceKey)obj; 561 return this.type.equals(other.type) 562 && this.algorithm.equals(other.algorithm); 563 } 564 boolean matches(String type, String algorithm) { 565 return (this.type == type) && (this.originalAlgorithm == algorithm); 566 } 567 } 568 569 /** 570 * Ensure all the legacy String properties are fully parsed into 571 * service objects. 572 */ 573 private void ensureLegacyParsed() { 574 if ((legacyChanged == false) || (legacyStrings == null)) { 575 return; 576 } 577 serviceSet = null; 578 if (legacyMap == null) { 579 legacyMap = new LinkedHashMap<ServiceKey,Service>(); 580 } else { 581 legacyMap.clear(); 582 } 583 for (Map.Entry<String,String> entry : legacyStrings.entrySet()) { 584 parseLegacyPut(entry.getKey(), entry.getValue()); 585 } 586 removeInvalidServices(legacyMap); 587 legacyChanged = false; 588 } 589 590 /** 591 * Remove all invalid services from the Map. Invalid services can only 592 * occur if the legacy properties are inconsistent or incomplete. 593 */ 594 private void removeInvalidServices(Map<ServiceKey,Service> map) { 595 for (Iterator t = map.entrySet().iterator(); t.hasNext(); ) { 596 Map.Entry entry = (Map.Entry)t.next(); 597 Service s = (Service)entry.getValue(); 598 if (s.isValid() == false) { 599 t.remove(); 600 } 601 } 602 } 603 604 private String[] getTypeAndAlgorithm(String key) { 605 int i = key.indexOf("."); 606 if (i < 1) { 607 if (debug != null) { 608 debug.println("Ignoring invalid entry in provider " 609 + name + ":" + key); 610 } 611 return null; 612 } 613 String type = key.substring(0, i); 614 String alg = key.substring(i + 1); 615 return new String[] {type, alg}; 616 } 617 618 private final static String ALIAS_PREFIX = "Alg.Alias."; 619 private final static String ALIAS_PREFIX_LOWER = "alg.alias."; 620 private final static int ALIAS_LENGTH = ALIAS_PREFIX.length(); 621 622 private void parseLegacyPut(String name, String value) { 623 if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) { 624 // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1"); 625 // aliasKey ~ MessageDigest.SHA 626 String stdAlg = value; 627 String aliasKey = name.substring(ALIAS_LENGTH); 628 String[] typeAndAlg = getTypeAndAlgorithm(aliasKey); 629 if (typeAndAlg == null) { 630 return; 631 } 632 String type = getEngineName(typeAndAlg[0]); 633 String aliasAlg = typeAndAlg[1].intern(); 634 ServiceKey key = new ServiceKey(type, stdAlg, true); 635 Service s = legacyMap.get(key); 636 if (s == null) { 637 s = new Service(this); 638 s.type = type; 639 s.algorithm = stdAlg; 640 legacyMap.put(key, s); 641 } 642 legacyMap.put(new ServiceKey(type, aliasAlg, true), s); 643 s.addAlias(aliasAlg); 644 } else { 645 String[] typeAndAlg = getTypeAndAlgorithm(name); 646 if (typeAndAlg == null) { 647 return; 648 } 649 int i = typeAndAlg[1].indexOf(' '); 650 if (i == -1) { 651 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA"); 652 String type = getEngineName(typeAndAlg[0]); 653 String stdAlg = typeAndAlg[1].intern(); 654 String className = value; 655 ServiceKey key = new ServiceKey(type, stdAlg, true); 656 Service s = legacyMap.get(key); 657 if (s == null) { 658 s = new Service(this); 659 s.type = type; 660 s.algorithm = stdAlg; 661 legacyMap.put(key, s); 662 } 663 s.className = className; 664 } else { // attribute 665 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software"); 666 String attributeValue = value; 667 String type = getEngineName(typeAndAlg[0]); 668 String attributeString = typeAndAlg[1]; 669 String stdAlg = attributeString.substring(0, i).intern(); 670 String attributeName = attributeString.substring(i + 1); 671 // kill additional spaces 672 while (attributeName.startsWith(" ")) { 673 attributeName = attributeName.substring(1); 674 } 675 attributeName = attributeName.intern(); 676 ServiceKey key = new ServiceKey(type, stdAlg, true); 677 Service s = legacyMap.get(key); 678 if (s == null) { 679 s = new Service(this); 680 s.type = type; 681 s.algorithm = stdAlg; 682 legacyMap.put(key, s); 683 } 684 s.addAttribute(attributeName, attributeValue); 685 } 686 } 687 } 688 689 /** 690 * Get the service describing this Provider's implementation of the 691 * specified type of this algorithm or alias. If no such 692 * implementation exists, this method returns null. If there are two 693 * matching services, one added to this provider using 694 * {@link #putService putService()} and one added via {@link #put put()}, 695 * the service added via {@link #putService putService()} is returned. 696 * 697 * @param type the type of {@link Service service} requested 698 * (for example, <code>MessageDigest</code>) 699 * @param algorithm the case insensitive algorithm name (or alternate 700 * alias) of the service requested (for example, <code>SHA-1</code>) 701 * 702 * @return the service describing this Provider's matching service 703 * or null if no such service exists 704 * 705 * @throws NullPointerException if type or algorithm is null 706 * 707 * @since 1.5 708 */ 709 public synchronized Service getService(String type, String algorithm) { 710 checkInitialized(); 711 // avoid allocating a new key object if possible 712 ServiceKey key = previousKey; 713 if (key.matches(type, algorithm) == false) { 714 key = new ServiceKey(type, algorithm, false); 715 previousKey = key; 716 } 717 if (serviceMap != null) { 718 Service service = serviceMap.get(key); 719 if (service != null) { 720 return service; 721 } 722 } 723 ensureLegacyParsed(); 724 return (legacyMap != null) ? legacyMap.get(key) : null; 725 } 726 727 // ServiceKey from previous getService() call 728 // by re-using it if possible we avoid allocating a new object 729 // and the toUpperCase() call. 730 // re-use will occur e.g. as the framework traverses the provider 731 // list and queries each provider with the same values until it finds 732 // a matching service 733 private static volatile ServiceKey previousKey = 734 new ServiceKey("", "", false); 735 736 /** 737 * Get an unmodifiable Set of all services supported by 738 * this Provider. 739 * 740 * @return an unmodifiable Set of all services supported by 741 * this Provider 742 * 743 * @since 1.5 744 */ 745 public synchronized Set<Service> getServices() { 746 checkInitialized(); 747 if (legacyChanged || servicesChanged) { 748 serviceSet = null; 749 } 750 if (serviceSet == null) { 751 ensureLegacyParsed(); 752 Set<Service> set = new LinkedHashSet<>(); 753 if (serviceMap != null) { 754 set.addAll(serviceMap.values()); 755 } 756 if (legacyMap != null) { 757 set.addAll(legacyMap.values()); 758 } 759 serviceSet = Collections.unmodifiableSet(set); 760 servicesChanged = false; 761 } 762 return serviceSet; 763 } 764 765 /** 766 * Add a service. If a service of the same type with the same algorithm 767 * name exists and it was added using {@link #putService putService()}, 768 * it is replaced by the new service. 769 * This method also places information about this service 770 * in the provider's Hashtable values in the format described in the 771 * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html"> 772 * Java Cryptography Architecture API Specification & Reference </a>. 773 * 774 * <p>Also, if there is a security manager, its 775 * <code>checkSecurityAccess</code> method is called with the string 776 * <code>"putProviderProperty."+name</code>, where <code>name</code> is 777 * the provider name, to see if it's ok to set this provider's property 778 * values. If the default implementation of <code>checkSecurityAccess</code> 779 * is used (that is, that method is not overriden), then this results in 780 * a call to the security manager's <code>checkPermission</code> method with 781 * a <code>SecurityPermission("putProviderProperty."+name)</code> 782 * permission. 783 * 784 * @param s the Service to add 785 * 786 * @throws SecurityException 787 * if a security manager exists and its <code>{@link 788 * java.lang.SecurityManager#checkSecurityAccess}</code> method denies 789 * access to set property values. 790 * @throws NullPointerException if s is null 791 * 792 * @since 1.5 793 */ 794 protected synchronized void putService(Service s) { 795 check("putProviderProperty." + name); 796 if (debug != null) { 797 debug.println(name + ".putService(): " + s); 798 } 799 if (s == null) { 800 throw new NullPointerException(); 801 } 802 if (s.getProvider() != this) { 803 throw new IllegalArgumentException 804 ("service.getProvider() must match this Provider object"); 805 } 806 if (serviceMap == null) { 807 serviceMap = new LinkedHashMap<ServiceKey,Service>(); 808 } 809 servicesChanged = true; 810 String type = s.getType(); 811 String algorithm = s.getAlgorithm(); 812 ServiceKey key = new ServiceKey(type, algorithm, true); 813 // remove existing service 814 implRemoveService(serviceMap.get(key)); 815 serviceMap.put(key, s); 816 for (String alias : s.getAliases()) { 817 serviceMap.put(new ServiceKey(type, alias, true), s); 818 } 819 putPropertyStrings(s); 820 } 821 822 /** 823 * Put the string properties for this Service in this Provider's 824 * Hashtable. 825 */ 826 private void putPropertyStrings(Service s) { 827 String type = s.getType(); 828 String algorithm = s.getAlgorithm(); 829 // use super() to avoid permission check and other processing 830 super.put(type + "." + algorithm, s.getClassName()); 831 for (String alias : s.getAliases()) { 832 super.put(ALIAS_PREFIX + type + "." + alias, algorithm); 833 } 834 for (Map.Entry<UString,String> entry : s.attributes.entrySet()) { 835 String key = type + "." + algorithm + " " + entry.getKey(); 836 super.put(key, entry.getValue()); 837 } 838 if (registered) { 839 Security.increaseVersion(); 840 } 841 } 842 843 /** 844 * Remove the string properties for this Service from this Provider's 845 * Hashtable. 846 */ 847 private void removePropertyStrings(Service s) { 848 String type = s.getType(); 849 String algorithm = s.getAlgorithm(); 850 // use super() to avoid permission check and other processing 851 super.remove(type + "." + algorithm); 852 for (String alias : s.getAliases()) { 853 super.remove(ALIAS_PREFIX + type + "." + alias); 854 } 855 for (Map.Entry<UString,String> entry : s.attributes.entrySet()) { 856 String key = type + "." + algorithm + " " + entry.getKey(); 857 super.remove(key); 858 } 859 if (registered) { 860 Security.increaseVersion(); 861 } 862 } 863 864 /** 865 * Remove a service previously added using 866 * {@link #putService putService()}. The specified service is removed from 867 * this provider. It will no longer be returned by 868 * {@link #getService getService()} and its information will be removed 869 * from this provider's Hashtable. 870 * 871 * <p>Also, if there is a security manager, its 872 * <code>checkSecurityAccess</code> method is called with the string 873 * <code>"removeProviderProperty."+name</code>, where <code>name</code> is 874 * the provider name, to see if it's ok to remove this provider's 875 * properties. If the default implementation of 876 * <code>checkSecurityAccess</code> is used (that is, that method is not 877 * overriden), then this results in a call to the security manager's 878 * <code>checkPermission</code> method with a 879 * <code>SecurityPermission("removeProviderProperty."+name)</code> 880 * permission. 881 * 882 * @param s the Service to be removed 883 * 884 * @throws SecurityException 885 * if a security manager exists and its <code>{@link 886 * java.lang.SecurityManager#checkSecurityAccess}</code> method denies 887 * access to remove this provider's properties. 888 * @throws NullPointerException if s is null 889 * 890 * @since 1.5 891 */ 892 protected synchronized void removeService(Service s) { 893 check("removeProviderProperty." + name); 894 if (debug != null) { 895 debug.println(name + ".removeService(): " + s); 896 } 897 if (s == null) { 898 throw new NullPointerException(); 899 } 900 implRemoveService(s); 901 } 902 903 private void implRemoveService(Service s) { 904 if ((s == null) || (serviceMap == null)) { 905 return; 906 } 907 String type = s.getType(); 908 String algorithm = s.getAlgorithm(); 909 ServiceKey key = new ServiceKey(type, algorithm, false); 910 Service oldService = serviceMap.get(key); 911 if (s != oldService) { 912 return; 913 } 914 servicesChanged = true; 915 serviceMap.remove(key); 916 for (String alias : s.getAliases()) { 917 serviceMap.remove(new ServiceKey(type, alias, false)); 918 } 919 removePropertyStrings(s); 920 } 921 922 // Wrapped String that behaves in a case insensitive way for equals/hashCode 923 private static class UString { 924 final String string; 925 final String lowerString; 926 927 UString(String s) { 928 this.string = s; 929 this.lowerString = s.toLowerCase(ENGLISH); 930 } 931 932 public int hashCode() { 933 return lowerString.hashCode(); 934 } 935 936 public boolean equals(Object obj) { 937 if (this == obj) { 938 return true; 939 } 940 if (obj instanceof UString == false) { 941 return false; 942 } 943 UString other = (UString)obj; 944 return lowerString.equals(other.lowerString); 945 } 946 947 public String toString() { 948 return string; 949 } 950 } 951 952 // describe relevant properties of a type of engine 953 private static class EngineDescription { 954 final String name; 955 final boolean supportsParameter; 956 final String constructorParameterClassName; 957 private volatile Class constructorParameterClass; 958 959 EngineDescription(String name, boolean sp, String paramName) { 960 this.name = name; 961 this.supportsParameter = sp; 962 this.constructorParameterClassName = paramName; 963 } 964 Class getConstructorParameterClass() throws ClassNotFoundException { 965 Class clazz = constructorParameterClass; 966 if (clazz == null) { 967 clazz = Class.forName(constructorParameterClassName); 968 constructorParameterClass = clazz; 969 } 970 return clazz; 971 } 972 } 973 974 // built in knowledge of the engine types shipped as part of the JDK 975 private static final Map<String,EngineDescription> knownEngines; 976 977 private static void addEngine(String name, boolean sp, String paramName) { 978 EngineDescription ed = new EngineDescription(name, sp, paramName); 979 // also index by canonical name to avoid toLowerCase() for some lookups 980 knownEngines.put(name.toLowerCase(ENGLISH), ed); 981 knownEngines.put(name, ed); 982 } 983 984 static { 985 knownEngines = new HashMap<String,EngineDescription>(); 986 // JCA 987 addEngine("AlgorithmParameterGenerator", false, null); 988 addEngine("AlgorithmParameters", false, null); 989 addEngine("KeyFactory", false, null); 990 addEngine("KeyPairGenerator", false, null); 991 addEngine("KeyStore", false, null); 992 addEngine("MessageDigest", false, null); 993 addEngine("SecureRandom", false, null); 994 addEngine("Signature", true, null); 995 addEngine("CertificateFactory", false, null); 996 addEngine("CertPathBuilder", false, null); 997 addEngine("CertPathValidator", false, null); 998 addEngine("CertStore", false, 999 "java.security.cert.CertStoreParameters"); 1000 // JCE 1001 addEngine("Cipher", true, null); 1002 addEngine("ExemptionMechanism", false, null); 1003 addEngine("Mac", true, null); 1004 addEngine("KeyAgreement", true, null); 1005 addEngine("KeyGenerator", false, null); 1006 addEngine("SecretKeyFactory", false, null); 1007 // JSSE 1008 addEngine("KeyManagerFactory", false, null); 1009 addEngine("SSLContext", false, null); 1010 addEngine("TrustManagerFactory", false, null); 1011 // JGSS 1012 addEngine("GssApiMechanism", false, null); 1013 // SASL 1014 addEngine("SaslClientFactory", false, null); 1015 addEngine("SaslServerFactory", false, null); 1016 // POLICY 1017 addEngine("Policy", false, 1018 "java.security.Policy$Parameters"); 1019 // CONFIGURATION 1020 addEngine("Configuration", false, 1021 "javax.security.auth.login.Configuration$Parameters"); 1022 // XML DSig 1023 addEngine("XMLSignatureFactory", false, null); 1024 addEngine("KeyInfoFactory", false, null); 1025 addEngine("TransformService", false, null); 1026 // Smart Card I/O 1027 addEngine("TerminalFactory", false, 1028 "java.lang.Object"); 1029 } 1030 1031 // get the "standard" (mixed-case) engine name for arbitary case engine name 1032 // if there is no known engine by that name, return s 1033 private static String getEngineName(String s) { 1034 // try original case first, usually correct 1035 EngineDescription e = knownEngines.get(s); 1036 if (e == null) { 1037 e = knownEngines.get(s.toLowerCase(ENGLISH)); 1038 } 1039 return (e == null) ? s : e.name; 1040 } 1041 1042 /** 1043 * The description of a security service. It encapsulates the properties 1044 * of a service and contains a factory method to obtain new implementation 1045 * instances of this service. 1046 * 1047 * <p>Each service has a provider that offers the service, a type, 1048 * an algorithm name, and the name of the class that implements the 1049 * service. Optionally, it also includes a list of alternate algorithm 1050 * names for this service (aliases) and attributes, which are a map of 1051 * (name, value) String pairs. 1052 * 1053 * <p>This class defines the methods {@link #supportsParameter 1054 * supportsParameter()} and {@link #newInstance newInstance()} 1055 * which are used by the Java security framework when it searches for 1056 * suitable services and instantes them. The valid arguments to those 1057 * methods depend on the type of service. For the service types defined 1058 * within Java SE, see the 1059 * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html"> 1060 * Java Cryptography Architecture API Specification & Reference </a> 1061 * for the valid values. 1062 * Note that components outside of Java SE can define additional types of 1063 * services and their behavior. 1064 * 1065 * <p>Instances of this class are immutable. 1066 * 1067 * @since 1.5 1068 */ 1069 public static class Service { 1070 1071 private String type, algorithm, className; 1072 private final Provider provider; 1073 private List<String> aliases; 1074 private Map<UString,String> attributes; 1075 1076 // Reference to the cached implementation Class object 1077 private volatile Reference<Class> classRef; 1078 1079 // flag indicating whether this service has its attributes for 1080 // supportedKeyFormats or supportedKeyClasses set 1081 // if null, the values have not been initialized 1082 // if TRUE, at least one of supportedFormats/Classes is non null 1083 private volatile Boolean hasKeyAttributes; 1084 1085 // supported encoding formats 1086 private String[] supportedFormats; 1087 1088 // names of the supported key (super) classes 1089 private Class[] supportedClasses; 1090 1091 // whether this service has been registered with the Provider 1092 private boolean registered; 1093 1094 private static final Class[] CLASS0 = new Class[0]; 1095 1096 // this constructor and these methods are used for parsing 1097 // the legacy string properties. 1098 1099 private Service(Provider provider) { 1100 this.provider = provider; 1101 aliases = Collections.<String>emptyList(); 1102 attributes = Collections.<UString,String>emptyMap(); 1103 } 1104 1105 private boolean isValid() { 1106 return (type != null) && (algorithm != null) && (className != null); 1107 } 1108 1109 private void addAlias(String alias) { 1110 if (aliases.isEmpty()) { 1111 aliases = new ArrayList<String>(2); 1112 } 1113 aliases.add(alias); 1114 } 1115 1116 void addAttribute(String type, String value) { 1117 if (attributes.isEmpty()) { 1118 attributes = new HashMap<UString,String>(8); 1119 } 1120 attributes.put(new UString(type), value); 1121 } 1122 1123 /** 1124 * Construct a new service. 1125 * 1126 * @param provider the provider that offers this service 1127 * @param type the type of this service 1128 * @param algorithm the algorithm name 1129 * @param className the name of the class implementing this service 1130 * @param aliases List of aliases or null if algorithm has no aliases 1131 * @param attributes Map of attributes or null if this implementation 1132 * has no attributes 1133 * 1134 * @throws NullPointerException if provider, type, algorithm, or 1135 * className is null 1136 */ 1137 public Service(Provider provider, String type, String algorithm, 1138 String className, List<String> aliases, 1139 Map<String,String> attributes) { 1140 if ((provider == null) || (type == null) || 1141 (algorithm == null) || (className == null)) { 1142 throw new NullPointerException(); 1143 } 1144 this.provider = provider; 1145 this.type = getEngineName(type); 1146 this.algorithm = algorithm; 1147 this.className = className; 1148 if (aliases == null) { 1149 this.aliases = Collections.<String>emptyList(); 1150 } else { 1151 this.aliases = new ArrayList<String>(aliases); 1152 } 1153 if (attributes == null) { 1154 this.attributes = Collections.<UString,String>emptyMap(); 1155 } else { 1156 this.attributes = new HashMap<UString,String>(); 1157 for (Map.Entry<String,String> entry : attributes.entrySet()) { 1158 this.attributes.put(new UString(entry.getKey()), entry.getValue()); 1159 } 1160 } 1161 } 1162 1163 /** 1164 * Get the type of this service. For example, <code>MessageDigest</code>. 1165 * 1166 * @return the type of this service 1167 */ 1168 public final String getType() { 1169 return type; 1170 } 1171 1172 /** 1173 * Return the name of the algorithm of this service. For example, 1174 * <code>SHA-1</code>. 1175 * 1176 * @return the algorithm of this service 1177 */ 1178 public final String getAlgorithm() { 1179 return algorithm; 1180 } 1181 1182 /** 1183 * Return the Provider of this service. 1184 * 1185 * @return the Provider of this service 1186 */ 1187 public final Provider getProvider() { 1188 return provider; 1189 } 1190 1191 /** 1192 * Return the name of the class implementing this service. 1193 * 1194 * @return the name of the class implementing this service 1195 */ 1196 public final String getClassName() { 1197 return className; 1198 } 1199 1200 // internal only 1201 private final List<String> getAliases() { 1202 return aliases; 1203 } 1204 1205 /** 1206 * Return the value of the specified attribute or null if this 1207 * attribute is not set for this Service. 1208 * 1209 * @param name the name of the requested attribute 1210 * 1211 * @return the value of the specified attribute or null if the 1212 * attribute is not present 1213 * 1214 * @throws NullPointerException if name is null 1215 */ 1216 public final String getAttribute(String name) { 1217 if (name == null) { 1218 throw new NullPointerException(); 1219 } 1220 return attributes.get(new UString(name)); 1221 } 1222 1223 /** 1224 * Return a new instance of the implementation described by this 1225 * service. The security provider framework uses this method to 1226 * construct implementations. Applications will typically not need 1227 * to call it. 1228 * 1229 * <p>The default implementation uses reflection to invoke the 1230 * standard constructor for this type of service. 1231 * Security providers can override this method to implement 1232 * instantiation in a different way. 1233 * For details and the values of constructorParameter that are 1234 * valid for the various types of services see the 1235 * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html"> 1236 * Java Cryptography Architecture API Specification & 1237 * Reference</a>. 1238 * 1239 * @param constructorParameter the value to pass to the constructor, 1240 * or null if this type of service does not use a constructorParameter. 1241 * 1242 * @return a new implementation of this service 1243 * 1244 * @throws InvalidParameterException if the value of 1245 * constructorParameter is invalid for this type of service. 1246 * @throws NoSuchAlgorithmException if instantation failed for 1247 * any other reason. 1248 */ 1249 public Object newInstance(Object constructorParameter) 1250 throws NoSuchAlgorithmException { 1251 if (registered == false) { 1252 if (provider.getService(type, algorithm) != this) { 1253 throw new NoSuchAlgorithmException 1254 ("Service not registered with Provider " 1255 + provider.getName() + ": " + this); 1256 } 1257 registered = true; 1258 } 1259 try { 1260 EngineDescription cap = knownEngines.get(type); 1261 if (cap == null) { 1262 // unknown engine type, use generic code 1263 // this is the code path future for non-core 1264 // optional packages 1265 return newInstanceGeneric(constructorParameter); 1266 } 1267 if (cap.constructorParameterClassName == null) { 1268 if (constructorParameter != null) { 1269 throw new InvalidParameterException 1270 ("constructorParameter not used with " + type 1271 + " engines"); 1272 } 1273 Class clazz = getImplClass(); 1274 return clazz.newInstance(); 1275 } else { 1276 Class paramClass = cap.getConstructorParameterClass(); 1277 if (constructorParameter != null) { 1278 Class argClass = constructorParameter.getClass(); 1279 if (paramClass.isAssignableFrom(argClass) == false) { 1280 throw new InvalidParameterException 1281 ("constructorParameter must be instanceof " 1282 + cap.constructorParameterClassName.replace('$', '.') 1283 + " for engine type " + type); 1284 } 1285 } 1286 Class clazz = getImplClass(); 1287 Constructor cons = clazz.getConstructor(paramClass); 1288 return cons.newInstance(constructorParameter); 1289 } 1290 } catch (NoSuchAlgorithmException e) { 1291 throw e; 1292 } catch (InvocationTargetException e) { 1293 throw new NoSuchAlgorithmException 1294 ("Error constructing implementation (algorithm: " 1295 + algorithm + ", provider: " + provider.getName() 1296 + ", class: " + className + ")", e.getCause()); 1297 } catch (Exception e) { 1298 throw new NoSuchAlgorithmException 1299 ("Error constructing implementation (algorithm: " 1300 + algorithm + ", provider: " + provider.getName() 1301 + ", class: " + className + ")", e); 1302 } 1303 } 1304 1305 // return the implementation Class object for this service 1306 private Class getImplClass() throws NoSuchAlgorithmException { 1307 try { 1308 Reference<Class> ref = classRef; 1309 Class clazz = (ref == null) ? null : ref.get(); 1310 if (clazz == null) { 1311 ClassLoader cl = provider.getClass().getClassLoader(); 1312 if (cl == null) { 1313 clazz = Class.forName(className); 1314 } else { 1315 clazz = cl.loadClass(className); 1316 } 1317 classRef = new WeakReference<Class>(clazz); 1318 } 1319 return clazz; 1320 } catch (ClassNotFoundException e) { 1321 throw new NoSuchAlgorithmException 1322 ("class configured for " + type + "(provider: " + 1323 provider.getName() + ")" + "cannot be found.", e); 1324 } 1325 } 1326 1327 /** 1328 * Generic code path for unknown engine types. Call the 1329 * no-args constructor if constructorParameter is null, otherwise 1330 * use the first matching constructor. 1331 */ 1332 private Object newInstanceGeneric(Object constructorParameter) 1333 throws Exception { 1334 Class clazz = getImplClass(); 1335 if (constructorParameter == null) { 1336 Object o = clazz.newInstance(); 1337 return o; 1338 } 1339 Class argClass = constructorParameter.getClass(); 1340 Constructor[] cons = clazz.getConstructors(); 1341 // find first public constructor that can take the 1342 // argument as parameter 1343 for (int i = 0; i < cons.length; i++) { 1344 Constructor con = cons[i]; 1345 Class[] paramTypes = con.getParameterTypes(); 1346 if (paramTypes.length != 1) { 1347 continue; 1348 } 1349 if (paramTypes[0].isAssignableFrom(argClass) == false) { 1350 continue; 1351 } 1352 Object o = con.newInstance(new Object[] {constructorParameter}); 1353 return o; 1354 } 1355 throw new NoSuchAlgorithmException("No constructor matching " 1356 + argClass.getName() + " found in class " + className); 1357 } 1358 1359 /** 1360 * Test whether this Service can use the specified parameter. 1361 * Returns false if this service cannot use the parameter. Returns 1362 * true if this service can use the parameter, if a fast test is 1363 * infeasible, or if the status is unknown. 1364 * 1365 * <p>The security provider framework uses this method with 1366 * some types of services to quickly exclude non-matching 1367 * implementations for consideration. 1368 * Applications will typically not need to call it. 1369 * 1370 * <p>For details and the values of parameter that are valid for the 1371 * various types of services see the top of this class and the 1372 * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html"> 1373 * Java Cryptography Architecture API Specification & 1374 * Reference</a>. 1375 * Security providers can override it to implement their own test. 1376 * 1377 * @param parameter the parameter to test 1378 * 1379 * @return false if this this service cannot use the specified 1380 * parameter; true if it can possibly use the parameter 1381 * 1382 * @throws InvalidParameterException if the value of parameter is 1383 * invalid for this type of service or if this method cannot be 1384 * used with this type of service 1385 */ 1386 public boolean supportsParameter(Object parameter) { 1387 EngineDescription cap = knownEngines.get(type); 1388 if (cap == null) { 1389 // unknown engine type, return true by default 1390 return true; 1391 } 1392 if (cap.supportsParameter == false) { 1393 throw new InvalidParameterException("supportsParameter() not " 1394 + "used with " + type + " engines"); 1395 } 1396 // allow null for keys without attributes for compatibility 1397 if ((parameter != null) && (parameter instanceof Key == false)) { 1398 throw new InvalidParameterException 1399 ("Parameter must be instanceof Key for engine " + type); 1400 } 1401 if (hasKeyAttributes() == false) { 1402 return true; 1403 } 1404 if (parameter == null) { 1405 return false; 1406 } 1407 Key key = (Key)parameter; 1408 if (supportsKeyFormat(key)) { 1409 return true; 1410 } 1411 if (supportsKeyClass(key)) { 1412 return true; 1413 } 1414 return false; 1415 } 1416 1417 /** 1418 * Return whether this service has its Supported* properties for 1419 * keys defined. Parses the attributes if not yet initialized. 1420 */ 1421 private boolean hasKeyAttributes() { 1422 Boolean b = hasKeyAttributes; 1423 if (b == null) { 1424 synchronized (this) { 1425 String s; 1426 s = getAttribute("SupportedKeyFormats"); 1427 if (s != null) { 1428 supportedFormats = s.split("\\|"); 1429 } 1430 s = getAttribute("SupportedKeyClasses"); 1431 if (s != null) { 1432 String[] classNames = s.split("\\|"); 1433 List<Class> classList = 1434 new ArrayList<>(classNames.length); 1435 for (String className : classNames) { 1436 Class clazz = getKeyClass(className); 1437 if (clazz != null) { 1438 classList.add(clazz); 1439 } 1440 } 1441 supportedClasses = classList.toArray(CLASS0); 1442 } 1443 boolean bool = (supportedFormats != null) 1444 || (supportedClasses != null); 1445 b = Boolean.valueOf(bool); 1446 hasKeyAttributes = b; 1447 } 1448 } 1449 return b.booleanValue(); 1450 } 1451 1452 // get the key class object of the specified name 1453 private Class getKeyClass(String name) { 1454 try { 1455 return Class.forName(name); 1456 } catch (ClassNotFoundException e) { 1457 // ignore 1458 } 1459 try { 1460 ClassLoader cl = provider.getClass().getClassLoader(); 1461 if (cl != null) { 1462 return cl.loadClass(name); 1463 } 1464 } catch (ClassNotFoundException e) { 1465 // ignore 1466 } 1467 return null; 1468 } 1469 1470 private boolean supportsKeyFormat(Key key) { 1471 if (supportedFormats == null) { 1472 return false; 1473 } 1474 String format = key.getFormat(); 1475 if (format == null) { 1476 return false; 1477 } 1478 for (String supportedFormat : supportedFormats) { 1479 if (supportedFormat.equals(format)) { 1480 return true; 1481 } 1482 } 1483 return false; 1484 } 1485 1486 private boolean supportsKeyClass(Key key) { 1487 if (supportedClasses == null) { 1488 return false; 1489 } 1490 Class keyClass = key.getClass(); 1491 for (Class clazz : supportedClasses) { 1492 if (clazz.isAssignableFrom(keyClass)) { 1493 return true; 1494 } 1495 } 1496 return false; 1497 } 1498 1499 /** 1500 * Return a String representation of this service. 1501 * 1502 * @return a String representation of this service. 1503 */ 1504 public String toString() { 1505 String aString = aliases.isEmpty() 1506 ? "" : "\r\n aliases: " + aliases.toString(); 1507 String attrs = attributes.isEmpty() 1508 ? "" : "\r\n attributes: " + attributes.toString(); 1509 return provider.getName() + ": " + type + "." + algorithm 1510 + " -> " + className + aString + attrs + "\r\n"; 1511 } 1512 1513 } 1514 1515 /** 1516 * @hide 1517 */ 1518 public void setRegistered() { 1519 registered = true; 1520 } 1521 1522 /** 1523 * @hide 1524 */ 1525 public void setUnregistered() { 1526 registered = false; 1527 } 1528 1529 /** 1530 * @hide 1531 */ 1532 public boolean isRegistered() { 1533 return registered; 1534 } 1535 1536} 1537