Merge "Fix CTS test failure under ART"
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
index f71acb1..e4caffa 100644
--- a/libart/src/main/java/java/lang/DexCache.java
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -71,7 +71,7 @@
     String[] strings;
 
     /** Holds C pointer to dexFile. */
-    private int dexFile;
+    private long dexFile;
 
     // Only created by the VM.
     private DexCache() {}
diff --git a/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java
index 5c81e36..2cc2857 100644
--- a/libart/src/main/java/java/lang/Thread.java
+++ b/libart/src/main/java/java/lang/Thread.java
@@ -124,8 +124,8 @@
      */
     public static final int NORM_PRIORITY = 5;
 
-    /* some of these are accessed directly by the VM; do not rename them */
-    private volatile int nativePeer;
+    /* Some of these are accessed directly by the VM; do not rename them. */
+    private volatile long nativePeer;
     volatile ThreadGroup group;
     volatile boolean daemon;
     volatile String name;
diff --git a/libart/src/main/java/java/lang/reflect/ArtMethod.java b/libart/src/main/java/java/lang/reflect/ArtMethod.java
index 9e90c8f..c88daef 100644
--- a/libart/src/main/java/java/lang/reflect/ArtMethod.java
+++ b/libart/src/main/java/java/lang/reflect/ArtMethod.java
@@ -42,38 +42,99 @@
  * @hide
  */
 public final class ArtMethod {
+    /* A note on the field order here, it reflects the same field order as laid out by ART. */
 
     /** Method's declaring class */
     private Class<?> declaringClass;
-    /** Method access flags (modifiers) */
-    private int accessFlags;
-    /** DexFile index */
-    private int methodDexIndex;
-    /** Dispatch table entry */
-    private int methodIndex;
-    /** DexFile offset of CodeItem for this Method */
-    private int codeItemOffset;
-    /* ART compiler meta-data */
-    private int frameSizeInBytes;
-    private int coreSpillMask;
-    private int fpSpillMask;
-    private int mappingTable;
-    private int gcMap;
-    private int vmapTable;
-    /** ART: compiled managed code associated with this Method */
-    private int entryPointFromCompiledCode;
-    /** ART: entry point from interpreter associated with this Method */
-    private int entryPointFromInterpreter;
-    /** ART: if this is a native method, the native code that will be invoked */
-    private int nativeMethod;
-    /* ART: dex cache fast access */
-    private String[] dexCacheStrings;
-    Class<?>[] dexCacheResolvedTypes;
+
+    /** Short-cut to declaringClass.dexCache.resolvedMethods */
     private ArtMethod[] dexCacheResolvedMethods;
 
+    /** Short-cut to declaringClass.dexCache.resolvedTypes */
+    /* package */ Class<?>[] dexCacheResolvedTypes;
+
+    /** Short-cut to declaringClass.dexCache.strings */
+    private String[] dexCacheStrings;
+
     /**
-     * Only created by art directly.
+     * Method dispatch from the interpreter invokes this pointer which may cause a bridge into
+     * compiled code.
      */
+    private long entryPointFromInterpreter;
+
+    /**
+     * Pointer to JNI function registered to this method, or a function to resolve the JNI function.
+     */
+    private long entryPointFromJni;
+
+    /**
+     * Method dispatch from portable compiled code invokes this pointer which may cause bridging
+     * into quick compiled code or the interpreter.
+     */
+    private long entryPointFromPortableCompiledCode;
+
+    /**
+     * Method dispatch from quick compiled code invokes this pointer which may cause bridging
+     * into portable compiled code or the interpreter.
+     */
+    private long entryPointFromQuickCompiledCode;
+
+    /**
+     * Pointer to a data structure created by the compiler and used by the garbage collector to
+     * determine which registers hold live references to objects within the heap.
+     */
+    private long gcMap;
+
+    /* Quick compiler meta-data. TODO: merge and place in native heap. */
+
+    /**
+     * Pointer to a data structure created by the quick compiler to map between dex PCs and
+     * native PCs, and vice-versa.
+     */
+    private long quickMappingTable;
+
+    /**
+     * Pointer to a data structure used by the quick compiler to map between dalvik and machine
+     * registers.
+     */
+    private long quickVmapTable;
+
+    /* End of quick compiler meta-data. */
+
+    /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */
+    private int accessFlags;
+
+    /* Dex file fields. The defining dex file is available via declaringClass.dexCache */
+
+    /** The offset of the code item associated with this method within its defining dex file */
+    private int dexCodeItemOffset;
+
+    /** The method index of this method within its defining dex file */
+    private int dexMethodIndex;
+
+    /* End of dex file fields. */
+
+    /**
+     * Entry within a dispatch table for this method. For static/direct methods the index is
+     * into the declaringClass.directMethods, for virtual methods the vtable and for
+     * interface methods the ifTable.
+     */
+    private int methodIndex;
+
+    /* Quick compiler meta-data. TODO: merge and place in native heap. */
+
+    /** Bit map of spilled machine registers. */
+    private int quickCoreSpillMask;
+
+    /** Bit map of spilled floating point machine registers. */
+    private int quickFpSpillMask;
+
+    /** Fixed frame size for this method when executed. */
+    private int quickFrameSizeInBytes;
+
+    /* End of quick compiler meta-data. */
+
+    /** Only created by ART directly. */
     private ArtMethod() {}
 
     Class getDeclaringClass() {
@@ -85,7 +146,7 @@
     }
 
     int getDexMethodIndex() {
-        return methodDexIndex;
+        return dexMethodIndex;
     }
 
     public static String getMethodName(ArtMethod artMethod) {
@@ -126,7 +187,7 @@
 
     Class<?>[] getParameterTypes() {
         Dex dex = getDeclaringClass().getDex();
-        short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
         if (types.length == 0) {
             return EmptyArray.CLASS;
         }
@@ -140,7 +201,7 @@
 
     Class<?> getReturnType() {
         Dex dex = declaringClass.getDex();
-        int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(methodDexIndex);
+        int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(dexMethodIndex);
         // Note, in the case of a Proxy the dex cache types are equal.
         return getDexCacheType(dex, returnTypeIndex);
     }
@@ -152,7 +213,7 @@
      */
     int compareParameters(Class<?>[] params) {
         Dex dex = getDeclaringClass().getDex();
-        short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
         int length = Math.min(types.length, params.length);
         for (int i = 0; i < length; i++) {
             Class<?> aType = getDexCacheType(dex, types[i]);
@@ -168,7 +229,7 @@
     }
 
     Annotation[][] getParameterAnnotations() {
-        return AnnotationAccess.getParameterAnnotations(declaringClass, methodDexIndex);
+        return AnnotationAccess.getParameterAnnotations(declaringClass, dexMethodIndex);
     }
 
     /**
@@ -210,7 +271,7 @@
             // Proxy method's declaring class' dex cache refers to that of Proxy. The local cache in
             // Method refers to the original interface's dex cache and is ensured to be resolved by
             // proxy generation.
-            return dexCacheResolvedMethods[methodDexIndex];
+            return dexCacheResolvedMethods[dexMethodIndex];
         }
         return this;
     }
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index 1147291..e70898b 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -21,11 +21,11 @@
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Set;
 import org.apache.harmony.security.fortress.Engine;
 
-
 /**
  * {@code Signature} is an engine class which is capable of creating and
  * verifying digital signatures, using different algorithms that have been
@@ -39,13 +39,13 @@
     private static final String SERVICE = "Signature";
 
     // Used to access common engine functionality
-    private static Engine ENGINE = new Engine(SERVICE);
+    private static final Engine ENGINE = new Engine(SERVICE);
 
     // The provider
-    private Provider provider;
+    Provider provider;
 
     // The algorithm.
-    private String algorithm;
+    final String algorithm;
 
     /**
      * Constant that indicates that this {@code Signature} instance has not yet
@@ -101,16 +101,7 @@
         if (algorithm == null) {
             throw new NullPointerException("algorithm == null");
         }
-        Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
-        Object spi = sap.spi;
-        Provider provider = sap.provider;
-        if (spi instanceof Signature) {
-            Signature result = (Signature) spi;
-            result.algorithm = algorithm;
-            result.provider = provider;
-            return result;
-        }
-        return new SignatureImpl((SignatureSpi) spi, provider, algorithm);
+        return getSignature(algorithm, null);
     }
 
     /**
@@ -143,7 +134,7 @@
         if (p == null) {
             throw new NoSuchProviderException(provider);
         }
-        return getSignatureInstance(algorithm, p);
+        return getSignature(algorithm, p);
     }
 
     /**
@@ -171,19 +162,64 @@
         if (provider == null) {
             throw new IllegalArgumentException("provider == null");
         }
-        return getSignatureInstance(algorithm, provider);
+        return getSignature(algorithm, provider);
     }
 
-    private static Signature getSignatureInstance(String algorithm,
-            Provider provider) throws NoSuchAlgorithmException {
-        Object spi = ENGINE.getInstance(algorithm, provider, null);
-        if (spi instanceof Signature) {
-            Signature result = (Signature) spi;
-            result.algorithm = algorithm;
-            result.provider = provider;
-            return result;
+    private static Signature getSignature(String algorithm, Provider provider)
+            throws NoSuchAlgorithmException {
+        if (algorithm == null || algorithm.isEmpty()) {
+            throw new NoSuchAlgorithmException("Unknown algorithm: " + algorithm);
         }
-        return new SignatureImpl((SignatureSpi) spi, provider, algorithm);
+
+        if (tryAlgorithm(null, provider, algorithm) == null) {
+            if (provider == null) {
+                throw new NoSuchAlgorithmException("No provider found for " + algorithm);
+            } else {
+                throw new NoSuchAlgorithmException("Provider " + provider.getName()
+                        + " does not provide " + algorithm);
+            }
+        }
+        return new SignatureImpl(algorithm, provider);
+    }
+
+    private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+        if (provider != null) {
+            Provider.Service service = provider.getService(SERVICE, algorithm);
+            if (service == null) {
+                return null;
+            }
+            return tryAlgorithmWithProvider(key, service);
+        }
+        ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
+        if (services == null) {
+            return null;
+        }
+        for (Provider.Service service : services) {
+            Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
+            if (sap != null) {
+                return sap;
+            }
+        }
+        return null;
+    }
+
+    private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+        try {
+            if (key != null && !service.supportsParameter(key)) {
+                return null;
+            }
+
+            Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
+            if (sap.spi == null || sap.provider == null) {
+                return null;
+            }
+            if (!(sap.spi instanceof SignatureSpi)) {
+                return null;
+            }
+            return sap;
+        } catch (NoSuchAlgorithmException ignored) {
+        }
+        return null;
     }
 
     /**
@@ -192,10 +228,18 @@
      * @return the provider associated with this {@code Signature}.
      */
     public final Provider getProvider() {
+        ensureProviderChosen();
         return provider;
     }
 
     /**
+     * This makes sure the provider is chosen since Signature is abstract and
+     * getProvider is final but we need to support late binding.
+     */
+    void ensureProviderChosen() {
+    }
+
+    /**
      * Returns the name of the algorithm of this {@code Signature}.
      *
      * @return the name of the algorithm of this {@code Signature}.
@@ -238,10 +282,10 @@
     public final void initVerify(Certificate certificate)
             throws InvalidKeyException {
         if (certificate instanceof X509Certificate) {
-            Set ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
+            Set<String> ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
             boolean critical = false;
             if (ce != null && !ce.isEmpty()) {
-                for (Iterator i = ce.iterator(); i.hasNext();) {
+                for (Iterator<String> i = ce.iterator(); i.hasNext();) {
                     if ("2.5.29.15".equals(i.next())) {
                         //KeyUsage OID = 2.5.29.15
                         critical = true;
@@ -584,79 +628,117 @@
     }
 
     /**
-     *
      * Internal Signature implementation
-     *
      */
     private static class SignatureImpl extends Signature {
 
-        private final SignatureSpi spiImpl;
+        /**
+         * Lock held while the SPI is initializing.
+         */
+        private final Object initLock = new Object();
 
-        public SignatureImpl(SignatureSpi signatureSpi, Provider provider,
-                String algorithm) {
+        // The provider specified when creating this instance.
+        private final Provider specifiedProvider;
+
+        private SignatureSpi spiImpl;
+
+        public SignatureImpl(String algorithm, Provider provider) {
             super(algorithm);
-            super.provider = provider;
-            spiImpl = signatureSpi;
+            this.specifiedProvider = provider;
+        }
+
+        private SignatureImpl(String algorithm, Provider provider, SignatureSpi spi) {
+            this(algorithm, provider);
+            spiImpl = spi;
+        }
+
+        @Override
+        void ensureProviderChosen() {
+            getSpi(null);
         }
 
         @Override
         protected byte[] engineSign() throws SignatureException {
-            return spiImpl.engineSign();
+            return getSpi().engineSign();
         }
 
         @Override
         protected void engineUpdate(byte arg0) throws SignatureException {
-            spiImpl.engineUpdate(arg0);
+            getSpi().engineUpdate(arg0);
         }
 
         @Override
         protected boolean engineVerify(byte[] arg0) throws SignatureException {
-            return spiImpl.engineVerify(arg0);
+            return getSpi().engineVerify(arg0);
         }
 
         @Override
-        protected void engineUpdate(byte[] arg0, int arg1, int arg2)
-                throws SignatureException {
-            spiImpl.engineUpdate(arg0, arg1, arg2);
+        protected void engineUpdate(byte[] arg0, int arg1, int arg2) throws SignatureException {
+            getSpi().engineUpdate(arg0, arg1, arg2);
         }
 
         @Override
-        protected void engineInitSign(PrivateKey arg0)
-                throws InvalidKeyException {
-            spiImpl.engineInitSign(arg0);
+        protected void engineInitSign(PrivateKey arg0) throws InvalidKeyException {
+            getSpi(arg0).engineInitSign(arg0);
         }
 
         @Override
-        protected void engineInitVerify(PublicKey arg0)
-                throws InvalidKeyException {
-            spiImpl.engineInitVerify(arg0);
+        protected void engineInitVerify(PublicKey arg0) throws InvalidKeyException {
+            getSpi(arg0).engineInitVerify(arg0);
         }
 
         @Override
-        protected Object engineGetParameter(String arg0)
-                throws InvalidParameterException {
-            return spiImpl.engineGetParameter(arg0);
+        protected Object engineGetParameter(String arg0) throws InvalidParameterException {
+            return getSpi().engineGetParameter(arg0);
         }
 
         @Override
         protected void engineSetParameter(String arg0, Object arg1)
                 throws InvalidParameterException {
-            spiImpl.engineSetParameter(arg0, arg1);
+            getSpi().engineSetParameter(arg0, arg1);
         }
 
         @Override
         protected void engineSetParameter(AlgorithmParameterSpec arg0)
                 throws InvalidAlgorithmParameterException {
-            spiImpl.engineSetParameter(arg0);
+            getSpi().engineSetParameter(arg0);
         }
 
         @Override
         public Object clone() throws CloneNotSupportedException {
             if (spiImpl instanceof Cloneable) {
                 SignatureSpi spi = (SignatureSpi) spiImpl.clone();
-                return new SignatureImpl(spi, getProvider(), getAlgorithm());
+                return new SignatureImpl(getAlgorithm(), getProvider(), spiImpl);
             }
             throw new CloneNotSupportedException();
         }
+
+        /**
+         * Makes sure a CipherSpi that matches this type is selected.
+         */
+        private SignatureSpi getSpi(Key key) {
+            synchronized (initLock) {
+                if (spiImpl != null && key == null) {
+                    return spiImpl;
+                }
+
+                final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
+                if (sap == null) {
+                    throw new ProviderException("No provider for " + getAlgorithm());
+                }
+
+                spiImpl = (SignatureSpi) sap.spi;
+                provider = sap.provider;
+
+                return spiImpl;
+            }
+        }
+
+        /**
+         * Convenience call when the Key is not available.
+         */
+        private SignatureSpi getSpi() {
+            return getSpi(null);
+        }
     }
 }
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 9ddf5ea..9bca45e 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -129,6 +129,11 @@
     private Provider provider;
 
     /**
+     * The provider specified when instance created.
+     */
+    private final Provider specifiedProvider;
+
+    /**
      * The SPI implementation.
      */
     private CipherSpi spiImpl;
@@ -170,7 +175,7 @@
         if (!(cipherSpi instanceof NullCipherSpi) && provider == null) {
             throw new NullPointerException("provider == null");
         }
-        this.provider = provider;
+        this.specifiedProvider = provider;
         this.spiImpl = cipherSpi;
         this.transformation = transformation;
         this.transformParts = null;
@@ -179,7 +184,7 @@
     private Cipher(String transformation, String[] transformParts, Provider provider) {
         this.transformation = transformation;
         this.transformParts = transformParts;
-        this.provider = provider;
+        this.specifiedProvider = provider;
     }
 
 
@@ -332,11 +337,12 @@
      */
     private CipherSpi getSpi(Key key) {
         synchronized (initLock) {
-            if (spiImpl != null) {
+            if (spiImpl != null && key == null) {
                 return spiImpl;
             }
 
-            final Engine.SpiAndProvider sap = tryCombinations(key, provider, transformParts);
+            final Engine.SpiAndProvider sap = tryCombinations(key, specifiedProvider,
+                    transformParts);
             if (sap == null) {
                 throw new ProviderException("No provider for " + transformation);
             }
@@ -436,10 +442,10 @@
 
             Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
             if (sap.spi == null || sap.provider == null) {
-                return sap;
+                return null;
             }
             if (!(sap.spi instanceof CipherSpi)) {
-                return sap;
+                return null;
             }
             CipherSpi spi = (CipherSpi) sap.spi;
             if (((type == NeedToSet.MODE) || (type == NeedToSet.BOTH))
@@ -475,7 +481,7 @@
 
     /**
      * Returns the provider of this cipher instance.
-     * 
+     *
      * @return the provider of this cipher instance.
      */
     public final Provider getProvider() {
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index dc3fa2f..abcfd0e 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -23,9 +23,11 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.Provider;
+import java.security.ProviderException;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
 import org.apache.harmony.security.fortress.Engine;
 
 /**
@@ -35,22 +37,33 @@
  */
 public class KeyAgreement {
 
+    // The service name.
+    private static final String SERVICE = "KeyAgreement";
+
     // Used to access common engine functionality
-    private static final Engine ENGINE = new Engine("KeyAgreement");
+    private static final Engine ENGINE = new Engine(SERVICE);
 
     // Store SecureRandom
     private static final SecureRandom RANDOM = new SecureRandom();
 
     // Store used provider
-    private final Provider provider;
+    private Provider provider;
+
+    // Provider that was requested during creation.
+    private final Provider specifiedProvider;
 
     // Store used spi implementation
-    private final KeyAgreementSpi spiImpl;
+    private KeyAgreementSpi spiImpl;
 
     // Store used algorithm name
     private final String algorithm;
 
     /**
+     * Lock held while the SPI is initializing.
+     */
+    private final Object initLock = new Object();
+
+    /**
      * Creates a new {@code KeyAgreement} instance.
      *
      * @param keyAgreeSpi
@@ -62,9 +75,9 @@
      */
     protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider,
             String algorithm) {
-        this.provider = provider;
-        this.algorithm = algorithm;
         this.spiImpl = keyAgreeSpi;
+        this.specifiedProvider = provider;
+        this.algorithm = algorithm;
     }
 
     /**
@@ -82,6 +95,7 @@
      * @return the provider for this {@code KeyAgreement} instance.
      */
     public final Provider getProvider() {
+        getSpi();
         return provider;
     }
 
@@ -96,13 +110,8 @@
      * @throws NullPointerException
      *             if the specified algorithm is {@code null}.
      */
-    public static final KeyAgreement getInstance(String algorithm)
-            throws NoSuchAlgorithmException {
-        if (algorithm == null) {
-            throw new NullPointerException("algorithm == null");
-        }
-        Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
-        return new KeyAgreement((KeyAgreementSpi) sap.spi, sap.provider, algorithm);
+    public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException {
+        return getKeyAgreement(algorithm, null);
     }
 
     /**
@@ -124,9 +133,8 @@
      * @throws IllegalArgumentException
      *             if the specified provider name is {@code null} or empty.
      */
-    public static final KeyAgreement getInstance(String algorithm,
-            String provider) throws NoSuchAlgorithmException,
-            NoSuchProviderException {
+    public static final KeyAgreement getInstance(String algorithm, String provider)
+            throws NoSuchAlgorithmException, NoSuchProviderException {
         if (provider == null || provider.isEmpty()) {
             throw new IllegalArgumentException("Provider is null or empty");
         }
@@ -134,7 +142,7 @@
         if (impProvider == null) {
             throw new NoSuchProviderException(provider);
         }
-        return getInstance(algorithm, impProvider);
+        return getKeyAgreement(algorithm, impProvider);
     }
 
     /**
@@ -156,29 +164,108 @@
      * @throws NullPointerException
      *             if the specified algorithm name is {@code null}.
      */
-    public static final KeyAgreement getInstance(String algorithm,
-            Provider provider) throws NoSuchAlgorithmException {
+    public static final KeyAgreement getInstance(String algorithm, Provider provider)
+            throws NoSuchAlgorithmException {
         if (provider == null) {
             throw new IllegalArgumentException("provider == null");
         }
+        return getKeyAgreement(algorithm, provider);
+    }
+
+    private static KeyAgreement getKeyAgreement(String algorithm, Provider provider)
+            throws NoSuchAlgorithmException {
         if (algorithm == null) {
             throw new NullPointerException("algorithm == null");
         }
-        Object spi = ENGINE.getInstance(algorithm, provider, null);
-        return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm);
+
+        if (tryAlgorithm(null, provider, algorithm) == null) {
+            if (provider == null) {
+                throw new NoSuchAlgorithmException("No provider found for " + algorithm);
+            } else {
+                throw new NoSuchAlgorithmException("Provider " + provider.getName()
+                        + " does not provide " + algorithm);
+            }
+        }
+        return new KeyAgreement(null, provider, algorithm);
+    }
+
+    private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+        if (provider != null) {
+            Provider.Service service = provider.getService(SERVICE, algorithm);
+            if (service == null) {
+                return null;
+            }
+            return tryAlgorithmWithProvider(key, service);
+        }
+        ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
+        if (services == null) {
+            return null;
+        }
+        for (Provider.Service service : services) {
+            Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
+            if (sap != null) {
+                return sap;
+            }
+        }
+        return null;
+    }
+
+    private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+        try {
+            if (key != null && !service.supportsParameter(key)) {
+                return null;
+            }
+
+            Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
+            if (sap.spi == null || sap.provider == null) {
+                return null;
+            }
+            if (!(sap.spi instanceof KeyAgreementSpi)) {
+                return null;
+            }
+            return sap;
+        } catch (NoSuchAlgorithmException ignored) {
+        }
+        return null;
+    }
+
+    /**
+     * Makes sure a KeyAgreementSpi that matches this type is selected.
+     */
+    private KeyAgreementSpi getSpi(Key key) {
+        synchronized (initLock) {
+            if (spiImpl != null && key == null) {
+                return spiImpl;
+            }
+
+            final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
+            if (sap == null) {
+                throw new ProviderException("No provider for " + getAlgorithm());
+            }
+
+            spiImpl = (KeyAgreementSpi) sap.spi;
+            provider = sap.provider;
+
+            return spiImpl;
+        }
+    }
+
+    /**
+     * Convenience call when the Key is not available.
+     */
+    private KeyAgreementSpi getSpi() {
+        return getSpi(null);
     }
 
     /**
      * Initializes this {@code KeyAgreement} with the specified key.
      *
-     * @param key
-     *            the key to initialize this key agreement.
-     * @throws InvalidKeyException
-     *             if the specified key cannot be used to initialize this key
-     *             agreement.
+     * @param key the key to initialize this key agreement.
+     * @throws InvalidKeyException if the specified key cannot be used to
+     *             initialize this key agreement.
      */
     public final void init(Key key) throws InvalidKeyException {
-        spiImpl.engineInit(key, RANDOM);//new SecureRandom());
+        getSpi(key).engineInit(key, RANDOM);//new SecureRandom());
     }
 
     /**
@@ -195,7 +282,7 @@
      */
     public final void init(Key key, SecureRandom random)
             throws InvalidKeyException {
-        spiImpl.engineInit(key, random);
+        getSpi(key).engineInit(key, random);
     }
 
     /**
@@ -215,7 +302,7 @@
      */
     public final void init(Key key, AlgorithmParameterSpec params)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
-        spiImpl.engineInit(key, params, RANDOM);//new SecureRandom());
+        getSpi(key).engineInit(key, params, RANDOM);//new SecureRandom());
     }
 
     /**
@@ -238,7 +325,7 @@
     public final void init(Key key, AlgorithmParameterSpec params,
             SecureRandom random) throws InvalidKeyException,
             InvalidAlgorithmParameterException {
-        spiImpl.engineInit(key, params, random);
+        getSpi(key).engineInit(key, params, random);
     }
 
     /**
@@ -260,7 +347,7 @@
      */
     public final Key doPhase(Key key, boolean lastPhase)
             throws InvalidKeyException, IllegalStateException {
-        return spiImpl.engineDoPhase(key, lastPhase);
+        return getSpi().engineDoPhase(key, lastPhase);
     }
 
     /**
@@ -271,7 +358,7 @@
      *             if this key agreement is not complete.
      */
     public final byte[] generateSecret() throws IllegalStateException {
-        return spiImpl.engineGenerateSecret();
+        return getSpi().engineGenerateSecret();
     }
 
     /**
@@ -290,7 +377,7 @@
      */
     public final int generateSecret(byte[] sharedSecret, int offset)
             throws IllegalStateException, ShortBufferException {
-        return spiImpl.engineGenerateSecret(sharedSecret, offset);
+        return getSpi().engineGenerateSecret(sharedSecret, offset);
     }
 
     /**
@@ -312,7 +399,7 @@
     public final SecretKey generateSecret(String algorithm)
             throws IllegalStateException, NoSuchAlgorithmException,
             InvalidKeyException {
-        return spiImpl.engineGenerateSecret(algorithm);
+        return getSpi().engineGenerateSecret(algorithm);
     }
 
 }
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 073eb2d..2c94882 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -171,10 +171,8 @@
    * by {@code toString}. This is very lenient, and doesn't care what's between the underscores:
    * this method can parse strings that {@code Locale.toString} won't produce.
    * Used to remove duplication.
-   *
-   * Package visible for testing.
    */
-  static Locale localeFromIcuLocaleId(String localeId) {
+  public static Locale localeFromIcuLocaleId(String localeId) {
     // @ == ULOC_KEYWORD_SEPARATOR_UNICODE (uloc.h).
     final int extensionsIndex = localeId.indexOf('@');
 
diff --git a/luni/src/main/java/libcore/io/StructStat.java b/luni/src/main/java/libcore/io/StructStat.java
index 05ecca7..00371fb 100644
--- a/luni/src/main/java/libcore/io/StructStat.java
+++ b/luni/src/main/java/libcore/io/StructStat.java
@@ -70,7 +70,7 @@
     /** Number of blocks allocated for this object. */
     public final long st_blocks; /*blkcnt_t*/
 
-    StructStat(long st_dev, long st_ino, int st_mode, long st_nlink, int st_uid, int st_gid,
+    public StructStat(long st_dev, long st_ino, int st_mode, long st_nlink, int st_uid, int st_gid,
             long st_rdev, long st_size, long st_atime, long st_mtime, long st_ctime,
             long st_blksize, long st_blocks) {
         this.st_dev = st_dev;
diff --git a/luni/src/main/java/libcore/io/StructStatVfs.java b/luni/src/main/java/libcore/io/StructStatVfs.java
index bb78ff2..bdff111 100644
--- a/luni/src/main/java/libcore/io/StructStatVfs.java
+++ b/luni/src/main/java/libcore/io/StructStatVfs.java
@@ -53,7 +53,7 @@
   /** Maximum filename length. */
   public final long f_namemax; /*unsigned long*/
 
-  StructStatVfs(long f_bsize, long f_frsize, long f_blocks, long f_bfree, long f_bavail,
+  public StructStatVfs(long f_bsize, long f_frsize, long f_blocks, long f_bfree, long f_bavail,
                 long f_files, long f_ffree, long f_favail,
                 long f_fsid, long f_flag, long f_namemax) {
     this.f_bsize = f_bsize;
diff --git a/luni/src/main/java/libcore/io/StructUcred.java b/luni/src/main/java/libcore/io/StructUcred.java
index 359995d..c13212c 100644
--- a/luni/src/main/java/libcore/io/StructUcred.java
+++ b/luni/src/main/java/libcore/io/StructUcred.java
@@ -29,7 +29,7 @@
   /** The peer process' gid. */
   public final int gid;
 
-  private StructUcred(int pid, int uid, int gid) {
+  public StructUcred(int pid, int uid, int gid) {
     this.pid = pid;
     this.uid = uid;
     this.gid = gid;
diff --git a/luni/src/main/java/libcore/io/StructUtsname.java b/luni/src/main/java/libcore/io/StructUtsname.java
index e6a8e42..d7d606b 100644
--- a/luni/src/main/java/libcore/io/StructUtsname.java
+++ b/luni/src/main/java/libcore/io/StructUtsname.java
@@ -37,7 +37,7 @@
     /** The machine architecture, such as "armv7l" or "x86_64". */
     public final String machine;
 
-    StructUtsname(String sysname, String nodename, String release, String version, String machine) {
+    public StructUtsname(String sysname, String nodename, String release, String version, String machine) {
         this.sysname = sysname;
         this.nodename = nodename;
         this.release = release;
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
index 006dda8..855a8c7 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
@@ -69,7 +69,7 @@
  *
  * }</pre>
  */
-public class Engine {
+public final class Engine {
 
     /**
      * Access to package visible api in java.security
diff --git a/luni/src/test/java/libcore/java/security/MockPrivateKey.java b/luni/src/test/java/libcore/java/security/MockPrivateKey.java
new file mode 100644
index 0000000..e5ac797
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockPrivateKey.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.security;
+
+import java.security.PrivateKey;
+
+/**
+ * A mock PrivateKey class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockPrivateKey implements PrivateKey {
+    @Override
+    public String getAlgorithm() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public String getFormat() {
+        return "MOCK";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/MockPrivateKey2.java b/luni/src/test/java/libcore/java/security/MockPrivateKey2.java
new file mode 100644
index 0000000..a1c02c9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockPrivateKey2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.security;
+
+import java.security.PrivateKey;
+
+/**
+ * A mock PrivateKey class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockPrivateKey2 implements PrivateKey {
+    @Override
+    public String getAlgorithm() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public String getFormat() {
+        return "MOCK";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/MockPublicKey.java b/luni/src/test/java/libcore/java/security/MockPublicKey.java
new file mode 100644
index 0000000..130b461
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockPublicKey.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.security;
+
+import java.security.PublicKey;
+
+/**
+ * A mock PublicKey class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockPublicKey implements PublicKey {
+    @Override
+    public String getAlgorithm() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public String getFormat() {
+        return "MOCK";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/MockSignatureSpi.java b/luni/src/test/java/libcore/java/security/MockSignatureSpi.java
new file mode 100644
index 0000000..6017547
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockSignatureSpi.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.security;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * Mock SignatureSpi used by {@link SignatureTest}.
+ */
+public class MockSignatureSpi extends SignatureSpi {
+    public static class SpecificKeyTypes extends MockSignatureSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            if (!(key instanceof MockPrivateKey)) {
+                throw new InvalidKeyException("Must be MockPrivateKey!");
+            }
+        }
+    }
+
+    public static class SpecificKeyTypes2 extends MockSignatureSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            if (!(key instanceof MockPrivateKey2)) {
+                throw new InvalidKeyException("Must be MockPrivateKey2!");
+            }
+        }
+    }
+
+    public static class AllKeyTypes extends MockSignatureSpi {
+    }
+
+    public void checkKeyType(Key key) throws InvalidKeyException {
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey)
+     */
+    @Override
+    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey)
+     */
+    @Override
+    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+        checkKeyType(privateKey);
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineUpdate(byte)
+     */
+    @Override
+    protected void engineUpdate(byte b) throws SignatureException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineSign()
+     */
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineVerify(byte[])
+     */
+    @Override
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object)
+     */
+    @Override
+    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineGetParameter(java.lang.String)
+     */
+    @Override
+    protected Object engineGetParameter(String param) throws InvalidParameterException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see java.security.SignatureSpi#engineUpdate(byte[], int, int)
+     */
+    @Override
+    protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 1027dd4..9c5738b 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -18,6 +18,7 @@
 
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
 import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -38,10 +39,192 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
-
 import junit.framework.TestCase;
 
 public class SignatureTest extends TestCase {
+    private static abstract class MockProvider extends Provider {
+        public MockProvider(String name) {
+            super(name, 1.0, "Mock provider used for testing");
+            setup();
+        }
+
+        public abstract void setup();
+    }
+
+    public void testSignature_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        {
+            Signature s = Signature.getInstance("FOO", mockProvider);
+            s.initSign(new MockPrivateKey());
+            assertEquals(mockProvider, s.getProvider());
+        }
+    }
+
+    public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
+            throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        Security.addProvider(mockProvider);
+        try {
+            {
+                Provider mockProvider2 = new MockProvider("MockProvider") {
+                    public void setup() {
+                        put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+                    }
+                };
+                Signature s = Signature.getInstance("FOO", mockProvider2);
+                assertEquals(mockProvider2, s.getProvider());
+            }
+        } finally {
+            Security.removeProvider(mockProvider.getName());
+        }
+    }
+
+    public void testSignature_getInstance_DelayedInitialization_KeyType() throws Exception {
+        Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
+            public void setup() {
+                put("Signature.FOO", MockSignatureSpi.SpecificKeyTypes.class.getName());
+                put("Signature.FOO SupportedKeyClasses", MockPrivateKey.class.getName());
+            }
+        };
+        Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+            public void setup() {
+                put("Signature.FOO", MockSignatureSpi.SpecificKeyTypes2.class.getName());
+                put("Signature.FOO SupportedKeyClasses", MockPrivateKey2.class.getName());
+            }
+        };
+        Provider mockProviderAll = new MockProvider("MockProviderAll") {
+            public void setup() {
+                put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        Security.addProvider(mockProviderSpecific);
+        Security.addProvider(mockProviderSpecific2);
+        Security.addProvider(mockProviderAll);
+
+        try {
+            {
+                Signature s = Signature.getInstance("FOO");
+                s.initSign(new MockPrivateKey());
+                assertEquals(mockProviderSpecific, s.getProvider());
+
+                try {
+                    s.initSign(new MockPrivateKey2());
+                    assertEquals(mockProviderSpecific2, s.getProvider());
+                    if (StandardNames.IS_RI) {
+                        fail("RI was broken before; fix tests now that it works!");
+                    }
+                } catch (InvalidKeyException e) {
+                    if (!StandardNames.IS_RI) {
+                        fail("Non-RI should select the right provider");
+                    }
+                }
+            }
+
+            {
+                Signature s = Signature.getInstance("FOO");
+                s.initSign(new PrivateKey() {
+                    @Override
+                    public String getAlgorithm() {
+                        throw new UnsupportedOperationException("not implemented");
+                    }
+
+                    @Override
+                    public String getFormat() {
+                        throw new UnsupportedOperationException("not implemented");
+                    }
+
+                    @Override
+                    public byte[] getEncoded() {
+                        throw new UnsupportedOperationException("not implemented");
+                    }
+                });
+                assertEquals(mockProviderAll, s.getProvider());
+            }
+
+            {
+                Signature s = Signature.getInstance("FOO");
+                assertEquals(mockProviderSpecific, s.getProvider());
+            }
+        } finally {
+            Security.removeProvider(mockProviderSpecific.getName());
+            Security.removeProvider(mockProviderSpecific2.getName());
+            Security.removeProvider(mockProviderAll.getName());
+        }
+    }
+
+    private static class MySignature extends Signature {
+        protected MySignature(String algorithm) {
+            super(algorithm);
+        }
+
+        @Override
+        protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected void engineUpdate(byte b) throws SignatureException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected byte[] engineSign() throws SignatureException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected void engineSetParameter(String param, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected Object engineGetParameter(String param) throws InvalidParameterException {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public void testSignature_getProvider_Subclass() throws Exception {
+        Provider mockProviderNonSpi = new MockProvider("MockProviderNonSpi") {
+            public void setup() {
+                put("Signature.FOO", MySignature.class.getName());
+            }
+        };
+
+        Security.addProvider(mockProviderNonSpi);
+
+        try {
+            Signature s = new MySignature("FOO");
+            assertNull(s.getProvider());
+        } finally {
+            Security.removeProvider(mockProviderNonSpi.getName());
+        }
+    }
 
     // 20 bytes for DSA
     private final byte[] DATA = new byte[20];
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index 21cc5da..f7a0a72 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -890,7 +890,13 @@
         Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
             public void setup() {
                 put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes.class.getName());
-                put("Cipher.FOO SupportedKeyClasses", this.getClass().getPackage().getName() + ".MockKey");
+                put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+            }
+        };
+        Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+            public void setup() {
+                put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes2.class.getName());
+                put("Cipher.FOO SupportedKeyClasses", MockKey2.class.getName());
             }
         };
         Provider mockProviderAll = new MockProvider("MockProviderAll") {
@@ -900,13 +906,27 @@
         };
 
         Security.addProvider(mockProviderSpecific);
+        Security.addProvider(mockProviderSpecific2);
         Security.addProvider(mockProviderAll);
 
         try {
             {
+                System.out.println(Arrays.deepToString(Security.getProviders("Cipher.FOO")));
                 Cipher c = Cipher.getInstance("FOO");
                 c.init(Cipher.ENCRYPT_MODE, new MockKey());
                 assertEquals(mockProviderSpecific, c.getProvider());
+
+                try {
+                    c.init(Cipher.ENCRYPT_MODE, new MockKey2());
+                    assertEquals(mockProviderSpecific2, c.getProvider());
+                    if (StandardNames.IS_RI) {
+                        fail("RI was broken before; fix tests now that it works!");
+                    }
+                } catch (InvalidKeyException e) {
+                    if (!StandardNames.IS_RI) {
+                        fail("Non-RI should select the right provider");
+                    }
+                }
             }
 
             {
@@ -936,10 +956,28 @@
             }
         } finally {
             Security.removeProvider(mockProviderSpecific.getName());
+            Security.removeProvider(mockProviderSpecific2.getName());
             Security.removeProvider(mockProviderAll.getName());
         }
     }
 
+    public void testCipher_getInstance_WrongType_Failure() throws Exception {
+        Provider mockProviderInvalid = new MockProvider("MockProviderInvalid") {
+            public void setup() {
+                put("Cipher.FOO", Object.class.getName());
+            }
+        };
+
+        Security.addProvider(mockProviderInvalid);
+        try {
+            Cipher.getInstance("FOO");
+            fail("Should not find any matching providers");
+        } catch (NoSuchAlgorithmException expected) {
+        } finally {
+            Security.removeProvider(mockProviderInvalid.getName());
+        }
+    }
+
     public void test_getInstance() throws Exception {
         final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
         PrintStream out = new PrintStream(errBuffer);
diff --git a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
index eb950cc..6742cf3 100644
--- a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
+++ b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
@@ -38,7 +38,17 @@
         @Override
         public void checkKeyType(Key key) throws InvalidKeyException {
             if (!(key instanceof MockKey)) {
-                throw new InvalidKeyException("Must be a mock key!");
+                throw new InvalidKeyException("Must be MockKey!");
+            }
+        }
+    }
+
+    public static class SpecificKeyTypes2 extends MockCipherSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            System.err.println("Checking key of type " + key.getClass().getName());
+            if (!(key instanceof MockKey2)) {
+                throw new InvalidKeyException("Must be MockKey2!");
             }
         }
     }
diff --git a/luni/src/test/java/libcore/javax/crypto/MockKey2.java b/luni/src/test/java/libcore/javax/crypto/MockKey2.java
new file mode 100644
index 0000000..44ac0cc
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockKey2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.crypto;
+
+import java.security.Key;
+
+/**
+ * A mock Key class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockKey2 implements Key {
+    @Override
+    public String getAlgorithm() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public String getFormat() {
+        return "MOCK";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java
index 99d127a..0642229 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java
@@ -22,6 +22,8 @@
 
 package org.apache.harmony.crypto.tests.javax.crypto;
 
+import org.apache.harmony.security.tests.support.SpiEngUtils;
+import org.apache.harmony.security.tests.support.TestKeyPair;
 import java.math.BigInteger;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -31,6 +33,7 @@
 import java.security.Provider;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.Security;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.DSAParameterSpec;
 import java.security.spec.RSAKeyGenParameterSpec;
@@ -40,12 +43,10 @@
 import javax.crypto.ShortBufferException;
 import javax.crypto.interfaces.DHPrivateKey;
 import javax.crypto.spec.DHParameterSpec;
-
-import org.apache.harmony.crypto.tests.support.MyKeyAgreementSpi;
-import org.apache.harmony.security.tests.support.SpiEngUtils;
-import org.apache.harmony.security.tests.support.TestKeyPair;
-
 import junit.framework.TestCase;
+import libcore.java.security.StandardNames;
+import libcore.javax.crypto.MockKey;
+import libcore.javax.crypto.MockKey2;
 
 
 /**
@@ -676,4 +677,127 @@
             //expected
         }
     }
+
+    private static abstract class MockProvider extends Provider {
+        public MockProvider(String name) {
+            super(name, 1.0, "Mock provider used for testing");
+            setup();
+        }
+
+        public abstract void setup();
+    }
+
+    public void testKeyAgreement_getInstance_SuppliedProviderNotRegistered_Success()
+            throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        {
+            KeyAgreement s = KeyAgreement.getInstance("FOO", mockProvider);
+            s.init(new MockKey());
+            assertEquals(mockProvider, s.getProvider());
+        }
+    }
+
+    public void testKeyAgreement_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
+            throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        Security.addProvider(mockProvider);
+        try {
+            {
+                Provider mockProvider2 = new MockProvider("MockProvider") {
+                    public void setup() {
+                        put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+                    }
+                };
+                KeyAgreement s = KeyAgreement.getInstance("FOO", mockProvider2);
+                assertEquals(mockProvider2, s.getProvider());
+            }
+        } finally {
+            Security.removeProvider(mockProvider.getName());
+        }
+    }
+
+    public void testKeyAgreement_getInstance_DelayedInitialization_KeyType() throws Exception {
+        Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.SpecificKeyTypes.class.getName());
+                put("KeyAgreement.FOO SupportedKeyClasses", MockKey.class.getName());
+            }
+        };
+        Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.SpecificKeyTypes2.class.getName());
+                put("KeyAgreement.FOO SupportedKeyClasses", MockKey2.class.getName());
+            }
+        };
+        Provider mockProviderAll = new MockProvider("MockProviderAll") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        Security.addProvider(mockProviderSpecific);
+        Security.addProvider(mockProviderSpecific2);
+        Security.addProvider(mockProviderAll);
+
+        try {
+            {
+                KeyAgreement s = KeyAgreement.getInstance("FOO");
+                s.init(new MockKey());
+                assertEquals(mockProviderSpecific, s.getProvider());
+
+                try {
+                    s.init(new MockKey2());
+                    assertEquals(mockProviderSpecific2, s.getProvider());
+                    if (StandardNames.IS_RI) {
+                        fail("RI was broken before; fix tests now that it works!");
+                    }
+                } catch (InvalidKeyException e) {
+                    if (!StandardNames.IS_RI) {
+                        fail("Non-RI should select the right provider");
+                    }
+                }
+            }
+
+            {
+                KeyAgreement s = KeyAgreement.getInstance("FOO");
+                s.init(new PrivateKey() {
+                    @Override
+                    public String getAlgorithm() {
+                        throw new UnsupportedOperationException("not implemented");
+                    }
+
+                    @Override
+                    public String getFormat() {
+                        throw new UnsupportedOperationException("not implemented");
+                    }
+
+                    @Override
+                    public byte[] getEncoded() {
+                        throw new UnsupportedOperationException("not implemented");
+                    }
+                });
+                assertEquals(mockProviderAll, s.getProvider());
+            }
+
+            {
+                KeyAgreement s = KeyAgreement.getInstance("FOO");
+                assertEquals(mockProviderSpecific, s.getProvider());
+            }
+        } finally {
+            Security.removeProvider(mockProviderSpecific.getName());
+            Security.removeProvider(mockProviderSpecific2.getName());
+            Security.removeProvider(mockProviderAll.getName());
+        }
+    }
+
 }
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockKeyAgreementSpi.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockKeyAgreementSpi.java
new file mode 100644
index 0000000..4b53a6b
--- /dev/null
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockKeyAgreementSpi.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.crypto.tests.javax.crypto;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import libcore.javax.crypto.MockKey;
+import libcore.javax.crypto.MockKey2;
+
+public class MockKeyAgreementSpi extends KeyAgreementSpi {
+    public static class SpecificKeyTypes extends MockKeyAgreementSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            if (!(key instanceof MockKey)) {
+                throw new InvalidKeyException("Must be MockKey!");
+            }
+        }
+    }
+
+    public static class SpecificKeyTypes2 extends MockKeyAgreementSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            if (!(key instanceof MockKey2)) {
+                throw new InvalidKeyException("Must be MockKey2!");
+            }
+        }
+    }
+
+    public static class AllKeyTypes extends MockKeyAgreementSpi {
+    }
+
+    public void checkKeyType(Key key) throws InvalidKeyException {
+    }
+
+    @Override
+    protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException,
+            IllegalStateException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected byte[] engineGenerateSecret() throws IllegalStateException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+            throws IllegalStateException, ShortBufferException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException,
+            NoSuchAlgorithmException, InvalidKeyException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+        checkKeyType(key);
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        checkKeyType(key);
+    }
+}