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/**
19* @author Boris V. Kuznetsov
20* @version $Revision$
21*/
22
23package org.apache.harmony.security.fortress;
24
25import java.security.AccessController;
26import java.security.PrivilegedAction;
27import java.security.Provider;
28import java.security.Security;
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import org.apache.harmony.security.Util;
34
35
36/**
37 * This class contains information about all registered providers and preferred
38 * implementations for all "serviceName.algName".
39 *
40 */
41
42public class Services {
43
44    // The HashMap that contains information about preferred implementations for
45    // all serviceName.algName in the registered providers
46    // BEGIN android-changed
47    // set the initial size to 600 so we don't grow to 1024 by default because
48    // initialization adds a few entries more than the growth threshold.
49    private static final Map<String, Provider.Service> services
50            = new HashMap<String, Provider.Service>(600);
51    // save default SecureRandom service as well.
52    // avoids similar provider/services iteration in SecureRandom constructor
53    private static Provider.Service secureRandom;
54    // END android-changed
55
56    // Need refresh flag
57    private static boolean needRefresh; // = false;
58
59    /**
60     * Refresh number
61     */
62    static int refreshNumber = 1;
63
64    // Registered providers
65    private static final List<Provider> providers = new ArrayList<Provider>(20);
66
67    // Hash for quick provider access by name
68    private static final Map<String, Provider> providersNames = new HashMap<String, Provider>(20);
69
70    static {
71        AccessController.doPrivileged(new PrivilegedAction<Object>() {
72            public Object run() {
73                loadProviders();
74                return null;
75            }
76        });
77    }
78
79    // Load statically registered providers and init Services Info
80    private static void loadProviders() {
81        String providerClassName = null;
82        int i = 1;
83        ClassLoader cl = ClassLoader.getSystemClassLoader();
84        Provider p;
85
86        while ((providerClassName = Security.getProperty("security.provider."
87                + i++)) != null) {
88            try {
89                p = (Provider) Class
90                        .forName(providerClassName.trim(), true, cl)
91                        .newInstance();
92                providers.add(p);
93                providersNames.put(p.getName(), p);
94                initServiceInfo(p);
95            } catch (ClassNotFoundException e) { // ignore Exceptions
96            } catch (IllegalAccessException e) {
97            } catch (InstantiationException e) {
98            }
99        }
100        Engine.door.renumProviders();
101    }
102
103    /**
104     * Returns registered providers
105     *
106     * @return
107     */
108    public static Provider[] getProviders() {
109        return providers.toArray(new Provider[providers.size()]);
110    }
111
112    /**
113     * Returns registered providers as List
114     *
115     * @return
116     */
117    public static List<Provider> getProvidersList() {
118        return new ArrayList<Provider>(providers);
119    }
120
121    /**
122     * Returns the provider with the specified name
123     *
124     * @param name
125     * @return
126     */
127    public static Provider getProvider(String name) {
128        if (name == null) {
129            return null;
130        }
131        return providersNames.get(name);
132    }
133
134    /**
135     * Inserts a provider at a specified position
136     *
137     * @param provider
138     * @param position
139     * @return
140     */
141    public static int insertProviderAt(Provider provider, int position) {
142        int size = providers.size();
143        if ((position < 1) || (position > size)) {
144            position = size + 1;
145        }
146        providers.add(position - 1, provider);
147        providersNames.put(provider.getName(), provider);
148        setNeedRefresh();
149        return position;
150    }
151
152    /**
153     * Removes the provider
154     *
155     * @param providerNumber
156     */
157    public static void removeProvider(int providerNumber) {
158        Provider p = providers.remove(providerNumber - 1);
159        providersNames.remove(p.getName());
160        setNeedRefresh();
161    }
162
163    /**
164     *
165     * Adds information about provider services into HashMap.
166     *
167     * @param p
168     */
169    public static void initServiceInfo(Provider p) {
170        StringBuilder sb = new StringBuilder(128);
171
172        for (Provider.Service serv : p.getServices()) {
173            String type = serv.getType();
174            if (secureRandom == null && type.equals("SecureRandom")) {
175                secureRandom = serv;
176            }
177            sb.delete(0, sb.length());
178            String key = sb.append(type).append(".").append(
179                    Util.toUpperCase(serv.getAlgorithm())).toString();
180            if (!services.containsKey(key)) {
181                services.put(key, serv);
182            }
183            for (String alias : Engine.door.getAliases(serv)) {
184                sb.delete(0, sb.length());
185                key = sb.append(type).append(".").append(Util.toUpperCase(alias))
186                        .toString();
187                if (!services.containsKey(key)) {
188                    services.put(key, serv);
189                }
190            }
191        }
192    }
193
194    /**
195     *
196     * Updates services hashtable for all registered providers
197     *
198     */
199    public static void updateServiceInfo() {
200        services.clear();
201        secureRandom = null;
202        for (Provider p : providers) {
203            initServiceInfo(p);
204        }
205        needRefresh = false;
206    }
207
208    /**
209     * Returns true if services contain any provider information
210     * @return
211     */
212    public static boolean isEmpty() {
213        return services.isEmpty();
214    }
215
216    /**
217     * Returns service description.
218     * Call refresh() before.
219     *
220     * @param key in the format TYPE.ALGORITHM
221     * @return
222     */
223    public static Provider.Service getService(String key) {
224        return services.get(key);
225    }
226
227    /**
228     * Returns the default SecureRandom service description.
229     * Call refresh() before.
230     */
231    public static Provider.Service getSecureRandomService() {
232        return secureRandom;
233    }
234
235    /**
236     * Set flag needRefresh
237     *
238     */
239    public static void setNeedRefresh() {
240        needRefresh = true;
241    }
242
243    /**
244     * Refresh services info
245     *
246     */
247    public static void refresh() {
248        if (needRefresh) {
249            refreshNumber++;
250            updateServiceInfo();
251        }
252    }
253}
254