content: /*
   * Copyright (C) 2014 The Android Open Source Project
   * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package java.security;
  
  import java.lang.reflect.*;
  import java.util.*;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.io.*;
  import sun.security.jca.GetInstance;
  import sun.security.jca.ProviderList;
  import sun.security.jca.Providers;
  
  /**
   * <p>This class centralizes all security properties and common security
   * methods. One of its primary uses is to manage providers.
   *
   * <p>The default values of security properties are read from an
   * implementation-specific location, which is typically the properties file
   * {@code lib/security/java.security} in the Java installation directory.
   *
   * @author Benjamin Renaud
   */
  
  public final class Security {
  
      // Android-added: Track the version to allow callers know when something has changed.
      private static final AtomicInteger version = new AtomicInteger();
  
      // Android-removed: Debug is stubbed and disabled on Android.
      // /* Are we debugging? -- for developers */
      // private static final Debug sdebug =
      //                     Debug.getInstance("properties");
  
      /* The java.security properties */
      // Android-changed: Added final.
      private static final Properties props;
  
      // An element in the cache
      private static class ProviderProperty {
          String className;
          Provider provider;
      }
  
      static {
  // BEGIN Android-changed: doPrivileged is stubbed on Android.
  // Also, because props is final it must be assigned in the static block, not a method.
          /*
          // doPrivileged here because there are multiple
          // things in initialize that might require privs.
          // (the FileInputStream call and the File.exists call,
          // the securityPropFile call, etc)
          AccessController.doPrivileged(new PrivilegedAction<Void>() {
              public Void run() {
                  initialize();
                  return null;
              }
          });
      }
  
      private static void initialize() {
          */
  // END Android-changed: doPrivileged is stubbed on Android.
          props = new Properties();
          boolean loadedProps = false;
          // BEGIN Android-changed: Use a resource file, Android logging, and only one file.
          InputStream is = null;
          try {
              /*
               * Android keeps the property file in a resource file.
               */
              InputStream propStream = Security.class.getResourceAsStream("security.properties");
              if (propStream == null) {
                  System.logE("Could not find 'security.properties'.");
              } else {
                  is  = new BufferedInputStream(propStream);
                  props.load(is);
                  loadedProps = true;
              }
          } catch (IOException ex) {
              System.logE("Could not load 'security.properties'", ex);
          } finally {
              if (is != null) {
                  try {
                      is.close();
                  } catch (IOException ignored) {}
              }
          }
          // END Android-changed: Use a resource file, Android logging, and only one file.
  
          if (!loadedProps) {
              initializeStatic();
          }
      }
  
      /*
       * Initialize to default values, if <java.home>/lib/java.security
       * is not found.
       */
      private static void initializeStatic() {
          // Android-changed: Use Conscrypt and BC, not the sun.security providers.
          /*
          props.put("security.provider.1", "sun.security.provider.Sun");
          props.put("security.provider.2", "sun.security.rsa.SunRsaSign");
          props.put("security.provider.3", "com.sun.net.ssl.internal.ssl.Provider");
          props.put("security.provider.4", "com.sun.crypto.provider.SunJCE");
          props.put("security.provider.5", "sun.security.jgss.SunProvider");
          props.put("security.provider.6", "com.sun.security.sasl.Provider");
          */
          props.put("security.provider.1", "com.android.org.conscrypt.OpenSSLProvider");
          props.put("security.provider.2", "sun.security.provider.CertPathProvider");
          props.put("security.provider.3", "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");
          props.put("security.provider.4", "com.android.org.conscrypt.JSSEProvider");
      }
  
      /**
       * Don't let anyone instantiate this.
       */
      private Security() {
      }
  
      /**
       * Looks up providers, and returns the property (and its associated
       * provider) mapping the key, if any.
       * The order in which the providers are looked up is the
       * provider-preference order, as specificed in the security
       * properties file.
       */
      private static ProviderProperty getProviderProperty(String key) {
          ProviderProperty entry = null;
  
          List<Provider> providers = Providers.getProviderList().providers();
          for (int i = 0; i < providers.size(); i++) {
  
              String matchKey = null;
              Provider prov = providers.get(i);
              String prop = prov.getProperty(key);
  
              if (prop == null) {
                  // Is there a match if we do a case-insensitive property name
                  // comparison? Let's try ...
                  for (Enumeration<Object> e = prov.keys();
                                  e.hasMoreElements() && prop == null; ) {
                      matchKey = (String)e.nextElement();
                      if (key.equalsIgnoreCase(matchKey)) {
                          prop = prov.getProperty(matchKey);
                          break;
                      }
                  }
              }
  
              if (prop != null) {
                  ProviderProperty newEntry = new ProviderProperty();
                  newEntry.className = prop;
                  newEntry.provider = prov;
                  return newEntry;
              }
          }
  
          return entry;
      }
  
      /**
       * Returns the property (if any) mapping the key for the given provider.
       */
      private static String getProviderProperty(String key, Provider provider) {
          String prop = provider.getProperty(key);
          if (prop == null) {
              // Is there a match if we do a case-insensitive property name
              // comparison? Let's try ...
              for (Enumeration<Object> e = provider.keys();
                                  e.hasMoreElements() && prop == null; ) {
                  String matchKey = (String)e.nextElement();
                  if (key.equalsIgnoreCase(matchKey)) {
                      prop = provider.getProperty(matchKey);
                      break;
                  }
              }
          }
          return prop;
      }
  
      /**
       * Gets a specified property for an algorithm. The algorithm name
       * should be a standard name. See the <a href=
       * "{@docRoot}/../technotes/guides/security/StandardNames.html">
       * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
       * for information about standard algorithm names.
       *
       * One possible use is by specialized algorithm parsers, which may map
       * classes to algorithms which they understand (much like Key parsers
       * do).
       *
       * @param algName the algorithm name.
       *
       * @param propName the name of the property to get.
       *
       * @return the value of the specified property.
       *
       * @deprecated This method used to return the value of a proprietary
       * property in the master file of the "SUN" Cryptographic Service
       * Provider in order to determine how to parse algorithm-specific
       * parameters. Use the new provider-based and algorithm-independent
       * {@code AlgorithmParameters} and {@code KeyFactory} engine
       * classes (introduced in the J2SE version 1.2 platform) instead.
       */
      @Deprecated
      public static String getAlgorithmProperty(String algName,
                                                String propName) {
          ProviderProperty entry = getProviderProperty("Alg." + propName
                                                       + "." + algName);
          if (entry != null) {
              return entry.className;
          } else {
              return null;
          }
      }
  
      /**
       * Adds a new provider, at a specified position. The position is
       * the preference order in which providers are searched for
       * requested algorithms.  The position is 1-based, that is,
       * 1 is most preferred, followed by 2, and so on.
       *
       * <p>If the given provider is installed at the requested position,
       * the provider that used to be at that position, and all providers
       * with a position greater than {@code position}, are shifted up
       * one position (towards the end of the list of installed providers).
       *
       * <p>A provider cannot be added if it is already installed.
       *
       * <p>If there is a security manager, the
       * {@link SecurityManager#checkSecurityAccess} method is called
       * with the {@code "insertProvider"} permission target name to see if
       * it's ok to add a new provider. If this permission check is denied,
       * {@code checkSecurityAccess} is called again with the
       * {@code "insertProvider."+provider.getName()} permission target name. If
       * both checks are denied, a {@code SecurityException} is thrown.
       *
       * @param provider the provider to be added.
       *
       * @param position the preference position that the caller would
       * like for this provider.
       *
       * @return the actual preference position in which the provider was
       * added, or -1 if the provider was not added because it is
       * already installed.
       *
       * @throws  NullPointerException if provider is null
       * @throws  SecurityException
       *          if a security manager exists and its {@link
       *          SecurityManager#checkSecurityAccess} method
       *          denies access to add a new provider
       *
       * @see #getProvider
       * @see #removeProvider
       * @see SecurityPermission
       */
      public static synchronized int insertProviderAt(Provider provider,
              int position) {
          String providerName = provider.getName();
          // Android-removed: Checks using SecurityManager, which is not functional in Android.
          // checkInsertProvider(providerName);
          ProviderList list = Providers.getFullProviderList();
          ProviderList newList = ProviderList.insertAt(list, provider, position - 1);
          if (list == newList) {
              return -1;
          }
          // Android-added: Version tracking call.
          increaseVersion();
          Providers.setProviderList(newList);
          return newList.getIndex(providerName) + 1;
      }
  
      /**
       * Adds a provider to the next position available.
       *
       * <p>If there is a security manager, the
       * {@link SecurityManager#checkSecurityAccess} method is called
       * with the {@code "insertProvider"} permission target name to see if
       * it's ok to add a new provider. If this permission check is denied,
       * {@code checkSecurityAccess} is called again with the
       * {@code "insertProvider."+provider.getName()} permission target name. If
       * both checks are denied, a {@code SecurityException} is thrown.
       *
       * @param provider the provider to be added.
       *
       * @return the preference position in which the provider was
       * added, or -1 if the provider was not added because it is
       * already installed.
       *
       * @throws  NullPointerException if provider is null
       * @throws  SecurityException
       *          if a security manager exists and its {@link
       *          SecurityManager#checkSecurityAccess} method
       *          denies access to add a new provider
       *
       * @see #getProvider
       * @see #removeProvider
       * @see SecurityPermission
       */
      public static int addProvider(Provider provider) {
          /*
           * We can't assign a position here because the statically
           * registered providers may not have been installed yet.
           * insertProviderAt() will fix that value after it has
           * loaded the static providers.
           */
          return insertProviderAt(provider, 0);
      }
  
      /**
       * Removes the provider with the specified name.
       *
       * <p>When the specified provider is removed, all providers located
       * at a position greater than where the specified provider was are shifted
       * down one position (towards the head of the list of installed
       * providers).
       *
       * <p>This method returns silently if the provider is not installed or
       * if name is null.
       *
       * <p>First, if there is a security manager, its
       * {@code checkSecurityAccess}
       * method is called with the string {@code "removeProvider."+name}
       * to see if it's ok to remove the provider.
       * If the default implementation of {@code checkSecurityAccess}
       * is used (i.e., that method is not overriden), then this will result in
       * a call to the security manager's {@code checkPermission} method
       * with a {@code SecurityPermission("removeProvider."+name)}
       * permission.
       *
       * @param name the name of the provider to remove.
       *
       * @throws  SecurityException
       *          if a security manager exists and its {@link
       *          SecurityManager#checkSecurityAccess} method
       *          denies
       *          access to remove the provider
       *
       * @see #getProvider
       * @see #addProvider
       */
      public static synchronized void removeProvider(String name) {
          // Android-removed: Checks using SecurityManager, which is not functional in Android.
          // check("removeProvider." + name);
          ProviderList list = Providers.getFullProviderList();
          ProviderList newList = ProviderList.remove(list, name);
          Providers.setProviderList(newList);
          // Android-added: Version tracking call.
          increaseVersion();
      }
  
      /**
       * Returns an array containing all the installed providers. The order of
       * the providers in the array is their preference order.
       *
       * @return an array of all the installed providers.
       */
      public static Provider[] getProviders() {
          return Providers.getFullProviderList().toArray();
      }
  
      /**
       * Returns the provider installed with the specified name, if
       * any. Returns null if no provider with the specified name is
       * installed or if name is null.
       *
       * @param name the name of the provider to get.
       *
       * @return the provider of the specified name.
       *
       * @see #removeProvider
       * @see #addProvider
       */
      public static Provider getProvider(String name) {
          return Providers.getProviderList().getProvider(name);
      }
  
      /**
       * Returns an array containing all installed providers that satisfy the
       * specified selection criterion, or null if no such providers have been
       * installed. The returned providers are ordered
       * according to their
       * {@linkplain #insertProviderAt(Provider, int) preference order}.
       *
       * <p> A cryptographic service is always associated with a particular
       * algorithm or type. For example, a digital signature service is
       * always associated with a particular algorithm (e.g., DSA),
       * and a CertificateFactory service is always associated with
       * a particular certificate type (e.g., X.509).
       *
       * <p>The selection criterion must be specified in one of the following two
       * formats:
       * <ul>
       * <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
       * <p> The cryptographic service name must not contain any dots.
       * <p> A
       * provider satisfies the specified selection criterion iff the provider
       * implements the
       * specified algorithm or type for the specified cryptographic service.
       * <p> For example, "CertificateFactory.X.509"
       * would be satisfied by any provider that supplied
       * a CertificateFactory implementation for X.509 certificates.
       * <li> <i>{@literal <crypto_service>.<algorithm_or_type>
       * <attribute_name>:<attribute_value>}</i>
       * <p> The cryptographic service name must not contain any dots. There
       * must be one or more space characters between the
       * <i>{@literal <algorithm_or_type>}</i> and the
       * <i>{@literal <attribute_name>}</i>.
       *  <p> A provider satisfies this selection criterion iff the
       * provider implements the specified algorithm or type for the specified
       * cryptographic service and its implementation meets the
       * constraint expressed by the specified attribute name/value pair.
       * <p> For example, "Signature.SHA1withDSA KeySize:1024" would be
       * satisfied by any provider that implemented
       * the SHA1withDSA signature algorithm with a keysize of 1024 (or larger).
       *
       * </ul>
       *
       * <p> See the <a href=
       * "{@docRoot}/../technotes/guides/security/StandardNames.html">
       * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
       * for information about standard cryptographic service names, standard
       * algorithm names and standard attribute names.
       *
       * @param filter the criterion for selecting
       * providers. The filter is case-insensitive.
       *
       * @return all the installed providers that satisfy the selection
       * criterion, or null if no such providers have been installed.
       *
       * @throws InvalidParameterException
       *         if the filter is not in the required format
       * @throws NullPointerException if filter is null
       *
       * @see #getProviders(Map)
       * @since 1.3
       */
      public static Provider[] getProviders(String filter) {
          String key = null;
          String value = null;
          int index = filter.indexOf(':');
  
          if (index == -1) {
              key = filter;
              value = "";
          } else {
              key = filter.substring(0, index);
              value = filter.substring(index + 1);
          }
  
          Hashtable<String, String> hashtableFilter = new Hashtable<>(1);
          hashtableFilter.put(key, value);
  
          return (getProviders(hashtableFilter));
      }
  
      /**
       * Returns an array containing all installed providers that satisfy the
       * specified* selection criteria, or null if no such providers have been
       * installed. The returned providers are ordered
       * according to their
       * {@linkplain #insertProviderAt(Provider, int)
       * preference order}.
       *
       * <p>The selection criteria are represented by a map.
       * Each map entry represents a selection criterion.
       * A provider is selected iff it satisfies all selection
       * criteria. The key for any entry in such a map must be in one of the
       * following two formats:
       * <ul>
       * <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
       * <p> The cryptographic service name must not contain any dots.
       * <p> The value associated with the key must be an empty string.
       * <p> A provider
       * satisfies this selection criterion iff the provider implements the
       * specified algorithm or type for the specified cryptographic service.
       * <li>  <i>{@literal <crypto_service>}.
       * {@literal <algorithm_or_type> <attribute_name>}</i>
       * <p> The cryptographic service name must not contain any dots. There
       * must be one or more space characters between the
       * <i>{@literal <algorithm_or_type>}</i>
       * and the <i>{@literal <attribute_name>}</i>.
       * <p> The value associated with the key must be a non-empty string.
       * A provider satisfies this selection criterion iff the
       * provider implements the specified algorithm or type for the specified
       * cryptographic service and its implementation meets the
       * constraint expressed by the specified attribute name/value pair.
       * </ul>
       *
       * <p> See the <a href=
       * "../../../technotes/guides/security/StandardNames.html">
       * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
       * for information about standard cryptographic service names, standard
       * algorithm names and standard attribute names.
       *
       * @param filter the criteria for selecting
       * providers. The filter is case-insensitive.
       *
       * @return all the installed providers that satisfy the selection
       * criteria, or null if no such providers have been installed.
       *
       * @throws InvalidParameterException
       *         if the filter is not in the required format
       * @throws NullPointerException if filter is null
       *
       * @see #getProviders(String)
       * @since 1.3
       */
      public static Provider[] getProviders(Map<String,String> filter) {
          // Get all installed providers first.
          // Then only return those providers who satisfy the selection criteria.
          Provider[] allProviders = Security.getProviders();
          Set<String> keySet = filter.keySet();
          LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5);
  
          // Returns all installed providers
          // if the selection criteria is null.
          if ((keySet == null) || (allProviders == null)) {
              return allProviders;
          }
  
          boolean firstSearch = true;
  
          // For each selection criterion, remove providers
          // which don't satisfy the criterion from the candidate set.
          for (Iterator<String> ite = keySet.iterator(); ite.hasNext(); ) {
              String key = ite.next();
              String value = filter.get(key);
  
              LinkedHashSet<Provider> newCandidates = getAllQualifyingCandidates(key, value,
                                                                 allProviders);
              if (firstSearch) {
                  candidates = newCandidates;
                  firstSearch = false;
              }
  
              if ((newCandidates != null) && !newCandidates.isEmpty()) {
                  // For each provider in the candidates set, if it
                  // isn't in the newCandidate set, we should remove
                  // it from the candidate set.
                  for (Iterator<Provider> cansIte = candidates.iterator();
                       cansIte.hasNext(); ) {
                      Provider prov = cansIte.next();
                      if (!newCandidates.contains(prov)) {
                          cansIte.remove();
                      }
                  }
              } else {
                  candidates = null;
                  break;
              }
          }
  
          if ((candidates == null) || (candidates.isEmpty()))
              return null;
  
          Object[] candidatesArray = candidates.toArray();
          Provider[] result = new Provider[candidatesArray.length];
  
          for (int i = 0; i < result.length; i++) {
              result[i] = (Provider)candidatesArray[i];
          }
  
          return result;
      }
  
      // Map containing cached Spi Class objects of the specified type
      private static final Map<String, Class<?>> spiMap =
              new ConcurrentHashMap<>();
  
      /**
       * Return the Class object for the given engine type
       * (e.g. "MessageDigest"). Works for Spis in the java.security package
       * only.
       */
      private static Class<?> getSpiClass(String type) {
          Class<?> clazz = spiMap.get(type);
          if (clazz != null) {
              return clazz;
          }
          try {
              clazz = Class.forName("java.security." + type + "Spi");
              spiMap.put(type, clazz);
              return clazz;
          } catch (ClassNotFoundException e) {
              throw new AssertionError("Spi class not found", e);
          }
      }
  
      /*
       * Returns an array of objects: the first object in the array is
       * an instance of an implementation of the requested algorithm
       * and type, and the second object in the array identifies the provider
       * of that implementation.
       * The {@code provider} argument can be null, in which case all
       * configured providers will be searched in order of preference.
       */
      static Object[] getImpl(String algorithm, String type, String provider)
              throws NoSuchAlgorithmException, NoSuchProviderException {
          if (provider == null) {
              return GetInstance.getInstance
                  (type, getSpiClass(type), algorithm).toArray();
          } else {
              return GetInstance.getInstance
                  (type, getSpiClass(type), algorithm, provider).toArray();
          }
      }
  
      static Object[] getImpl(String algorithm, String type, String provider,
              Object params) throws NoSuchAlgorithmException,
              NoSuchProviderException, InvalidAlgorithmParameterException {
          if (provider == null) {
              return GetInstance.getInstance
                  (type, getSpiClass(type), algorithm, params).toArray();
          } else {
              return GetInstance.getInstance
                  (type, getSpiClass(type), algorithm, params, provider).toArray();
          }
      }
  
      /*
       * Returns an array of objects: the first object in the array is
       * an instance of an implementation of the requested algorithm
       * and type, and the second object in the array identifies the provider
       * of that implementation.
       * The {@code provider} argument cannot be null.
       */
      static Object[] getImpl(String algorithm, String type, Provider provider)
              throws NoSuchAlgorithmException {
          return GetInstance.getInstance
              (type, getSpiClass(type), algorithm, provider).toArray();
      }
  
      static Object[] getImpl(String algorithm, String type, Provider provider,
              Object params) throws NoSuchAlgorithmException,
              InvalidAlgorithmParameterException {
          return GetInstance.getInstance
              (type, getSpiClass(type), algorithm, params, provider).toArray();
      }
  
      /**
       * Gets a security property value.
       *
       * <p>First, if there is a security manager, its
       * {@code checkPermission}  method is called with a
       * {@code java.security.SecurityPermission("getProperty."+key)}
       * permission to see if it's ok to retrieve the specified
       * security property value..
       *
       * @param key the key of the property being retrieved.
       *
       * @return the value of the security property corresponding to key.
       *
       * @throws  SecurityException
       *          if a security manager exists and its {@link
       *          SecurityManager#checkPermission} method
       *          denies
       *          access to retrieve the specified security property value
       * @throws  NullPointerException is key is null
       *
       * @see #setProperty
       * @see SecurityPermission
       */
      public static String getProperty(String key) {
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
              sm.checkPermission(new SecurityPermission("getProperty."+
                                                        key));
          }
          String name = props.getProperty(key);
          if (name != null)
              name = name.trim(); // could be a class name with trailing ws
          return name;
      }
  
      /**
       * Sets a security property value.
       *
       * <p>First, if there is a security manager, its
       * {@code checkPermission} method is called with a
       * {@code java.security.SecurityPermission("setProperty."+key)}
       * permission to see if it's ok to set the specified
       * security property value.
       *
       * @param key the name of the property to be set.
       *
       * @param datum the value of the property to be set.
       *
       * @throws  SecurityException
       *          if a security manager exists and its {@link
       *          SecurityManager#checkPermission} method
       *          denies access to set the specified security property value
       * @throws  NullPointerException if key or datum is null
       *
       * @see #getProperty
       * @see SecurityPermission
       */
      public static void setProperty(String key, String datum) {
          // Android-removed: Checks using SecurityManager, which is not functional in Android.
          // check("setProperty."+key);
          props.put(key, datum);
          // Android-added: Version tracking call.
          increaseVersion();
          invalidateSMCache(key);  /* See below. */
      }
  
      /*
       * Implementation detail:  If the property we just set in
       * setProperty() was either "package.access" or
       * "package.definition", we need to signal to the SecurityManager
       * class that the value has just changed, and that it should
       * invalidate it's local cache values.
       *
       * Rather than create a new API entry for this function,
       * we use reflection to set a private variable.
       */
      private static void invalidateSMCache(String key) {
  
          final boolean pa = key.equals("package.access");
          final boolean pd = key.equals("package.definition");
  
          if (pa || pd) {
              AccessController.doPrivileged(new PrivilegedAction<Void>() {
                  public Void run() {
                      try {
                          /* Get the class via the bootstrap class loader. */
                          Class<?> cl = Class.forName(
                              "java.lang.SecurityManager", false, null);
                          Field f = null;
                          boolean accessible = false;
  
                          if (pa) {
                              f = cl.getDeclaredField("packageAccessValid");
                              accessible = f.isAccessible();
                              f.setAccessible(true);
                          } else {
                              f = cl.getDeclaredField("packageDefinitionValid");
                              accessible = f.isAccessible();
                              f.setAccessible(true);
                          }
                          f.setBoolean(f, false);
                          f.setAccessible(accessible);
                      }
                      catch (Exception e1) {
                          /* If we couldn't get the class, it hasn't
                           * been loaded yet.  If there is no such
                           * field, we shouldn't try to set it.  There
                           * shouldn't be a security execption, as we
                           * are loaded by boot class loader, and we
                           * are inside a doPrivileged() here.
                           *
                           * NOOP: don't do anything...
                           */
                      }
                      return null;
                  }  /* run */
              });  /* PrivilegedAction */
          }  /* if */
      }
  
      // BEGIN Android-removed: SecurityManager is stubbed on Android.
      /*
      private static void check(String directive) {
          SecurityManager security = System.getSecurityManager();
          if (security != null) {
              security.checkSecurityAccess(directive);
          }
      }
  
      private static void checkInsertProvider(String name) {
          SecurityManager security = System.getSecurityManager();
          if (security != null) {
              try {
                  security.checkSecurityAccess("insertProvider");
              } catch (SecurityException se1) {
                  try {
                      security.checkSecurityAccess("insertProvider." + name);
                  } catch (SecurityException se2) {
                      // throw first exception, but add second to suppressed
                      se1.addSuppressed(se2);
                      throw se1;
                  }
              }
          }
      }
      */
      // END Android-removed: SecurityManager is stubbed on Android.
  
      /*
      * Returns all providers who satisfy the specified
      * criterion.
      */
      private static LinkedHashSet<Provider> getAllQualifyingCandidates(
                                                  String filterKey,
                                                  String filterValue,
                                                  Provider[] allProviders) {
          String[] filterComponents = getFilterComponents(filterKey,
                                                          filterValue);
  
          // The first component is the service name.
          // The second is the algorithm name.
          // If the third isn't null, that is the attrinute name.
          String serviceName = filterComponents[0];
          String algName = filterComponents[1];
          String attrName = filterComponents[2];
  
          return getProvidersNotUsingCache(serviceName, algName, attrName,
                                           filterValue, allProviders);
      }
  
      private static LinkedHashSet<Provider> getProvidersNotUsingCache(
                                                  String serviceName,
                                                  String algName,
                                                  String attrName,
                                                  String filterValue,
                                                  Provider[] allProviders) {
          LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5);
          for (int i = 0; i < allProviders.length; i++) {
              if (isCriterionSatisfied(allProviders[i], serviceName,
                                       algName,
                                       attrName, filterValue)) {
                  candidates.add(allProviders[i]);
              }
          }
          return candidates;
      }
  
      /*
       * Returns true if the given provider satisfies
       * the selection criterion key:value.
       */
      private static boolean isCriterionSatisfied(Provider prov,
                                                  String serviceName,
                                                  String algName,
                                                  String attrName,
                                                  String filterValue) {
          String key = serviceName + '.' + algName;
  
          if (attrName != null) {
              key += ' ' + attrName;
          }
          // Check whether the provider has a property
          // whose key is the same as the given key.
          String propValue = getProviderProperty(key, prov);
  
          if (propValue == null) {
              // Check whether we have an alias instead
              // of a standard name in the key.
              String standardName = getProviderProperty("Alg.Alias." +
                                                        serviceName + "." +
                                                        algName,
                                                        prov);
              if (standardName != null) {
                  key = serviceName + "." + standardName;
  
                  if (attrName != null) {
                      key += ' ' + attrName;
                  }
  
                  propValue = getProviderProperty(key, prov);
              }
  
              if (propValue == null) {
                  // The provider doesn't have the given
                  // key in its property list.
                  return false;
              }
          }
  
          // If the key is in the format of:
          // <crypto_service>.<algorithm_or_type>,
          // there is no need to check the value.
  
          if (attrName == null) {
              return true;
          }
  
          // If we get here, the key must be in the
          // format of <crypto_service>.<algorithm_or_provider> <attribute_name>.
          if (isStandardAttr(attrName)) {
              return isConstraintSatisfied(attrName, filterValue, propValue);
          } else {
              return filterValue.equalsIgnoreCase(propValue);
          }
      }
  
      /*
       * Returns true if the attribute is a standard attribute;
       * otherwise, returns false.
       */
      private static boolean isStandardAttr(String attribute) {
          // For now, we just have two standard attributes:
          // KeySize and ImplementedIn.
          if (attribute.equalsIgnoreCase("KeySize"))
              return true;
  
          if (attribute.equalsIgnoreCase("ImplementedIn"))
              return true;
  
          return false;
      }
  
      /*
       * Returns true if the requested attribute value is supported;
       * otherwise, returns false.
       */
      private static boolean isConstraintSatisfied(String attribute,
                                                   String value,
                                                   String prop) {
          // For KeySize, prop is the max key size the
          // provider supports for a specific <crypto_service>.<algorithm>.
          if (attribute.equalsIgnoreCase("KeySize")) {
              int requestedSize = Integer.parseInt(value);
              int maxSize = Integer.parseInt(prop);
              if (requestedSize <= maxSize) {
                  return true;
              } else {
                  return false;
              }
          }
  
          // For Type, prop is the type of the implementation
          // for a specific <crypto service>.<algorithm>.
          if (attribute.equalsIgnoreCase("ImplementedIn")) {
              return value.equalsIgnoreCase(prop);
          }
  
          return false;
      }
  
      static String[] getFilterComponents(String filterKey, String filterValue) {
          int algIndex = filterKey.indexOf('.');
  
          if (algIndex < 0) {
              // There must be a dot in the filter, and the dot
              // shouldn't be at the beginning of this string.
              throw new InvalidParameterException("Invalid filter");
          }
  
          String serviceName = filterKey.substring(0, algIndex);
          String algName = null;
          String attrName = null;
  
          if (filterValue.length() == 0) {
              // The filterValue is an empty string. So the filterKey
              // should be in the format of <crypto_service>.<algorithm_or_type>.
              algName = filterKey.substring(algIndex + 1).trim();
              if (algName.length() == 0) {
                  // There must be a algorithm or type name.
                  throw new InvalidParameterException("Invalid filter");
              }
          } else {
              // The filterValue is a non-empty string. So the filterKey must be
              // in the format of
              // <crypto_service>.<algorithm_or_type> <attribute_name>
              int attrIndex = filterKey.indexOf(' ');
  
              if (attrIndex == -1) {
                  // There is no attribute name in the filter.
                  throw new InvalidParameterException("Invalid filter");
              } else {
                  attrName = filterKey.substring(attrIndex + 1).trim();
                  if (attrName.length() == 0) {
                      // There is no attribute name in the filter.
                      throw new InvalidParameterException("Invalid filter");
                  }
              }
  
              // There must be an algorithm name in the filter.
              if ((attrIndex < algIndex) ||
                  (algIndex == attrIndex - 1)) {
                  throw new InvalidParameterException("Invalid filter");
              } else {
                  algName = filterKey.substring(algIndex + 1, attrIndex);
              }
          }
  
          String[] result = new String[3];
          result[0] = serviceName;
          result[1] = algName;
          result[2] = attrName;
  
          return result;
      }
  
      /**
       * Returns a Set of Strings containing the names of all available
       * algorithms or types for the specified Java cryptographic service
       * (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore). Returns
       * an empty Set if there is no provider that supports the
       * specified service or if serviceName is null. For a complete list
       * of Java cryptographic services, please see the
       * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">Java
       * Cryptography Architecture API Specification &amp; Reference</a>.
       * Note: the returned set is immutable.
       *
       * @param serviceName the name of the Java cryptographic
       * service (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore).
       * Note: this parameter is case-insensitive.
       *
       * @return a Set of Strings containing the names of all available
       * algorithms or types for the specified Java cryptographic service
       * or an empty set if no provider supports the specified service.
       *
       * @since 1.4
       **/
      public static Set<String> getAlgorithms(String serviceName) {
  
          if ((serviceName == null) || (serviceName.length() == 0) ||
              (serviceName.endsWith("."))) {
              return Collections.emptySet();
          }
  
          HashSet<String> result = new HashSet<>();
          Provider[] providers = Security.getProviders();
  
          for (int i = 0; i < providers.length; i++) {
              // Check the keys for each provider.
              for (Enumeration<Object> e = providers[i].keys();
                                                  e.hasMoreElements(); ) {
                  String currentKey =
                          ((String)e.nextElement()).toUpperCase(Locale.ENGLISH);
                  if (currentKey.startsWith(
                          serviceName.toUpperCase(Locale.ENGLISH))) {
                      // We should skip the currentKey if it contains a
                      // whitespace. The reason is: such an entry in the
                      // provider property contains attributes for the
                      // implementation of an algorithm. We are only interested
                      // in entries which lead to the implementation
                      // classes.
                      if (currentKey.indexOf(" ") < 0) {
                          result.add(currentKey.substring(
                                                  serviceName.length() + 1));
                      }
                  }
              }
          }
          return Collections.unmodifiableSet(result);
      }
  
      // BEGIN Android-added: Methods for version handling.
      /**
       * @hide
       */
      public static void increaseVersion() {
          version.incrementAndGet();
      }
      /**
       * @hide
       */
      public static int getVersion() {
          return version.get();
      }
      // END Android-added: Methods for version handling.
  }
  