Home | History | Annotate | Download | only in security
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.security;
     19 
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.io.NotActiveException;
     23 import java.util.ArrayList;
     24 import java.util.Collection;
     25 import java.util.Collections;
     26 import java.util.Enumeration;
     27 import java.util.HashMap;
     28 import java.util.HashSet;
     29 import java.util.Iterator;
     30 import java.util.List;
     31 import java.util.Map;
     32 import java.util.Properties;
     33 import java.util.Set;
     34 import org.apache.harmony.luni.util.TwoKeyHashMap;
     35 import org.apache.harmony.security.Util;
     36 import org.apache.harmony.security.fortress.Services;
     37 
     38 /**
     39  * {@code Provider} is the abstract superclass for all security providers in the
     40  * Java security infrastructure.
     41  */
     42 public abstract class Provider extends Properties {
     43     private static final long serialVersionUID = -4298000515446427739L;
     44 
     45     private String name;
     46 
     47     private double version;
     48 
     49     // String representation of the provider version number.
     50     private transient String versionString;
     51 
     52     private String info;
     53 
     54     //The provider preference order number.
     55     // Equals -1 for non registered provider.
     56     private transient int providerNumber = -1;
     57 
     58     // Contains "Service.Algorithm" and Provider.Service classes added using
     59     // putService()
     60     private transient TwoKeyHashMap<String, String, Service> serviceTable;
     61 
     62     // Contains "Service.Alias" and Provider.Service classes added using
     63     // putService()
     64     private transient TwoKeyHashMap<String, String, Service> aliasTable;
     65 
     66     // Contains "Service.Algorithm" and Provider.Service classes added using
     67     // put()
     68     private transient TwoKeyHashMap<String, String, Service> propertyServiceTable;
     69 
     70     // Contains "Service.Alias" and Provider.Service classes added using put()
     71     private transient TwoKeyHashMap<String, String, Service> propertyAliasTable;
     72 
     73     // The properties changed via put()
     74     private transient Properties changedProperties;
     75 
     76     // For getService(String type, String algorithm) optimization:
     77     // previous result
     78     private transient Provider.Service returnedService;
     79     // previous parameters
     80     private transient String lastAlgorithm;
     81     // last name
     82     private transient String lastServiceName;
     83 
     84     // For getServices() optimization:
     85     private transient Set<Service> lastServicesSet;
     86 
     87     // For getService(String type) optimization:
     88     private transient String lastType;
     89     // last Service found by type
     90     private transient Provider.Service lastServicesByType;
     91 
     92     /**
     93      * Constructs a new instance of {@code Provider} with its name, version and
     94      * description.
     95      *
     96      * @param name
     97      *            the name of the provider.
     98      * @param version
     99      *            the version of the provider.
    100      * @param info
    101      *            a description of the provider.
    102      */
    103     protected Provider(String name, double version, String info) {
    104         this.name = name;
    105         this.version = version;
    106         this.info = info;
    107         versionString = String.valueOf(version);
    108         putProviderInfo();
    109     }
    110 
    111     /**
    112      * Returns the name of this provider.
    113      *
    114      * @return the name of this provider.
    115      */
    116     public String getName() {
    117         return name;
    118     }
    119 
    120     /**
    121      * Returns the version number for the services being provided.
    122      *
    123      * @return the version number for the services being provided.
    124      */
    125     public double getVersion() {
    126         return version;
    127     }
    128 
    129     /**
    130      * Returns a description of the services being provided.
    131      *
    132      * @return a description of the services being provided.
    133      */
    134     public String getInfo() {
    135         return info;
    136     }
    137 
    138     /**
    139      * Returns a string containing a concise, human-readable description of
    140      * this {@code Provider} including its name and its version.
    141      *
    142      * @return a printable representation for this {@code Provider}.
    143      */
    144     @Override
    145     public String toString() {
    146         return name + " version " + version;
    147     }
    148 
    149     /**
    150      * Clears all properties used to look up services implemented by this
    151      * {@code Provider}.
    152      * <p>
    153      * If a {@code SecurityManager} is installed, code calling this method needs
    154      * the {@code SecurityPermission} {@code clearProviderProperties.NAME}
    155      * (where NAME is the provider name) to be granted, otherwise a {@code
    156      * SecurityException} will be thrown.
    157      *
    158      * @throws SecurityException
    159      *             if a {@code SecurityManager} is installed and the caller does
    160      *             not have permission to invoke this method.
    161      */
    162     @Override
    163     public synchronized void clear() {
    164         SecurityManager sm = System.getSecurityManager();
    165         if (sm != null) {
    166             sm.checkSecurityAccess("clearProviderProperties." + name);
    167         }
    168         super.clear();
    169         if (serviceTable != null) {
    170             serviceTable.clear();
    171         }
    172         if (propertyServiceTable != null) {
    173             propertyServiceTable.clear();
    174         }
    175         if (aliasTable != null) {
    176             aliasTable.clear();
    177         }
    178         if (propertyAliasTable != null) {
    179             propertyAliasTable.clear();
    180         }
    181         // BEGIN android-changed
    182         changedProperties = null;
    183         // END android-changed
    184         putProviderInfo();
    185         if (providerNumber != -1) {
    186             // if registered then refresh Services
    187             Services.setNeedRefresh();
    188         }
    189         servicesChanged();
    190     }
    191 
    192     @Override
    193     public synchronized void load(InputStream inStream) throws IOException {
    194         Properties tmp = new Properties();
    195         tmp.load(inStream);
    196         myPutAll(tmp);
    197     }
    198 
    199     /**
    200      * Copies all from the provided map to this {@code Provider}.
    201      * <p>
    202      * If a {@code SecurityManager} is installed, code calling this method needs
    203      * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where
    204      * NAME is the provider name) to be granted, otherwise a {@code
    205      * SecurityException} will be thrown.
    206      *
    207      * @param t
    208      *            the mappings to copy to this provider.
    209      * @throws SecurityException
    210      *             if a {@code SecurityManager} is installed and the caller does
    211      *             not have permission to invoke this method.
    212      */
    213     @Override
    214     public synchronized void putAll(Map<?,?> t) {
    215 
    216         // Implementation note:
    217         // checkSecurityAccess method call is NOT specified
    218         // Do it as in put(Object key, Object value).
    219 
    220         SecurityManager sm = System.getSecurityManager();
    221         if (sm != null) {
    222             sm.checkSecurityAccess("putProviderProperty." + name);
    223         }
    224         myPutAll(t);
    225     }
    226 
    227     private void myPutAll(Map<?,?> t) {
    228         if (changedProperties == null) {
    229             changedProperties = new Properties();
    230         }
    231         Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator();
    232         Object key;
    233         Object value;
    234         while (it.hasNext()) {
    235             Map.Entry<?, ?> entry = it.next();
    236             key = entry.getKey();
    237             if (key instanceof String && ((String) key).startsWith("Provider.")) {
    238                 // Provider service type is reserved
    239                 continue;
    240             }
    241             value = entry.getValue();
    242             super.put(key, value);
    243             if (changedProperties.remove(key) == null) {
    244                 removeFromPropertyServiceTable(key);
    245             }
    246             changedProperties.put(key, value);
    247         }
    248         if (providerNumber != -1) {
    249             // if registered then refresh Services
    250             Services.setNeedRefresh();
    251         }
    252     }
    253 
    254     @Override
    255     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
    256         return Collections.unmodifiableSet(super.entrySet());
    257     }
    258 
    259     @Override
    260     public Set<Object> keySet() {
    261         return Collections.unmodifiableSet(super.keySet());
    262     }
    263 
    264     @Override
    265     public Collection<Object> values() {
    266         return Collections.unmodifiableCollection(super.values());
    267     }
    268 
    269     /**
    270      * Maps the specified {@code key} property name to the specified {@code
    271      * value}.
    272      * <p>
    273      * If a {@code SecurityManager} is installed, code calling this method needs
    274      * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where
    275      * NAME is the provider name) to be granted, otherwise a {@code
    276      * SecurityException} will be thrown.
    277      *
    278      * @param key
    279      *            the name of the property.
    280      * @param value
    281      *            the value of the property.
    282      * @return the value that was previously mapped to the specified {@code key}
    283      *         ,or {@code null} if it did not have one.
    284      * @throws SecurityException
    285      *             if a {@code SecurityManager} is installed and the caller does
    286      *             not have permission to invoke this method.
    287      */
    288     @Override
    289     public synchronized Object put(Object key, Object value) {
    290         SecurityManager sm = System.getSecurityManager();
    291         if (sm != null) {
    292             sm.checkSecurityAccess("putProviderProperty." + name);
    293         }
    294         if (key instanceof String && ((String) key).startsWith("Provider.")) {
    295             // Provider service type is reserved
    296             return null;
    297         }
    298         if (providerNumber != -1) {
    299             // if registered then refresh Services
    300             Services.setNeedRefresh();
    301         }
    302         if (changedProperties != null && changedProperties.remove(key) == null) {
    303             removeFromPropertyServiceTable(key);
    304         }
    305         if (changedProperties == null) {
    306             changedProperties = new Properties();
    307         }
    308         changedProperties.put(key, value);
    309         return super.put(key, value);
    310     }
    311 
    312     /**
    313      * Removes the specified {@code key} and its associated value from this
    314      * {@code Provider}.
    315      * <p>
    316      * If a {@code SecurityManager} is installed, code calling this method needs
    317      * the {@code SecurityPermission} {@code removeProviderProperty.NAME} (where
    318      * NAME is the provider name) to be granted, otherwise a {@code
    319      * SecurityException} will be thrown.
    320      *
    321      * @param key
    322      *            the name of the property
    323      * @return the value that was mapped to the specified {@code key} ,or
    324      *         {@code null} if no mapping was present
    325      * @throws SecurityException
    326      *             if a {@code SecurityManager} is installed and the caller does
    327      *             not have the permission to invoke this method.
    328      */
    329     @Override
    330     public synchronized Object remove(Object key) {
    331         SecurityManager sm = System.getSecurityManager();
    332         if (sm != null) {
    333             sm.checkSecurityAccess("removeProviderProperty." + name);
    334         }
    335         if (key instanceof String && ((String) key).startsWith("Provider.")) {
    336             // Provider service type is reserved
    337             return null;
    338         }
    339         if (providerNumber != -1) {
    340             // if registered then refresh Services
    341             Services.setNeedRefresh();
    342         }
    343         if (changedProperties != null && changedProperties.remove(key) == null) {
    344             removeFromPropertyServiceTable(key);
    345             // BEGIN android-added
    346             if (changedProperties.size() == 0) {
    347                 changedProperties = null;
    348             }
    349             // END android-added
    350         }
    351         return super.remove(key);
    352     }
    353 
    354     /**
    355      * Returns true if this provider implements the given algorithm. Caller
    356      * must specify the cryptographic service and specify constraints via the
    357      * attribute name and value.
    358      *
    359      * @param serv
    360      *            Crypto service.
    361      * @param alg
    362      *            Algorithm or type.
    363      * @param attribute
    364      *            The attribute name or {@code null}.
    365      * @param val
    366      *            The attribute value.
    367      * @return
    368      */
    369     boolean implementsAlg(String serv, String alg, String attribute, String val) {
    370         String servAlg = serv + "." + alg;
    371         String prop = getPropertyIgnoreCase(servAlg);
    372         if (prop == null) {
    373             alg = getPropertyIgnoreCase("Alg.Alias." + servAlg);
    374             if (alg != null) {
    375                 servAlg = serv + "." + alg;
    376                 prop = getPropertyIgnoreCase(servAlg);
    377             }
    378         }
    379         if (prop != null) {
    380             if (attribute == null) {
    381                 return true;
    382             }
    383             return checkAttribute(servAlg, attribute, val);
    384         }
    385         return false;
    386     }
    387 
    388     // Returns true if this provider has the same value as is given for the
    389     // given attribute
    390     private boolean checkAttribute(String servAlg, String attribute, String val) {
    391 
    392         String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute);
    393         if (attributeValue != null) {
    394             if (Util.equalsIgnoreCase(attribute,"KeySize")) {
    395                 // BEGIN android-changed
    396                 if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) {
    397                     return true;
    398                 }
    399                 // END android-changed
    400             } else { // other attributes
    401                 if (Util.equalsIgnoreCase(attributeValue, val)) {
    402                     return true;
    403                 }
    404             }
    405         }
    406         return false;
    407     }
    408 
    409     /**
    410      *
    411      * Set the provider preference order number.
    412      *
    413      * @param n
    414      */
    415     void setProviderNumber(int n) {
    416         providerNumber = n;
    417     }
    418 
    419     /**
    420      *
    421      * Get the provider preference order number.
    422      *
    423      * @return
    424      */
    425     int getProviderNumber() {
    426         return providerNumber;
    427     }
    428 
    429     /**
    430      * Get the service of the specified type
    431      *
    432      */
    433     synchronized Provider.Service getService(String type) {
    434         updatePropertyServiceTable();
    435         if (lastServicesByType != null && type.equals(lastType)) {
    436             return lastServicesByType;
    437         }
    438         Provider.Service service;
    439         for (Iterator<Service> it = getServices().iterator(); it.hasNext();) {
    440             service = it.next();
    441             if (type.equals(service.type)) {
    442                 lastType = type;
    443                 lastServicesByType = service;
    444                 return service;
    445             }
    446         }
    447         return null;
    448     }
    449 
    450     /**
    451      * Returns the service with the specified {@code type} implementing the
    452      * specified {@code algorithm}, or {@code null} if no such implementation
    453      * exists.
    454      * <p>
    455      * If two services match the requested type and algorithm, the one added
    456      * with the {@link #putService(Service)} is returned (as opposed to the one
    457      * added via {@link #put(Object, Object)}.
    458      *
    459      * @param type
    460      *            the type of the service (for example {@code KeyPairGenerator})
    461      * @param algorithm
    462      *            the algorithm name (case insensitive)
    463      * @return the requested service, or {@code null} if no such implementation
    464      *         exists
    465      */
    466     public synchronized Provider.Service getService(String type,
    467             String algorithm) {
    468         if (type == null || algorithm == null) {
    469             throw new NullPointerException();
    470         }
    471 
    472         if (type.equals(lastServiceName)
    473                 && Util.equalsIgnoreCase(algorithm, lastAlgorithm)) {
    474             return returnedService;
    475         }
    476 
    477         String alg = Util.toUpperCase(algorithm);
    478         Object o = null;
    479         if (serviceTable != null) {
    480             o = serviceTable.get(type, alg);
    481         }
    482         if (o == null && aliasTable != null) {
    483             o = aliasTable.get(type, alg);
    484         }
    485         if (o == null) {
    486             updatePropertyServiceTable();
    487         }
    488         if (o == null && propertyServiceTable != null) {
    489             o = propertyServiceTable.get(type, alg);
    490         }
    491         if (o == null && propertyAliasTable != null) {
    492             o = propertyAliasTable.get(type, alg);
    493         }
    494 
    495         if (o != null) {
    496             lastServiceName = type;
    497             lastAlgorithm = algorithm;
    498             returnedService = (Provider.Service) o;
    499             return returnedService;
    500         }
    501         return null;
    502     }
    503 
    504     /**
    505      * Returns an unmodifiable {@code Set} of all services registered by this
    506      * provider.
    507      *
    508      * @return an unmodifiable {@code Set} of all services registered by this
    509      *         provider
    510      */
    511     public synchronized Set<Provider.Service> getServices() {
    512         updatePropertyServiceTable();
    513         if (lastServicesSet != null) {
    514             return lastServicesSet;
    515         }
    516         if (serviceTable != null) {
    517             lastServicesSet = new HashSet<Service>(serviceTable.values());
    518         } else {
    519             lastServicesSet = new HashSet<Service>();
    520         }
    521         if (propertyServiceTable != null) {
    522             lastServicesSet.addAll(propertyServiceTable.values());
    523         }
    524         lastServicesSet = Collections.unmodifiableSet(lastServicesSet);
    525         return lastServicesSet;
    526     }
    527 
    528     /**
    529      * Adds a {@code Service} to this {@code Provider}. If a service with the
    530      * same name was registered via this method, it is replace.
    531      * <p>
    532      * If a {@code SecurityManager} is installed, code calling this method needs
    533      * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where
    534      * NAME is the provider name) to be granted, otherwise a {@code
    535      * SecurityException} will be thrown.
    536      *
    537      * @param s
    538      *            the {@code Service} to register
    539      * @throws SecurityException
    540      *             if a {@code SecurityManager} is installed and the caller does
    541      *             not have permission to invoke this method
    542      */
    543     protected synchronized void putService(Provider.Service s) {
    544         if (s == null) {
    545             throw new NullPointerException();
    546         }
    547         SecurityManager sm = System.getSecurityManager();
    548         if (sm != null) {
    549             sm.checkSecurityAccess("putProviderProperty." + name);
    550         }
    551         if ("Provider".equals(s.getType())) { // Provider service type cannot be added
    552             return;
    553         }
    554         servicesChanged();
    555         if (serviceTable == null) {
    556             serviceTable = new TwoKeyHashMap<String, String, Service>(128);
    557         }
    558         serviceTable.put(s.type, Util.toUpperCase(s.algorithm), s);
    559         if (s.aliases != null) {
    560             if (aliasTable == null) {
    561                 aliasTable = new TwoKeyHashMap<String, String, Service>(256);
    562             }
    563             for (String alias : s.getAliases()) {
    564                 aliasTable.put(s.type, Util.toUpperCase(alias), s);
    565             }
    566         }
    567         serviceInfoToProperties(s);
    568     }
    569 
    570     /**
    571      * Removes a previously registered {@code Service} from this {@code
    572      * Provider}.
    573      * <p>
    574      * If a {@code SecurityManager} is installed, code calling this method needs
    575      * the {@code SecurityPermission} {@code removeProviderProperty.NAME} (where
    576      * NAME is the provider name) to be granted, otherwise a {@code
    577      * SecurityException} will be thrown.
    578      *
    579      * @param s
    580      *            the {@code Service} to remove
    581      * @throws SecurityException
    582      *             if a {@code SecurityManager} is installed and the caller does
    583      *             not have permission to invoke this method
    584      * @throws NullPointerException
    585      *             if {@code s} is {@code null}
    586      */
    587     protected synchronized void removeService(Provider.Service s) {
    588         if (s == null) {
    589             throw new NullPointerException();
    590         }
    591         SecurityManager sm = System.getSecurityManager();
    592         if (sm != null) {
    593             sm.checkSecurityAccess("removeProviderProperty." + name);
    594         }
    595         servicesChanged();
    596         if (serviceTable != null) {
    597             serviceTable.remove(s.type, Util.toUpperCase(s.algorithm));
    598         }
    599         if (aliasTable != null && s.aliases != null) {
    600             for (String alias: s.getAliases()) {
    601                 aliasTable.remove(s.type, Util.toUpperCase(alias));
    602             }
    603         }
    604         serviceInfoFromProperties(s);
    605     }
    606 
    607     /**
    608      * Add Service information to the provider's properties.
    609      */
    610     private void serviceInfoToProperties(Provider.Service s) {
    611         super.put(s.type + "." + s.algorithm, s.className);
    612         if (s.aliases != null) {
    613             for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) {
    614                 super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm);
    615             }
    616         }
    617         if (s.attributes != null) {
    618             for (Map.Entry<String, String> entry : s.attributes.entrySet()) {
    619                 super.put(s.type + "." + s.algorithm + " " + entry.getKey(),
    620                         entry.getValue());
    621             }
    622         }
    623         if (providerNumber != -1) {
    624             // if registered then refresh Services
    625             Services.setNeedRefresh();
    626         }
    627     }
    628 
    629     /**
    630      * Remove Service information from the provider's properties.
    631      */
    632     private void serviceInfoFromProperties(Provider.Service s) {
    633         super.remove(s.type + "." + s.algorithm);
    634         if (s.aliases != null) {
    635             for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) {
    636                 super.remove("Alg.Alias." + s.type + "." + i.next());
    637             }
    638         }
    639         if (s.attributes != null) {
    640             for (Map.Entry<String, String> entry : s.attributes.entrySet()) {
    641                 super.remove(s.type + "." + s.algorithm + " " + entry.getKey());
    642             }
    643         }
    644         if (providerNumber != -1) {
    645             // if registered then refresh Services
    646             Services.setNeedRefresh();
    647         }
    648     }
    649 
    650     // Remove property information from provider Services
    651     private void removeFromPropertyServiceTable(Object key) {
    652         if (key == null || !(key instanceof String)) {
    653             return;
    654         }
    655         String k = (String) key;
    656         if (k.startsWith("Provider.")) { // Provider service type is reserved
    657             return;
    658         }
    659         Provider.Service s;
    660         String serviceName;
    661         String algorithm = null;
    662         String attribute = null;
    663         int i;
    664         if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName>
    665             String aliasName;
    666             String service_alias = k.substring(10);
    667             i = service_alias.indexOf('.');
    668             serviceName = service_alias.substring(0, i);
    669             aliasName = service_alias.substring(i + 1);
    670             if (propertyAliasTable != null) {
    671                 propertyAliasTable.remove(serviceName, Util.toUpperCase(aliasName));
    672             }
    673             if (propertyServiceTable != null) {
    674                 for (Iterator<Service> it = propertyServiceTable.values().iterator(); it
    675                         .hasNext();) {
    676                     s = it.next();
    677                     if (s.aliases.contains(aliasName)) {
    678                         s.aliases.remove(aliasName);
    679                         return;
    680                     }
    681                 }
    682             }
    683             return;
    684         }
    685         int j = k.indexOf('.');
    686         if (j == -1) { // unknown format
    687             return;
    688         }
    689 
    690         i = k.indexOf(' ');
    691         if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
    692             serviceName = k.substring(0, j);
    693             algorithm = k.substring(j + 1);
    694             if (propertyServiceTable != null) {
    695                 Provider.Service ser = propertyServiceTable.remove(serviceName,
    696                                                                    Util.toUpperCase(algorithm));
    697                 if (ser != null && propertyAliasTable != null
    698                         && ser.aliases != null) {
    699                     for (Iterator<String> it = ser.aliases.iterator(); it.hasNext();) {
    700                         propertyAliasTable.remove(serviceName, Util.toUpperCase(it
    701                                 .next()));
    702                     }
    703                 }
    704             }
    705         } else {
    706             // <crypto_service>.<algorithm_or_type>
    707             // <attribute_name>=<attrValue>
    708             attribute = k.substring(i + 1);
    709             serviceName = k.substring(0, j);
    710             algorithm = k.substring(j + 1, i);
    711             if (propertyServiceTable != null) {
    712                 Object o = propertyServiceTable.get(serviceName, Util.toUpperCase(algorithm));
    713                 if (o != null) {
    714                     s = (Provider.Service) o;
    715                     s.attributes.remove(attribute);
    716                 }
    717             }
    718         }
    719     }
    720 
    721     // Update provider Services if the properties was changed
    722     private void updatePropertyServiceTable() {
    723         Object _key;
    724         Object _value;
    725         Provider.Service s;
    726         String serviceName;
    727         String algorithm;
    728         if (changedProperties == null || changedProperties.isEmpty()) {
    729             return;
    730         }
    731         for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it
    732                 .hasNext();) {
    733             Map.Entry<Object, Object> entry = it.next();
    734             _key = entry.getKey();
    735             _value = entry.getValue();
    736             if (_key == null || _value == null || !(_key instanceof String)
    737                     || !(_value instanceof String)) {
    738                 continue;
    739             }
    740             String key = (String) _key;
    741             String value = (String) _value;
    742             if (key.startsWith("Provider")) {
    743                 // Provider service type is reserved
    744                 continue;
    745             }
    746             int i;
    747             if (key.startsWith("Alg.Alias.")) {
    748                 // Alg.Alias.<crypto_service>.<aliasName>=<standardName>
    749                 String aliasName;
    750                 String service_alias = key.substring(10);
    751                 i = service_alias.indexOf('.');
    752                 serviceName = service_alias.substring(0, i);
    753                 aliasName = service_alias.substring(i + 1);
    754                 algorithm = value;
    755                 String algUp = Util.toUpperCase(algorithm);
    756                 Object o = null;
    757                 if (propertyServiceTable == null) {
    758                     propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
    759                 } else {
    760                     o = propertyServiceTable.get(serviceName, algUp);
    761                 }
    762                 if (o != null) {
    763                     s = (Provider.Service) o;
    764                     // BEGIN android-changed
    765                     s.addAlias(aliasName);
    766                     // END android-changed
    767                     if (propertyAliasTable == null) {
    768                         propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
    769                     }
    770                     propertyAliasTable.put(serviceName,
    771                             Util.toUpperCase(aliasName), s);
    772                 } else {
    773                     String className = (String) changedProperties
    774                             .get(serviceName + "." + algorithm);
    775                     if (className != null) {
    776                         List<String> l = new ArrayList<String>();
    777                         l.add(aliasName);
    778                         s = new Provider.Service(this, serviceName, algorithm,
    779                                 className, l, new HashMap<String, String>());
    780                         propertyServiceTable.put(serviceName, algUp, s);
    781                         if (propertyAliasTable == null) {
    782                             propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
    783                         }
    784                         propertyAliasTable.put(serviceName, Util.toUpperCase(aliasName
    785                                 ), s);
    786                     }
    787                 }
    788                 continue;
    789             }
    790             int j = key.indexOf('.');
    791             if (j == -1) { // unknown format
    792                 continue;
    793             }
    794             i = key.indexOf(' ');
    795             if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
    796                 serviceName = key.substring(0, j);
    797                 algorithm = key.substring(j + 1);
    798                 String alg = Util.toUpperCase(algorithm);
    799                 Object o = null;
    800                 if (propertyServiceTable != null) {
    801                     o = propertyServiceTable.get(serviceName, alg);
    802                 }
    803                 if (o != null) {
    804                     s = (Provider.Service) o;
    805                     s.className = value;
    806                 } else {
    807                     // BEGIN android-changed
    808                     s = new Provider.Service(this, serviceName, algorithm,
    809                             value, Collections.<String>emptyList(),
    810                             Collections.<String,String>emptyMap());
    811                     // END android-changed
    812                     if (propertyServiceTable == null) {
    813                         propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
    814                     }
    815                     propertyServiceTable.put(serviceName, alg, s);
    816 
    817                 }
    818             } else {
    819                 // <crypto_service>.<algorithm_or_type>
    820                 // <attribute_name>=<attrValue>
    821                 serviceName = key.substring(0, j);
    822                 algorithm = key.substring(j + 1, i);
    823                 String attribute = key.substring(i + 1);
    824                 String alg = Util.toUpperCase(algorithm);
    825                 Object o = null;
    826                 if (propertyServiceTable != null) {
    827                     o = propertyServiceTable.get(serviceName, alg);
    828                 }
    829                 if (o != null) {
    830                     s = (Provider.Service) o;
    831                     // BEGIN android-changed
    832                     s.putAttribute(attribute, value);
    833                     // END android-changed
    834                 } else {
    835                     String className = (String) changedProperties
    836                             .get(serviceName + "." + algorithm);
    837                     if (className != null) {
    838                         Map<String, String> m = new HashMap<String, String>();
    839                         m.put(attribute, value);
    840                         s = new Provider.Service(this, serviceName, algorithm,
    841                                 className, new ArrayList<String>(), m);
    842                         if (propertyServiceTable == null) {
    843                             propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
    844                         }
    845                         propertyServiceTable.put(serviceName, alg, s);
    846                     }
    847                 }
    848             }
    849         }
    850         servicesChanged();
    851         // BEGIN android-changed
    852         changedProperties = null;
    853         // END android-changed
    854     }
    855 
    856     private void servicesChanged() {
    857         lastServicesByType = null;
    858         lastServiceName = null;
    859         lastServicesSet = null;
    860     }
    861 
    862     /**
    863      * These attributes should be placed in each Provider object:
    864      * Provider.id name, Provider.id version, Provider.id info,
    865      * Provider.id className
    866      */
    867     @SuppressWarnings("nls")
    868     private void putProviderInfo() {
    869         super.put("Provider.id name", null != name ? name : "null");
    870         super.put("Provider.id version", versionString);
    871         super.put("Provider.id info", null != info ? info : "null");
    872         super.put("Provider.id className", this.getClass().getName());
    873     }
    874 
    875     /**
    876      * Searches for the property with the specified key in the
    877      * provider properties. Key is not case-sensitive.
    878      *
    879      * @param prop
    880      * @return the property value with the specified key value.
    881      */
    882     private String getPropertyIgnoreCase(String key) {
    883         String res = getProperty(key);
    884         if (res != null) {
    885             return res;
    886         }
    887         for (Enumeration<?> e = propertyNames(); e.hasMoreElements();) {
    888             String pname = (String) e.nextElement();
    889             if (Util.equalsIgnoreCase(key, pname)) {
    890                 return getProperty(pname);
    891             }
    892         }
    893         return null;
    894     }
    895 
    896     /**
    897      * {@code Service} represents a service in the Java Security infrastructure.
    898      * Each service describes its type, the algorithm it implements, to which
    899      * provider it belongs and other properties.
    900      */
    901     public static class Service {
    902         // The provider
    903         private Provider provider;
    904 
    905         // The type of this service
    906         private String type;
    907 
    908         // The algorithm name
    909         private String algorithm;
    910 
    911         // The class implementing this service
    912         private String className;
    913 
    914         // The aliases
    915         private List<String> aliases;
    916 
    917         // The attributes
    918         private Map<String,String> attributes;
    919 
    920         // Service implementation
    921         private Class<?> implementation;
    922 
    923         // For newInstance() optimization
    924         private String lastClassName;
    925 
    926         /**
    927          * Constructs a new instance of {@code Service} with the given
    928          * attributes.
    929          *
    930          * @param provider
    931          *            the provider to which this service belongs.
    932          * @param type
    933          *            the type of this service (for example {@code
    934          *            KeyPairGenerator}).
    935          * @param algorithm
    936          *            the algorithm this service implements.
    937          * @param className
    938          *            the name of the class implementing this service.
    939          * @param aliases
    940          *            {@code List} of aliases for the algorithm name, or {@code
    941          *            null} if the implemented algorithm has no aliases.
    942          * @param attributes
    943          *            {@code Map} of additional attributes, or {@code null} if
    944          *            this {@code Service} has no attributed.
    945          * @throws NullPointerException
    946          *             if {@code provider, type, algorithm} or {@code className}
    947          *             is {@code null}.
    948          */
    949         public Service(Provider provider, String type, String algorithm,
    950                 String className, List<String> aliases, Map<String, String> attributes) {
    951             if (provider == null || type == null || algorithm == null
    952                     || className == null) {
    953                 throw new NullPointerException();
    954             }
    955             this.provider = provider;
    956             this.type = type;
    957             this.algorithm = algorithm;
    958             this.className = className;
    959             // BEGIN android-changed
    960             this.aliases = ((aliases != null) && (aliases.size() == 0))
    961                     ? Collections.<String>emptyList() : aliases;
    962             this.attributes =
    963                     ((attributes != null) && (attributes.size() == 0))
    964                     ? Collections.<String,String>emptyMap() : attributes;
    965             // END android-changed
    966         }
    967 
    968         // BEGIN android-added
    969         /**
    970          * Adds an alias.
    971          *
    972          * @param alias the alias to add
    973          */
    974         /*package*/ void addAlias(String alias) {
    975             if ((aliases == null) || (aliases.size() == 0)) {
    976                 aliases = new ArrayList<String>();
    977             }
    978             aliases.add(alias);
    979         }
    980 
    981         /**
    982          * Puts a new attribute mapping.
    983          *
    984          * @param name the attribute name.
    985          * @param value the attribute value.
    986          */
    987         /*package*/ void putAttribute(String name, String value) {
    988             if ((attributes == null) || (attributes.size() == 0)) {
    989                 attributes = new HashMap<String,String>();
    990             }
    991             attributes.put(name, value);
    992         }
    993         // END android-added
    994 
    995         /**
    996          * Returns the type of this {@code Service}. For example {@code
    997          * KeyPairGenerator}.
    998          *
    999          * @return the type of this {@code Service}.
   1000          */
   1001         public final String getType() {
   1002             return type;
   1003         }
   1004 
   1005         /**
   1006          * Returns the name of the algorithm implemented by this {@code
   1007          * Service}.
   1008          *
   1009          * @return the name of the algorithm implemented by this {@code
   1010          *         Service}.
   1011          */
   1012         public final String getAlgorithm() {
   1013             return algorithm;
   1014         }
   1015 
   1016         /**
   1017          * Returns the {@code Provider} this {@code Service} belongs to.
   1018          *
   1019          * @return the {@code Provider} this {@code Service} belongs to.
   1020          */
   1021         public final Provider getProvider() {
   1022             return provider;
   1023         }
   1024 
   1025         /**
   1026          * Returns the name of the class implementing this {@code Service}.
   1027          *
   1028          * @return the name of the class implementing this {@code Service}.
   1029          */
   1030         public final String getClassName() {
   1031             return className;
   1032         }
   1033 
   1034         /**
   1035          * Returns the value of the attribute with the specified {@code name}.
   1036          *
   1037          * @param name
   1038          *            the name of the attribute.
   1039          * @return the value of the attribute, or {@code null} if no attribute
   1040          *         with the given name is set.
   1041          * @throws NullPointerException
   1042          *             if {@code name} is {@code null}.
   1043          */
   1044         public final String getAttribute(String name) {
   1045             if (name == null) {
   1046                 throw new NullPointerException();
   1047             }
   1048             if (attributes == null) {
   1049                 return null;
   1050             }
   1051             return attributes.get(name);
   1052         }
   1053 
   1054         List<String> getAliases() {
   1055             if (aliases == null){
   1056                 aliases = new ArrayList<String>(0);
   1057             }
   1058             return aliases;
   1059         }
   1060 
   1061         /**
   1062          * Creates and returns a new instance of the implementation described by
   1063          * this {@code Service}.
   1064          *
   1065          * @param constructorParameter
   1066          *            the parameter that is used by the constructor, or {@code
   1067          *            null} if the implementation does not declare a constructor
   1068          *            parameter.
   1069          * @return a new instance of the implementation described by this
   1070          *         {@code Service}.
   1071          * @throws NoSuchAlgorithmException
   1072          *             if the instance could not be constructed.
   1073          * @throws InvalidParameterException
   1074          *             if the implementation does not support the specified
   1075          *             {@code constructorParameter}.
   1076          */
   1077         public Object newInstance(Object constructorParameter)
   1078                 throws NoSuchAlgorithmException {
   1079             if (implementation == null || !className.equals(lastClassName)) {
   1080                 NoSuchAlgorithmException result = AccessController
   1081                         .doPrivileged(new PrivilegedAction<NoSuchAlgorithmException>() {
   1082                             public NoSuchAlgorithmException run() {
   1083                                 ClassLoader cl = provider.getClass()
   1084                                         .getClassLoader();
   1085                                 if (cl == null) {
   1086                                     cl = ClassLoader.getSystemClassLoader();
   1087                                 }
   1088                                 try {
   1089                                     implementation = Class.forName(className,
   1090                                             true, cl);
   1091                                 } catch (Exception e) {
   1092                                     return new NoSuchAlgorithmException(
   1093                                             type + " " + algorithm
   1094                                             + " implementation not found: " + e);
   1095                                 }
   1096                                 lastClassName = className;
   1097                                 return null;
   1098                             }
   1099                         });
   1100                 if (result != null) {
   1101                     throw result;
   1102                 }
   1103             }
   1104             if (constructorParameter == null) {
   1105                 try {
   1106                     return implementation.newInstance();
   1107                 } catch (Exception e) {
   1108                     throw new NoSuchAlgorithmException(
   1109                             type + " " + algorithm + " implementation not found", e);
   1110                 }
   1111             }
   1112             if (!supportsParameter(constructorParameter)) {
   1113                 throw new InvalidParameterException(type + ": service cannot use the parameter");
   1114             }
   1115 
   1116             Class[] parameterTypes = new Class[1];
   1117             Object[] initargs = { constructorParameter };
   1118             try {
   1119                 if (Util.equalsIgnoreCase(type,"CertStore")) {
   1120                     parameterTypes[0] = Class
   1121                             .forName("java.security.cert.CertStoreParameters");
   1122                 } else {
   1123                     parameterTypes[0] = constructorParameter.getClass();
   1124                 }
   1125                 return implementation.getConstructor(parameterTypes)
   1126                         .newInstance(initargs);
   1127             } catch (Exception e) {
   1128                 throw new NoSuchAlgorithmException(type + " " + algorithm
   1129                         + " implementation not found", e);
   1130             }
   1131         }
   1132 
   1133         /**
   1134          * Indicates whether this {@code Service} supports the specified
   1135          * constructor parameter.
   1136          *
   1137          * @param parameter
   1138          *            the parameter to test.
   1139          * @return {@code true} if this {@code Service} supports the specified
   1140          *         constructor parameter, {@code false} otherwise.
   1141          */
   1142         public boolean supportsParameter(Object parameter) {
   1143             return true;
   1144         }
   1145 
   1146         /**
   1147          * Returns a string containing a concise, human-readable description of
   1148          * this {@code Service}.
   1149          *
   1150          * @return a printable representation for this {@code Service}.
   1151          */
   1152         @Override
   1153         public String toString() {
   1154             String result = "Provider " + provider.getName() + " Service "
   1155                     + type + "." + algorithm + " " + className;
   1156             if (aliases != null) {
   1157                 result = result + "\nAliases " + aliases.toString();
   1158             }
   1159             if (attributes != null) {
   1160                 result = result + "\nAttributes " + attributes.toString();
   1161             }
   1162             return result;
   1163         }
   1164     }
   1165 
   1166     private void readObject(java.io.ObjectInputStream in)
   1167             throws NotActiveException, IOException, ClassNotFoundException {
   1168         in.defaultReadObject();
   1169         versionString = String.valueOf(version);
   1170         providerNumber = -1;
   1171     }
   1172 }
   1173