diff options
Diffstat (limited to 'luni/src/main/java/java/security/Provider.java')
-rw-r--r-- | luni/src/main/java/java/security/Provider.java | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/luni/src/main/java/java/security/Provider.java b/luni/src/main/java/java/security/Provider.java new file mode 100644 index 0000000..cf9c94d --- /dev/null +++ b/luni/src/main/java/java/security/Provider.java @@ -0,0 +1,1164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.security; + +import java.io.IOException; +import java.io.InputStream; +import java.io.NotActiveException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.harmony.luni.util.TwoKeyHashMap; +import org.apache.harmony.security.Util; +import org.apache.harmony.security.fortress.Services; +import org.apache.harmony.security.internal.nls.Messages; + +/** + * {@code Provider} is the abstract superclass for all security providers in the + * Java security infrastructure. + */ +public abstract class Provider extends Properties { + private static final long serialVersionUID = -4298000515446427739L; + + private String name; + + private double version; + + // String representation of the provider version number. + private transient String versionString; + + private String info; + + //The provider preference order number. + // Equals -1 for non registered provider. + private transient int providerNumber = -1; + + // Contains "Service.Algorithm" and Provider.Service classes added using + // putService() + private transient TwoKeyHashMap<String, String, Service> serviceTable; + + // Contains "Service.Alias" and Provider.Service classes added using + // putService() + private transient TwoKeyHashMap<String, String, Service> aliasTable; + + // Contains "Service.Algorithm" and Provider.Service classes added using + // put() + private transient TwoKeyHashMap<String, String, Service> propertyServiceTable; + + // Contains "Service.Alias" and Provider.Service classes added using put() + private transient TwoKeyHashMap<String, String, Service> propertyAliasTable; + + // The properties changed via put() + private transient Properties changedProperties; + + // For getService(String type, String algorithm) optimization: + // previous result + private transient Provider.Service returnedService; + // previous parameters + private transient String lastAlgorithm; + // last name + private transient String lastServiceName; + + // For getServices() optimization: + private transient Set<Service> lastServicesSet; + + // For getService(String type) optimization: + private transient String lastType; + // last Service found by type + private transient Provider.Service lastServicesByType; + + /** + * Constructs a new instance of {@code Provider} with its name, version and + * description. + * + * @param name + * the name of the provider. + * @param version + * the version of the provider. + * @param info + * a description of the provider. + */ + protected Provider(String name, double version, String info) { + this.name = name; + this.version = version; + this.info = info; + versionString = String.valueOf(version); + putProviderInfo(); + } + + /** + * Returns the name of this provider. + * + * @return the name of this provider. + */ + public String getName() { + return name; + } + + /** + * Returns the version number for the services being provided. + * + * @return the version number for the services being provided. + */ + public double getVersion() { + return version; + } + + /** + * Returns a description of the services being provided. + * + * @return a description of the services being provided. + */ + public String getInfo() { + return info; + } + + /** + * Returns a string containing a concise, human-readable description of + * this {@code Provider} including its name and its version. + * + * @return a printable representation for this {@code Provider}. + */ + @Override + public String toString() { + return name + " version " + version; //$NON-NLS-1$ + } + + /** + * Clears all properties used to look up services implemented by this + * {@code Provider}. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code clearProviderProperties.NAME} + * (where NAME is the provider name) to be granted, otherwise a {@code + * SecurityException} will be thrown. + * + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this method. + */ + @Override + public synchronized void clear() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkSecurityAccess("clearProviderProperties." + name); //$NON-NLS-1$ + } + super.clear(); + if (serviceTable != null) { + serviceTable.clear(); + } + if (propertyServiceTable != null) { + propertyServiceTable.clear(); + } + if (aliasTable != null) { + aliasTable.clear(); + } + if (propertyAliasTable != null) { + propertyAliasTable.clear(); + } + // BEGIN android-changed + changedProperties = null; + // END android-changed + putProviderInfo(); + if (providerNumber != -1) { + // if registered then refresh Services + Services.setNeedRefresh(); + } + servicesChanged(); + } + + @Override + public synchronized void load(InputStream inStream) throws IOException { + Properties tmp = new Properties(); + tmp.load(inStream); + myPutAll(tmp); + } + + /** + * Copies all from the provided map to this {@code Provider}. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where + * NAME is the provider name) to be granted, otherwise a {@code + * SecurityException} will be thrown. + * + * @param t + * the mappings to copy to this provider. + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this method. + */ + @Override + public synchronized void putAll(Map<?,?> t) { + + // Implementation note: + // checkSecurityAccess method call is NOT specified + // Do it as in put(Object key, Object value). + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkSecurityAccess("putProviderProperty." + name); //$NON-NLS-1$ + } + myPutAll(t); + } + + private void myPutAll(Map<?,?> t) { + if (changedProperties == null) { + changedProperties = new Properties(); + } + Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator(); + Object key; + Object value; + while (it.hasNext()) { + Map.Entry<?, ?> entry = it.next(); + key = entry.getKey(); + if (key instanceof String && ((String) key).startsWith("Provider.")) { //$NON-NLS-1$ + // Provider service type is reserved + continue; + } + value = entry.getValue(); + super.put(key, value); + if (changedProperties.remove(key) == null) { + removeFromPropertyServiceTable(key); + } + changedProperties.put(key, value); + } + if (providerNumber != -1) { + // if registered then refresh Services + Services.setNeedRefresh(); + } + } + + @Override + public synchronized Set<Map.Entry<Object,Object>> entrySet() { + return Collections.unmodifiableSet(super.entrySet()); + } + + @Override + public Set<Object> keySet() { + return Collections.unmodifiableSet(super.keySet()); + } + + @Override + public Collection<Object> values() { + return Collections.unmodifiableCollection(super.values()); + } + + /** + * Maps the specified {@code key} property name to the specified {@code + * value}. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where + * NAME is the provider name) to be granted, otherwise a {@code + * SecurityException} will be thrown. + * + * @param key + * the name of the property. + * @param value + * the value of the property. + * @return the value that was previously mapped to the specified {@code key} + * ,or {@code null} if it did not have one. + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this method. + */ + @Override + public synchronized Object put(Object key, Object value) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkSecurityAccess("putProviderProperty." + name); //$NON-NLS-1$ + } + if (key instanceof String && ((String) key).startsWith("Provider.")) { //$NON-NLS-1$ + // Provider service type is reserved + return null; + } + if (providerNumber != -1) { + // if registered then refresh Services + Services.setNeedRefresh(); + } + if (changedProperties != null && changedProperties.remove(key) == null) { + removeFromPropertyServiceTable(key); + } + if (changedProperties == null) { + changedProperties = new Properties(); + } + changedProperties.put(key, value); + return super.put(key, value); + } + + /** + * Removes the specified {@code key} and its associated value from this + * {@code Provider}. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code removeProviderProperty.NAME} (where + * NAME is the provider name) to be granted, otherwise a {@code + * SecurityException} will be thrown. + * + * @param key + * the name of the property + * @return the value that was mapped to the specified {@code key} ,or + * {@code null} if no mapping was present + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have the permission to invoke this method. + */ + @Override + public synchronized Object remove(Object key) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkSecurityAccess("removeProviderProperty." + name); //$NON-NLS-1$ + } + if (key instanceof String && ((String) key).startsWith("Provider.")) { //$NON-NLS-1$ + // Provider service type is reserved + return null; + } + if (providerNumber != -1) { + // if registered then refresh Services + Services.setNeedRefresh(); + } + if (changedProperties != null && changedProperties.remove(key) == null) { + removeFromPropertyServiceTable(key); + // BEGIN android-added + if (changedProperties.size() == 0) { + changedProperties = null; + } + // END android-added + } + return super.remove(key); + } + + /** + * Returns true if this provider implements the given algorithm. Caller + * must specify the cryptographic service and specify constraints via the + * attribute name and value. + * + * @param serv + * Crypto service. + * @param alg + * Algorithm or type. + * @param attribute + * The attribute name or {@code null}. + * @param val + * The attribute value. + * @return + */ + boolean implementsAlg(String serv, String alg, String attribute, String val) { + String servAlg = serv + "." + alg; //$NON-NLS-1$ + String prop = getPropertyIgnoreCase(servAlg); + if (prop == null) { + alg = getPropertyIgnoreCase("Alg.Alias." + servAlg); //$NON-NLS-1$ + if (alg != null) { + servAlg = serv + "." + alg; //$NON-NLS-1$ + prop = getPropertyIgnoreCase(servAlg); + } + } + if (prop != null) { + if (attribute == null) { + return true; + } + return checkAttribute(servAlg, attribute, val); + } + return false; + } + + // Returns true if this provider has the same value as is given for the + // given attribute + private boolean checkAttribute(String servAlg, String attribute, String val) { + + String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute); + if (attributeValue != null) { + if (Util.equalsIgnoreCase(attribute,"KeySize")) { //$NON-NLS-1$ + // BEGIN android-changed + if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) { + return true; + } + // END android-changed + } else { // other attributes + if (Util.equalsIgnoreCase(attributeValue, val)) { + return true; + } + } + } + return false; + } + + /** + * + * Set the provider preference order number. + * + * @param n + */ + void setProviderNumber(int n) { + providerNumber = n; + } + + /** + * + * Get the provider preference order number. + * + * @return + */ + int getProviderNumber() { + return providerNumber; + } + + /** + * Get the service of the specified type + * + */ + synchronized Provider.Service getService(String type) { + updatePropertyServiceTable(); + if (lastServicesByType != null && type.equals(lastType)) { + return lastServicesByType; + } + Provider.Service service; + for (Iterator<Service> it = getServices().iterator(); it.hasNext();) { + service = it.next(); + if (type.equals(service.type)) { + lastType = type; + lastServicesByType = service; + return service; + } + } + return null; + } + + /** + * Returns the service with the specified {@code type} implementing the + * specified {@code algorithm}, or {@code null} if no such implementation + * exists. + * <p> + * If two services match the requested type and algorithm, the one added + * with the {@link #putService(Service)} is returned (as opposed to the one + * added via {@link #put(Object, Object)}. + * + * @param type + * the type of the service (for example {@code KeyPairGenerator}) + * @param algorithm + * the algorithm name (case insensitive) + * @return the requested service, or {@code null} if no such implementation + * exists + */ + public synchronized Provider.Service getService(String type, + String algorithm) { + if (type == null || algorithm == null) { + throw new NullPointerException(); + } + + if (type.equals(lastServiceName) + && Util.equalsIgnoreCase(algorithm, lastAlgorithm)) { + return returnedService; + } + + String alg = Util.toUpperCase(algorithm); + Object o = null; + if (serviceTable != null) { + o = serviceTable.get(type, alg); + } + if (o == null && aliasTable != null) { + o = aliasTable.get(type, alg); + } + if (o == null) { + updatePropertyServiceTable(); + } + if (o == null && propertyServiceTable != null) { + o = propertyServiceTable.get(type, alg); + } + if (o == null && propertyAliasTable != null) { + o = propertyAliasTable.get(type, alg); + } + + if (o != null) { + lastServiceName = type; + lastAlgorithm = algorithm; + returnedService = (Provider.Service) o; + return returnedService; + } + return null; + } + + /** + * Returns an unmodifiable {@code Set} of all services registered by this + * provider. + * + * @return an unmodifiable {@code Set} of all services registered by this + * provider + */ + public synchronized Set<Provider.Service> getServices() { + updatePropertyServiceTable(); + if (lastServicesSet != null) { + return lastServicesSet; + } + if (serviceTable != null) { + lastServicesSet = new HashSet<Service>(serviceTable.values()); + } else { + lastServicesSet = new HashSet<Service>(); + } + if (propertyServiceTable != null) { + lastServicesSet.addAll(propertyServiceTable.values()); + } + lastServicesSet = Collections.unmodifiableSet(lastServicesSet); + return lastServicesSet; + } + + /** + * Adds a {@code Service} to this {@code Provider}. If a service with the + * same name was registered via this method, it is replace. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where + * NAME is the provider name) to be granted, otherwise a {@code + * SecurityException} will be thrown. + * + * @param s + * the {@code Service} to register + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this method + */ + protected synchronized void putService(Provider.Service s) { + if (s == null) { + throw new NullPointerException(); + } + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkSecurityAccess("putProviderProperty." + name); //$NON-NLS-1$ + } + if ("Provider".equals(s.getType())) { // Provider service type cannot be //$NON-NLS-1$ + // added + return; + } + servicesChanged(); + if (serviceTable == null) { + serviceTable = new TwoKeyHashMap<String, String, Service>(128); + } + serviceTable.put(s.type, Util.toUpperCase(s.algorithm), s); + if (s.aliases != null) { + if (aliasTable == null) { + aliasTable = new TwoKeyHashMap<String, String, Service>(256); + } + for (Iterator<String> it = s.getAliases(); it.hasNext();) { + aliasTable.put(s.type, Util.toUpperCase(it.next()), s); + } + } + serviceInfoToProperties(s); + } + + /** + * Removes a previously registered {@code Service} from this {@code + * Provider}. + * <p> + * If a {@code SecurityManager} is installed, code calling this method needs + * the {@code SecurityPermission} {@code removeProviderProperty.NAME} (where + * NAME is the provider name) to be granted, otherwise a {@code + * SecurityException} will be thrown. + * + * @param s + * the {@code Service} to remove + * @throws SecurityException + * if a {@code SecurityManager} is installed and the caller does + * not have permission to invoke this method + * @throws NullPointerException + * if {@code s} is {@code null} + */ + protected synchronized void removeService(Provider.Service s) { + if (s == null) { + throw new NullPointerException(); + } + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkSecurityAccess("removeProviderProperty." + name); //$NON-NLS-1$ + } + servicesChanged(); + if (serviceTable != null) { + serviceTable.remove(s.type, Util.toUpperCase(s.algorithm)); + } + if (aliasTable != null && s.aliases != null) { + for (Iterator<String> it = s.getAliases(); it.hasNext();) { + aliasTable.remove(s.type, Util.toUpperCase(it.next())); + } + } + serviceInfoFromProperties(s); + } + + // Add Service information to the provider's properties. + private void serviceInfoToProperties(Provider.Service s) { + super.put(s.type + "." + s.algorithm, s.className); //$NON-NLS-1$ + if (s.aliases != null) { + for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { + super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (s.attributes != null) { + for (Iterator<Map.Entry<String, String>> i = s.attributes.entrySet().iterator(); i.hasNext();) { + Map.Entry<String, String> entry = i.next(); + super.put(s.type + "." + s.algorithm + " " + entry.getKey(), //$NON-NLS-1$ //$NON-NLS-2$ + entry.getValue()); + } + } + if (providerNumber != -1) { + // if registered then refresh Services + Services.setNeedRefresh(); + } + } + + // Remove Service information from the provider's properties. + private void serviceInfoFromProperties(Provider.Service s) { + super.remove(s.type + "." + s.algorithm); //$NON-NLS-1$ + if (s.aliases != null) { + for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { + super.remove("Alg.Alias." + s.type + "." + i.next()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (s.attributes != null) { + for (Iterator<Map.Entry<String, String>> i = s.attributes.entrySet().iterator(); i.hasNext();) { + Map.Entry<String, String> entry = i.next(); + super.remove(s.type + "." + s.algorithm + " " + entry.getKey()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (providerNumber != -1) { + // if registered then refresh Services + Services.setNeedRefresh(); + } + } + + // Remove property information from provider Services + private void removeFromPropertyServiceTable(Object key) { + if (key == null || !(key instanceof String)) { + return; + } + String k = (String) key; + if (k.startsWith("Provider.")) { // Provider service type is reserved //$NON-NLS-1$ + return; + } + Provider.Service s; + String serviceName; + String algorithm = null; + String attribute = null; + int i; + if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> //$NON-NLS-1$ + String aliasName; + String service_alias = k.substring(10); + i = service_alias.indexOf('.'); + serviceName = service_alias.substring(0, i); + aliasName = service_alias.substring(i + 1); + if (propertyAliasTable != null) { + propertyAliasTable.remove(serviceName, Util.toUpperCase(aliasName)); + } + if (propertyServiceTable != null) { + for (Iterator<Service> it = propertyServiceTable.values().iterator(); it + .hasNext();) { + s = it.next(); + if (s.aliases.contains(aliasName)) { + s.aliases.remove(aliasName); + return; + } + } + } + return; + } + int j = k.indexOf('.'); + if (j == -1) { // unknown format + return; + } + + i = k.indexOf(' '); + if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> + serviceName = k.substring(0, j); + algorithm = k.substring(j + 1); + if (propertyServiceTable != null) { + Provider.Service ser = propertyServiceTable.remove(serviceName, Util.toUpperCase(algorithm)); + if (ser != null && propertyAliasTable != null + && ser.aliases != null) { + for (Iterator<String> it = ser.aliases.iterator(); it.hasNext();) { + propertyAliasTable.remove(serviceName, Util.toUpperCase(it + .next())); + } + } + } + } else { // <crypto_service>.<algorithm_or_type> + // <attribute_name>=<attrValue> + attribute = k.substring(i + 1); + serviceName = k.substring(0, j); + algorithm = k.substring(j + 1, i); + if (propertyServiceTable != null) { + Object o = propertyServiceTable.get(serviceName, Util.toUpperCase(algorithm)); + if (o != null) { + s = (Provider.Service) o; + s.attributes.remove(attribute); + } + } + } + } + + // Update provider Services if the properties was changed + private void updatePropertyServiceTable() { + Object _key; + Object _value; + Provider.Service s; + String serviceName; + String algorithm; + if (changedProperties == null || changedProperties.isEmpty()) { + return; + } + for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it + .hasNext();) { + Map.Entry<Object, Object> entry = it.next(); + _key = entry.getKey(); + _value = entry.getValue(); + if (_key == null || _value == null || !(_key instanceof String) + || !(_value instanceof String)) { + continue; + } + String key = (String) _key; + String value = (String) _value; + if (key.startsWith("Provider")) { // Provider service type is reserved //$NON-NLS-1$ + continue; + } + int i; + if (key.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> //$NON-NLS-1$ + String aliasName; + String service_alias = key.substring(10); + i = service_alias.indexOf('.'); + serviceName = service_alias.substring(0, i); + aliasName = service_alias.substring(i + 1); + algorithm = value; + String algUp = Util.toUpperCase(algorithm); + Object o = null; + if (propertyServiceTable == null) { + propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); + } else { + o = propertyServiceTable.get(serviceName, algUp); + } + if (o != null) { + s = (Provider.Service) o; + // BEGIN android-changed + s.addAlias(aliasName); + // END android-changed + if (propertyAliasTable == null) { + propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256); + } + propertyAliasTable.put(serviceName, + Util.toUpperCase(aliasName), s); + } else { + String className = (String) changedProperties + .get(serviceName + "." + algorithm); //$NON-NLS-1$ + if (className != null) { + List<String> l = new ArrayList<String>(); + l.add(aliasName); + s = new Provider.Service(this, serviceName, algorithm, + className, l, new HashMap<String, String>()); + propertyServiceTable.put(serviceName, algUp, s); + if (propertyAliasTable == null) { + propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256); + } + propertyAliasTable.put(serviceName, Util.toUpperCase(aliasName + ), s); + } + } + continue; + } + int j = key.indexOf('.'); + if (j == -1) { // unknown format + continue; + } + i = key.indexOf(' '); + if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> + serviceName = key.substring(0, j); + algorithm = key.substring(j + 1); + String alg = Util.toUpperCase(algorithm); + Object o = null; + if (propertyServiceTable != null) { + o = propertyServiceTable.get(serviceName, alg); + } + if (o != null) { + s = (Provider.Service) o; + s.className = value; + } else { + // BEGIN android-changed + s = new Provider.Service(this, serviceName, algorithm, + value, Collections.<String>emptyList(), + Collections.<String,String>emptyMap()); + // END android-changed + if (propertyServiceTable == null) { + propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); + } + propertyServiceTable.put(serviceName, alg, s); + + } + } else { // <crypto_service>.<algorithm_or_type> + // <attribute_name>=<attrValue> + serviceName = key.substring(0, j); + algorithm = key.substring(j + 1, i); + String attribute = key.substring(i + 1); + String alg = Util.toUpperCase(algorithm); + Object o = null; + if (propertyServiceTable != null) { + o = propertyServiceTable.get(serviceName, alg); + } + if (o != null) { + s = (Provider.Service) o; + // BEGIN android-changed + s.putAttribute(attribute, value); + // END android-changed + } else { + String className = (String) changedProperties + .get(serviceName + "." + algorithm); //$NON-NLS-1$ + if (className != null) { + Map<String, String> m = new HashMap<String, String>(); + m.put(attribute, value); + s = new Provider.Service(this, serviceName, algorithm, + className, new ArrayList<String>(), m); + if (propertyServiceTable == null) { + propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); + } + propertyServiceTable.put(serviceName, alg, s); + } + } + } + } + servicesChanged(); + // BEGIN android-changed + changedProperties = null; + // END android-changed + } + + private void servicesChanged() { + lastServicesByType = null; + lastServiceName = null; + lastServicesSet = null; + } + + // These attributes should be placed in each Provider object: + // Provider.id name, Provider.id version, Provider.id info, + // Provider.id className + @SuppressWarnings("nls") + private void putProviderInfo() { + super.put("Provider.id name", null != name ? name : "null"); + super.put("Provider.id version", versionString); + super.put("Provider.id info", null != info ? info : "null"); + super.put("Provider.id className", this.getClass().getName()); + } + + // Searches for the property with the specified key in the provider + // properties. Key is not case-sensitive. + // + // @param prop + // @return the property value with the specified key value. + private String getPropertyIgnoreCase(String key) { + String res = getProperty(key); + if (res != null) { + return res; + } + for (Enumeration<?> e = propertyNames(); e.hasMoreElements();) { + String pname = (String) e.nextElement(); + if (Util.equalsIgnoreCase(key, pname)) { + return getProperty(pname); + } + } + return null; + } + + /** + * {@code Service} represents a service in the Java Security infrastructure. + * Each service describes its type, the algorithm it implements, to which + * provider it belongs and other properties. + */ + public static class Service { + // The provider + private Provider provider; + + // The type of this service + private String type; + + // The algorithm name + private String algorithm; + + // The class implementing this service + private String className; + + // The aliases + private List<String> aliases; + + // The attributes + private Map<String,String> attributes; + + // Service implementation + private Class<?> implementation; + + // For newInstance() optimization + private String lastClassName; + + /** + * Constructs a new instance of {@code Service} with the given + * attributes. + * + * @param provider + * the provider to which this service belongs. + * @param type + * the type of this service (for example {@code + * KeyPairGenerator}). + * @param algorithm + * the algorithm this service implements. + * @param className + * the name of the class implementing this service. + * @param aliases + * {@code List} of aliases for the algorithm name, or {@code + * null} if the implemented algorithm has no aliases. + * @param attributes + * {@code Map} of additional attributes, or {@code null} if + * this {@code Service} has no attributed. + * @throws NullPointerException + * if {@code provider, type, algorithm} or {@code className} + * is {@code null}. + */ + public Service(Provider provider, String type, String algorithm, + String className, List<String> aliases, Map<String, String> attributes) { + if (provider == null || type == null || algorithm == null + || className == null) { + throw new NullPointerException(); + } + this.provider = provider; + this.type = type; + this.algorithm = algorithm; + this.className = className; + // BEGIN android-changed + this.aliases = ((aliases != null) && (aliases.size() == 0)) + ? Collections.<String>emptyList() : aliases; + this.attributes = + ((attributes != null) && (attributes.size() == 0)) + ? Collections.<String,String>emptyMap() : attributes; + // END android-changed + } + + // BEGIN android-added + /** + * Adds an alias. + * + * @param alias the alias to add + */ + /*package*/ void addAlias(String alias) { + if ((aliases == null) || (aliases.size() == 0)) { + aliases = new ArrayList<String>(); + } + aliases.add(alias); + } + + /** + * Puts a new attribute mapping. + * + * @param name the attribute name. + * @param value the attribute value. + */ + /*package*/ void putAttribute(String name, String value) { + if ((attributes == null) || (attributes.size() == 0)) { + attributes = new HashMap<String,String>(); + } + attributes.put(name, value); + } + // END android-added + + /** + * Returns the type of this {@code Service}. For example {@code + * KeyPairGenerator}. + * + * @return the type of this {@code Service}. + */ + public final String getType() { + return type; + } + + /** + * Returns the name of the algorithm implemented by this {@code + * Service}. + * + * @return the name of the algorithm implemented by this {@code + * Service}. + */ + public final String getAlgorithm() { + return algorithm; + } + + /** + * Returns the {@code Provider} this {@code Service} belongs to. + * + * @return the {@code Provider} this {@code Service} belongs to. + */ + public final Provider getProvider() { + return provider; + } + + /** + * Returns the name of the class implementing this {@code Service}. + * + * @return the name of the class implementing this {@code Service}. + */ + public final String getClassName() { + return className; + } + + /** + * Returns the value of the attribute with the specified {@code name}. + * + * @param name + * the name of the attribute. + * @return the value of the attribute, or {@code null} if no attribute + * with the given name is set. + * @throws NullPointerException + * if {@code name} is {@code null}. + */ + public final String getAttribute(String name) { + if (name == null) { + throw new NullPointerException(); + } + if (attributes == null) { + return null; + } + return attributes.get(name); + } + + Iterator<String> getAliases() { + if(aliases == null){ + aliases = new ArrayList<String>(0); + } + return aliases.iterator(); + } + + /** + * Creates and returns a new instance of the implementation described by + * this {@code Service}. + * + * @param constructorParameter + * the parameter that is used by the constructor, or {@code + * null} if the implementation does not declare a constructor + * parameter. + * @return a new instance of the implementation described by this + * {@code Service}. + * @throws NoSuchAlgorithmException + * if the instance could not be constructed. + * @throws InvalidParameterException + * if the implementation does not support the specified + * {@code constructorParameter}. + */ + public Object newInstance(Object constructorParameter) + throws NoSuchAlgorithmException { + if (implementation == null || !className.equals(lastClassName)) { + NoSuchAlgorithmException result = AccessController + .doPrivileged(new PrivilegedAction<NoSuchAlgorithmException>() { + public NoSuchAlgorithmException run() { + ClassLoader cl = provider.getClass() + .getClassLoader(); + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + try { + implementation = Class.forName(className, + true, cl); + } catch (Exception e) { + return new NoSuchAlgorithmException( + Messages.getString("security.11", //$NON-NLS-1$ + new Object[]{type, algorithm, e})); + } + lastClassName = className; + return null; + } + }); + if (result != null) { + throw result; + } + } + if (constructorParameter == null) { + try { + return implementation.newInstance(); + } catch (Exception e) { + throw new NoSuchAlgorithmException(Messages.getString("security.199", //$NON-NLS-1$ + type, algorithm), e); + } + } + if (!supportsParameter(constructorParameter)) { + throw new InvalidParameterException( + Messages.getString("security.12", type)); //$NON-NLS-1$ + } + + Class[] parameterTypes = new Class[1]; + Object[] initargs = { constructorParameter }; + try { + if (Util.equalsIgnoreCase(type,"CertStore")) { //$NON-NLS-1$ + parameterTypes[0] = Class + .forName("java.security.cert.CertStoreParameters"); //$NON-NLS-1$ + } else { + parameterTypes[0] = constructorParameter.getClass(); + } + return implementation.getConstructor(parameterTypes) + .newInstance(initargs); + } catch (Exception e) { + throw new NoSuchAlgorithmException(Messages.getString("security.199", //$NON-NLS-1$ + type, algorithm), e); + } + } + + /** + * Indicates whether this {@code Service} supports the specified + * constructor parameter. + * + * @param parameter + * the parameter to test. + * @return {@code true} if this {@code Service} supports the specified + * constructor parameter, {@code false} otherwise. + */ + public boolean supportsParameter(Object parameter) { + return true; + } + + /** + * Returns a string containing a concise, human-readable description of + * this {@code Service}. + * + * @return a printable representation for this {@code Service}. + */ + @Override + public String toString() { + String result = "Provider " + provider.getName() + " Service " //$NON-NLS-1$ //$NON-NLS-2$ + + type + "." + algorithm + " " + className; //$NON-NLS-1$ //$NON-NLS-2$ + if (aliases != null) { + result = result + "\nAliases " + aliases.toString(); //$NON-NLS-1$ + } + if (attributes != null) { + result = result + "\nAttributes " + attributes.toString(); //$NON-NLS-1$ + } + return result; + } + } + + private void readObject(java.io.ObjectInputStream in) throws NotActiveException, IOException, ClassNotFoundException { + in.defaultReadObject(); + versionString = String.valueOf(version); + providerNumber = -1; + } +} |