Merge from AOSP

Change-Id: I83f2ba38dca0fa86e9ff802e5df02311f6bc6e99
diff --git a/Android.mk b/Android.mk
index b969317..0532402 100644
--- a/Android.mk
+++ b/Android.mk
@@ -58,6 +58,7 @@
         dalvik \
         $(HOST_OUT)/bin/dalvikvm \
         $(HOST_OUT)/bin/dexopt \
+        $(HOST_OUT)/lib/libjavacore.so \
         cacerts-host \
         $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.dat \
         $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.idx \
diff --git a/Docs.mk b/Docs.mk
index 8b5e469..875a008 100644
--- a/Docs.mk
+++ b/Docs.mk
@@ -21,7 +21,6 @@
    dalvik/src/main/java/dalvik/annotation \
    dalvik/src/main/java/dalvik/bytecode \
    json/src/main/java \
-   junit/src/main/java \
    luni/src/main/java/java \
    luni/src/main/java/javax \
    luni/src/main/java/org/xml/sax \
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 7179fda..18fa870 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -88,16 +88,6 @@
 core-intermediates := ${intermediates}
 
 
-# Make core-junit
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-main-java-files-under,junit)
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core
-LOCAL_JAVACFLAGS := $(local_javac_flags)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := core-junit
-include $(BUILD_JAVA_LIBRARY)
-
 # Make the core-tests library.
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni support xml)
@@ -153,17 +143,6 @@
 
     include $(BUILD_HOST_JAVA_LIBRARY)
 
-    # Make core-junit
-    include $(CLEAR_VARS)
-    LOCAL_SRC_FILES := $(call all-main-java-files-under,junit)
-    LOCAL_NO_STANDARD_LIBRARIES := true
-    LOCAL_JAVA_LIBRARIES := core-hostdex
-    LOCAL_JAVACFLAGS := $(local_javac_flags)
-    LOCAL_MODULE_TAGS := optional
-    LOCAL_MODULE := core-junit-hostdex
-    LOCAL_BUILD_HOST_DEX := true
-    include $(BUILD_HOST_JAVA_LIBRARY)
-
     # Make the core-tests library.
     include $(CLEAR_VARS)
     LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni support xml)
diff --git a/NativeCode.mk b/NativeCode.mk
index 85b38e1..2a222b1 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -65,8 +65,9 @@
 core_c_includes := $(sort libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
 core_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
 core_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
-core_cflags := -fvisibility=hidden -fvisibility-inlines-hidden
+core_cflags := -fvisibility=hidden
 core_cflags += '-DGCC_HIDDEN=__attribute__((visibility("hidden")))'
+core_cppflags := -fvisibility-inlines-hidden
 
 
 #
@@ -75,8 +76,9 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_CFLAGS += -Wall -Wextra -Werror -Wno-unused-but-set-variable
+LOCAL_CFLAGS += -Wall -Wextra -Werror
 LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
 ifeq ($(TARGET_ARCH),arm)
 # Ignore "note: the mangling of 'va_list' has changed in GCC 4.4"
 LOCAL_CFLAGS += -Wno-psabi
@@ -85,7 +87,7 @@
 # Define the rules.
 LOCAL_SRC_FILES := $(core_src_files)
 LOCAL_C_INCLUDES := $(core_c_includes)
-LOCAL_SHARED_LIBRARIES := $(core_shared_libraries)
+LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz libnativehelper
 LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libjavacore
@@ -93,7 +95,7 @@
 LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
 LOCAL_SHARED_LIBRARIES += libstlport
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
 
 #
 # Build for the host.
@@ -105,9 +107,11 @@
     LOCAL_SRC_FILES := $(core_src_files)
     LOCAL_CFLAGS += $(core_cflags)
     LOCAL_C_INCLUDES := $(core_c_includes)
-    LOCAL_SHARED_LIBRARIES := $(core_shared_libraries)
-    LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
+    LOCAL_CPPFLAGS += $(core_cppflags)
+    LOCAL_LDLIBS += -ldl -lpthread
     LOCAL_MODULE_TAGS := optional
-    LOCAL_MODULE := libjavacore-host
-    include $(BUILD_HOST_STATIC_LIBRARY)
+    LOCAL_MODULE := libjavacore
+    LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz-host
+    LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
+    include $(BUILD_HOST_SHARED_LIBRARY)
 endif
diff --git a/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java b/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
index f253ee7..1209b2e 100644
--- a/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
+++ b/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
@@ -55,7 +55,7 @@
          */
         // BEGIN(libcore-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
         MAXIMUM_VALUE = 65535;
-        MAXIMUM_PACKED_VALUE = 511;
+        MAXIMUM_PACKED_VALUE = 255;
         // END(libcore-maximum-values)
     }
 
diff --git a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
index 58f0f7d7..f758d65 100644
--- a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
+++ b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
@@ -245,47 +245,87 @@
     int OP_SHL_INT_LIT8                 = 0x00e0;
     int OP_SHR_INT_LIT8                 = 0x00e1;
     int OP_USHR_INT_LIT8                = 0x00e2;
-    int OP_CONST_CLASS_JUMBO            = 0x00ff;
-    int OP_CHECK_CAST_JUMBO             = 0x01ff;
-    int OP_INSTANCE_OF_JUMBO            = 0x02ff;
-    int OP_NEW_INSTANCE_JUMBO           = 0x03ff;
-    int OP_NEW_ARRAY_JUMBO              = 0x04ff;
-    int OP_FILLED_NEW_ARRAY_JUMBO       = 0x05ff;
-    int OP_IGET_JUMBO                   = 0x06ff;
-    int OP_IGET_WIDE_JUMBO              = 0x07ff;
-    int OP_IGET_OBJECT_JUMBO            = 0x08ff;
-    int OP_IGET_BOOLEAN_JUMBO           = 0x09ff;
-    int OP_IGET_BYTE_JUMBO              = 0x0aff;
-    int OP_IGET_CHAR_JUMBO              = 0x0bff;
-    int OP_IGET_SHORT_JUMBO             = 0x0cff;
-    int OP_IPUT_JUMBO                   = 0x0dff;
-    int OP_IPUT_WIDE_JUMBO              = 0x0eff;
-    int OP_IPUT_OBJECT_JUMBO            = 0x0fff;
-    int OP_IPUT_BOOLEAN_JUMBO           = 0x10ff;
-    int OP_IPUT_BYTE_JUMBO              = 0x11ff;
-    int OP_IPUT_CHAR_JUMBO              = 0x12ff;
-    int OP_IPUT_SHORT_JUMBO             = 0x13ff;
-    int OP_SGET_JUMBO                   = 0x14ff;
-    int OP_SGET_WIDE_JUMBO              = 0x15ff;
-    int OP_SGET_OBJECT_JUMBO            = 0x16ff;
-    int OP_SGET_BOOLEAN_JUMBO           = 0x17ff;
-    int OP_SGET_BYTE_JUMBO              = 0x18ff;
-    int OP_SGET_CHAR_JUMBO              = 0x19ff;
-    int OP_SGET_SHORT_JUMBO             = 0x1aff;
-    int OP_SPUT_JUMBO                   = 0x1bff;
-    int OP_SPUT_WIDE_JUMBO              = 0x1cff;
-    int OP_SPUT_OBJECT_JUMBO            = 0x1dff;
-    int OP_SPUT_BOOLEAN_JUMBO           = 0x1eff;
-    int OP_SPUT_BYTE_JUMBO              = 0x1fff;
-    int OP_SPUT_CHAR_JUMBO              = 0x20ff;
-    int OP_SPUT_SHORT_JUMBO             = 0x21ff;
-    int OP_INVOKE_VIRTUAL_JUMBO         = 0x22ff;
-    int OP_INVOKE_SUPER_JUMBO           = 0x23ff;
-    int OP_INVOKE_DIRECT_JUMBO          = 0x24ff;
-    int OP_INVOKE_STATIC_JUMBO          = 0x25ff;
-    int OP_INVOKE_INTERFACE_JUMBO       = 0x26ff;
     // END(libcore-opcodes)
 
+    /** Never implemented; do not use. */
+    int OP_CONST_CLASS_JUMBO            = 0x00ff;
+    /** Never implemented; do not use. */
+    int OP_CHECK_CAST_JUMBO             = 0x01ff;
+    /** Never implemented; do not use. */
+    int OP_INSTANCE_OF_JUMBO            = 0x02ff;
+    /** Never implemented; do not use. */
+    int OP_NEW_INSTANCE_JUMBO           = 0x03ff;
+    /** Never implemented; do not use. */
+    int OP_NEW_ARRAY_JUMBO              = 0x04ff;
+    /** Never implemented; do not use. */
+    int OP_FILLED_NEW_ARRAY_JUMBO       = 0x05ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_JUMBO                   = 0x06ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_WIDE_JUMBO              = 0x07ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_OBJECT_JUMBO            = 0x08ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_BOOLEAN_JUMBO           = 0x09ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_BYTE_JUMBO              = 0x0aff;
+    /** Never implemented; do not use. */
+    int OP_IGET_CHAR_JUMBO              = 0x0bff;
+    /** Never implemented; do not use. */
+    int OP_IGET_SHORT_JUMBO             = 0x0cff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_JUMBO                   = 0x0dff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_WIDE_JUMBO              = 0x0eff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_OBJECT_JUMBO            = 0x0fff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_BOOLEAN_JUMBO           = 0x10ff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_BYTE_JUMBO              = 0x11ff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_CHAR_JUMBO              = 0x12ff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_SHORT_JUMBO             = 0x13ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_JUMBO                   = 0x14ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_WIDE_JUMBO              = 0x15ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_OBJECT_JUMBO            = 0x16ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_BOOLEAN_JUMBO           = 0x17ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_BYTE_JUMBO              = 0x18ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_CHAR_JUMBO              = 0x19ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_SHORT_JUMBO             = 0x1aff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_JUMBO                   = 0x1bff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_WIDE_JUMBO              = 0x1cff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_OBJECT_JUMBO            = 0x1dff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_BOOLEAN_JUMBO           = 0x1eff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_BYTE_JUMBO              = 0x1fff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_CHAR_JUMBO              = 0x20ff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_SHORT_JUMBO             = 0x21ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_VIRTUAL_JUMBO         = 0x22ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_SUPER_JUMBO           = 0x23ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_DIRECT_JUMBO          = 0x24ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_STATIC_JUMBO          = 0x25ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_INTERFACE_JUMBO       = 0x26ff;
+
     /*
      * The rest of these are either generated by dexopt for optimized
      * code, or inserted by the VM at runtime.  They are never generated
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index dc3e063..8db3985 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -20,6 +20,9 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Enumeration;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
 
 /**
  * Manipulates DEX files. The class is similar in principle to
@@ -90,6 +93,19 @@
      *  Enable optional features.
      */
     private DexFile(String sourceName, String outputName, int flags) throws IOException {
+        if (outputName != null) {
+            try {
+                String parent = new File(outputName).getParent();
+                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
+                    throw new IllegalArgumentException("Optimized data directory " + parent
+                            + " is not owned by the current user. Shared storage cannot protect"
+                            + " your application from code injection attacks.");
+                }
+            } catch (ErrnoException ignored) {
+                // assume we'll fail with a more contextual error later
+            }
+        }
+
         mCookie = openDexFile(sourceName, outputName, flags);
         mFileName = sourceName;
         guard.open("close");
diff --git a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
index 46e18ae..50e2fc2 100644
--- a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
+++ b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
@@ -61,7 +61,7 @@
             gMethods, NELEM(gMethods));
     if (result != 0) {
         /* print warning, but allow to continue */
-        LOGW("WARNING: NativeTestTarget not registered\n");
+        ALOGW("WARNING: NativeTestTarget not registered\n");
         env->ExceptionClear();
     }
     return 0;
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index 8a9952c..6fc48cd 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -3,6 +3,26 @@
  */
 [
 {
+  description: "libcore.java.io.OldFileTest#test_deleteOnExit fails on IRM05 mysid-user",
+  name: "libcore.java.io.OldFileTest#test_deleteOnExit",
+  bug: 5834665
+},
+{
+  description: "libcore.java.net.URLConnectionTest#testServerShutdownInput fails on ICL27 mysid-userdebug",
+  name: "libcore.java.net.URLConnectionTest#testServerShutdownInput",
+  bug: 5534202
+},
+{
+  description: "libcore.java.security.KeyPairGeneratorTest long test is too long",
+  name: "libcore.java.security.KeyPairGeneratorTest#test_getInstance",
+  bug: 5513723
+},
+{
+  description: "Support digest authentication in HttpURLConnection",
+  name: "libcore.net.http.ParsedHeadersTest#testParseChallengesWithManyParameters",
+  bug: 6156454
+},
+{
   description: "Without no security manager, we don't care if checkPermission's argument is null",
   name: "org.apache.harmony.security.tests.java.security.AccessController2Test#test_checkPermission_NullParameter",
   result: EXEC_FAILED
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 7915a10..ae650ea 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -200,11 +200,6 @@
   bug: 3057090
 },
 {
-  description: "finalize() called on objects whose constructor didn't complete normally",
-  name: "libcore.java.lang.SystemTest#testBackFromTheDead",
-  bug: 3342343
-},
-{
   description: "DecimalFormat is limited to 127 digits",
   name: "libcore.java.text.DecimalFormatTest#test_setMaximumIntegerDigits",
   bug: 2400429
@@ -312,16 +307,6 @@
   modes: [ "host" ]
 },
 {
-  description: "Fails in CTS but passes under run-core-tests",
-  result: EXEC_FAILED,
-  name: "libcore.java.io.OldFileTest#test_deleteOnExit"
-},
-{
-  description: "Fails in CTS but passes under run-core-tests",
-  result: EXEC_FAILED,
-  name: "tests.api.java.io.SerializationStressTest4#test_writeObject_Proxy"
-},
-{
   description: "Defining classes from byte[] not supported in Android",
   result: EXEC_FAILED,
   name: "libcore.java.lang.OldClassTest#test_getClasses_subtest0"
@@ -332,14 +317,6 @@
   name: "libcore.java.lang.OldClassTest#test_getProtectionDomain"
 },
 {
-  description: "Fails in CTS but passes under run-core-tests",
-  result: EXEC_FAILED,
-  names: [
-    "tests.api.java.net.MulticastSocketTest#test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface",
-    "tests.api.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_DateTimeConversion"
-  ]
-},
-{
   description: "Runtime.getRuntime().traceMethodCalls(true) doesn't return on the host, fails in CTS",
   bug: 3447964,
   name: "libcore.java.lang.OldRuntimeTest#test_traceMethodCalls"
@@ -351,14 +328,6 @@
   name: "tests.api.java.util.ResourceBundleTest#test_getBundleLjava_lang_StringLjava_util_LocaleLjava_lang_ClassLoader"
 },
 {
-  description: "Fails in CTS but passes under run-core-tests",
-  result: EXEC_FAILED,
-  names: [
-    "tests.api.java.nio.charset.CharsetProviderTest#testForName_InsufficientPrivilege",
-    "tests.api.java.nio.charset.CharsetProviderTest#testIsSupported_And_ForName_NormalProvider"
-  ]
-},
-{
   description: "Fails (probably) because no protection domain is set.",
   result: EXEC_FAILED,
   names: [
diff --git a/junit/MODULE_LICENSE_CPL b/junit/MODULE_LICENSE_CPL
deleted file mode 100644
index 541dbb5..0000000
--- a/junit/MODULE_LICENSE_CPL
+++ /dev/null
@@ -1 +0,0 @@
-http://www.opensource.org/licenses/cpl1.0.php
diff --git a/junit/src/main/java/junit/extensions/ActiveTestSuite.java b/junit/src/main/java/junit/extensions/ActiveTestSuite.java
deleted file mode 100644
index 8b62853..0000000
--- a/junit/src/main/java/junit/extensions/ActiveTestSuite.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package junit.extensions;
-
-import junit.framework.*;
-
-/**
- * A TestSuite for active Tests. It runs each
- * test in a separate thread and waits until all
- * threads have terminated.
- * -- Aarhus Radisson Scandinavian Center 11th floor
- */
-public class ActiveTestSuite extends TestSuite {
-    private volatile int fActiveTestDeathCount;
-
-    public ActiveTestSuite() {
-    }
-
-    public ActiveTestSuite(Class theClass) {
-        super(theClass);
-    }
-
-    public ActiveTestSuite(String name) {
-        super (name);
-    }
-
-    public ActiveTestSuite(Class theClass, String name) {
-        super(theClass, name);
-    }
-
-    public void run(TestResult result) {
-        fActiveTestDeathCount= 0;
-        super.run(result);
-        waitUntilFinished();
-    }
-
-    public void runTest(final Test test, final TestResult result) {
-        Thread t= new Thread() {
-            public void run() {
-                try {
-                    // inlined due to limitation in VA/Java
-                    //ActiveTestSuite.super.runTest(test, result);
-                    test.run(result);
-                } finally {
-                    ActiveTestSuite.this.runFinished(test);
-                }
-            }
-        };
-        t.start();
-    }
-
-    synchronized void waitUntilFinished() {
-        while (fActiveTestDeathCount < testCount()) {
-            try {
-                wait();
-            } catch (InterruptedException e) {
-                return; // ignore
-            }
-        }
-    }
-
-    synchronized public void runFinished(Test test) {
-        fActiveTestDeathCount++;
-        notifyAll();
-    }
-}
diff --git a/junit/src/main/java/junit/extensions/ExceptionTestCase.java b/junit/src/main/java/junit/extensions/ExceptionTestCase.java
deleted file mode 100644
index de64b5b..0000000
--- a/junit/src/main/java/junit/extensions/ExceptionTestCase.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package junit.extensions;
-
-import junit.framework.*;
-
-/**
- * A TestCase that expects an Exception of class fExpected to be thrown.
- * The other way to check that an expected exception is thrown is:
- * <pre>
- * try {
- *   shouldThrow();
- * }
- * catch (SpecialException e) {
- *   return;
- * }
- * fail("Expected SpecialException");
- * </pre>
- *
- * To use ExceptionTestCase, create a TestCase like:
- * <pre>
- * new ExceptionTestCase("testShouldThrow", SpecialException.class);
- * </pre>
- */
-public class ExceptionTestCase extends TestCase {
-    Class fExpected;
-
-    public ExceptionTestCase(String name, Class exception) {
-        super(name);
-        fExpected= exception;
-    }
-    /**
-     * Execute the test method expecting that an Exception of
-     * class fExpected or one of its subclasses will be thrown
-     */
-    protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        }
-        catch (Exception e) {
-            if (fExpected.isAssignableFrom(e.getClass()))
-                return;
-            else
-                throw e;
-        }
-        fail("Expected exception " + fExpected);
-    }
-}
diff --git a/junit/src/main/java/junit/extensions/RepeatedTest.java b/junit/src/main/java/junit/extensions/RepeatedTest.java
deleted file mode 100644
index 34f2541..0000000
--- a/junit/src/main/java/junit/extensions/RepeatedTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package junit.extensions;
-
-import junit.framework.*;
-
-/**
- * A Decorator that runs a test repeatedly.
- *
- */
-public class RepeatedTest extends  TestDecorator {
-    private int fTimesRepeat;
-
-    public RepeatedTest(Test test, int repeat) {
-        super(test);
-        if (repeat < 0)
-            throw new IllegalArgumentException("Repetition count must be > 0");
-        fTimesRepeat= repeat;
-    }
-    public int countTestCases() {
-        return super.countTestCases()*fTimesRepeat;
-    }
-    public void run(TestResult result) {
-        for (int i= 0; i < fTimesRepeat; i++) {
-            if (result.shouldStop())
-                break;
-            super.run(result);
-        }
-    }
-    public String toString() {
-        return super.toString()+"(repeated)";
-    }
-}
diff --git a/junit/src/main/java/junit/extensions/TestDecorator.java b/junit/src/main/java/junit/extensions/TestDecorator.java
deleted file mode 100644
index cfbd021..0000000
--- a/junit/src/main/java/junit/extensions/TestDecorator.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package junit.extensions;
-
-import junit.framework.*;
-
-/**
- * A Decorator for Tests. Use TestDecorator as the base class
- * for defining new test decorators. Test decorator subclasses
- * can be introduced to add behavior before or after a test
- * is run.
- *
- */
-public class TestDecorator extends Assert implements Test {
-    protected Test fTest;
-
-    public TestDecorator(Test test) {
-        fTest= test;
-    }
-    /**
-     * The basic run behavior.
-     */
-    public void basicRun(TestResult result) {
-        fTest.run(result);
-    }
-    public int countTestCases() {
-        return fTest.countTestCases();
-    }
-    public void run(TestResult result) {
-        basicRun(result);
-    }
-
-    public String toString() {
-        return fTest.toString();
-    }
-
-    public Test getTest() {
-        return fTest;
-    }
-}
diff --git a/junit/src/main/java/junit/extensions/TestSetup.java b/junit/src/main/java/junit/extensions/TestSetup.java
deleted file mode 100644
index 3651501..0000000
--- a/junit/src/main/java/junit/extensions/TestSetup.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package junit.extensions;
-
-import junit.framework.*;
-
-/**
- * A Decorator to set up and tear down additional fixture state.
- * Subclass TestSetup and insert it into your tests when you want
- * to set up additional state once before the tests are run.
- */
-public class TestSetup extends TestDecorator {
-
-    public TestSetup(Test test) {
-        super(test);
-    }
-    public void run(final TestResult result) {
-        Protectable p= new Protectable() {
-            public void protect() throws Exception {
-                setUp();
-                basicRun(result);
-                tearDown();
-            }
-        };
-        result.runProtected(this, p);
-    }
-    /**
-     * Sets up the fixture. Override to set up additional fixture
-     * state.
-     */
-    protected void setUp() throws Exception {
-    }
-    /**
-     * Tears down the fixture. Override to tear down the additional
-     * fixture state.
-     */
-    protected void tearDown() throws Exception {
-    }
-}
diff --git a/junit/src/main/java/junit/extensions/package.html b/junit/src/main/java/junit/extensions/package.html
deleted file mode 100644
index 6b4be72..0000000
--- a/junit/src/main/java/junit/extensions/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-{@hide} - Not needed for 1.0 SDK 
-</BODY>
-</HTML>
diff --git a/junit/src/main/java/junit/framework/Assert.java b/junit/src/main/java/junit/framework/Assert.java
deleted file mode 100644
index 289449a..0000000
--- a/junit/src/main/java/junit/framework/Assert.java
+++ /dev/null
@@ -1,291 +0,0 @@
-package junit.framework;
-
-/**
- * A set of assert methods.  Messages are only displayed when an assert fails.
- */
-
-public class Assert {
-    /**
-     * Protect constructor since it is a static only class
-     */
-    protected Assert() {
-    }
-
-    /**
-     * Asserts that a condition is true. If it isn't it throws
-     * an AssertionFailedError with the given message.
-     */
-    static public void assertTrue(String message, boolean condition) {
-        if (!condition)
-            fail(message);
-    }
-    /**
-     * Asserts that a condition is true. If it isn't it throws
-     * an AssertionFailedError.
-     */
-    static public void assertTrue(boolean condition) {
-        assertTrue(null, condition);
-    }
-    /**
-     * Asserts that a condition is false. If it isn't it throws
-     * an AssertionFailedError with the given message.
-     */
-    static public void assertFalse(String message, boolean condition) {
-        assertTrue(message, !condition);
-    }
-    /**
-     * Asserts that a condition is false. If it isn't it throws
-     * an AssertionFailedError.
-     */
-    static public void assertFalse(boolean condition) {
-        assertFalse(null, condition);
-    }
-    /**
-     * Fails a test with the given message.
-     */
-    static public void fail(String message) {
-        throw new AssertionFailedError(message);
-    }
-    /**
-     * Fails a test with no message.
-     */
-    static public void fail() {
-        fail(null);
-    }
-    /**
-     * Asserts that two objects are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, Object expected, Object actual) {
-        if (expected == null && actual == null)
-            return;
-        if (expected != null && expected.equals(actual))
-            return;
-        failNotEquals(message, expected, actual);
-    }
-    /**
-     * Asserts that two objects are equal. If they are not
-     * an AssertionFailedError is thrown.
-     */
-    static public void assertEquals(Object expected, Object actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two Strings are equal.
-     */
-    static public void assertEquals(String message, String expected, String actual) {
-        if (expected == null && actual == null)
-            return;
-        if (expected != null && expected.equals(actual))
-            return;
-        throw new ComparisonFailure(message, expected, actual);
-    }
-    /**
-     * Asserts that two Strings are equal.
-     */
-    static public void assertEquals(String expected, String actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two doubles are equal concerning a delta.  If they are not
-     * an AssertionFailedError is thrown with the given message.  If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(String message, double expected, double actual, double delta) {
-        // handle infinity specially since subtracting to infinite values gives NaN and the
-        // the following test fails
-        if (Double.isInfinite(expected)) {
-            if (!(expected == actual))
-                failNotEquals(message, new Double(expected), new Double(actual));
-        } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
-            failNotEquals(message, new Double(expected), new Double(actual));
-    }
-    /**
-     * Asserts that two doubles are equal concerning a delta. If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(double expected, double actual, double delta) {
-        assertEquals(null, expected, actual, delta);
-    }
-    /**
-     * Asserts that two floats are equal concerning a delta. If they are not
-     * an AssertionFailedError is thrown with the given message.  If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(String message, float expected, float actual, float delta) {
-         // handle infinity specially since subtracting to infinite values gives NaN and the
-        // the following test fails
-        if (Float.isInfinite(expected)) {
-            if (!(expected == actual))
-                failNotEquals(message, new Float(expected), new Float(actual));
-        } else if (!(Math.abs(expected-actual) <= delta))
-              failNotEquals(message, new Float(expected), new Float(actual));
-    }
-    /**
-     * Asserts that two floats are equal concerning a delta. If the expected
-     * value is infinity then the delta value is ignored.
-     */
-    static public void assertEquals(float expected, float actual, float delta) {
-        assertEquals(null, expected, actual, delta);
-    }
-    /**
-     * Asserts that two longs are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, long expected, long actual) {
-        assertEquals(message, new Long(expected), new Long(actual));
-    }
-    /**
-     * Asserts that two longs are equal.
-     */
-    static public void assertEquals(long expected, long actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two booleans are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, boolean expected, boolean actual) {
-            assertEquals(message, new Boolean(expected), new Boolean(actual));
-      }
-    /**
-     * Asserts that two booleans are equal.
-      */
-    static public void assertEquals(boolean expected, boolean actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two bytes are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, byte expected, byte actual) {
-        assertEquals(message, new Byte(expected), new Byte(actual));
-    }
-    /**
-        * Asserts that two bytes are equal.
-     */
-    static public void assertEquals(byte expected, byte actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two chars are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, char expected, char actual) {
-            assertEquals(message, new Character(expected), new Character(actual));
-      }
-    /**
-     * Asserts that two chars are equal.
-     */
-      static public void assertEquals(char expected, char actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two shorts are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertEquals(String message, short expected, short actual) {
-            assertEquals(message, new Short(expected), new Short(actual));
-    }
-      /**
-     * Asserts that two shorts are equal.
-     */
-    static public void assertEquals(short expected, short actual) {
-        assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that two ints are equal. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-      static public void assertEquals(String message, int expected, int actual) {
-        assertEquals(message, new Integer(expected), new Integer(actual));
-      }
-      /**
-        * Asserts that two ints are equal.
-     */
-      static public void assertEquals(int expected, int actual) {
-          assertEquals(null, expected, actual);
-    }
-    /**
-     * Asserts that an object isn't null.
-     */
-    static public void assertNotNull(Object object) {
-        assertNotNull(null, object);
-    }
-    /**
-     * Asserts that an object isn't null. If it is
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertNotNull(String message, Object object) {
-        assertTrue(message, object != null);
-    }
-    /**
-     * Asserts that an object is null.
-     */
-    static public void assertNull(Object object) {
-        assertNull(null, object);
-    }
-    /**
-     * Asserts that an object is null.  If it is not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertNull(String message, Object object) {
-        assertTrue(message, object == null);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * an AssertionFailedError is thrown with the given message.
-     */
-    static public void assertSame(String message, Object expected, Object actual) {
-        if (expected == actual)
-            return;
-        failNotSame(message, expected, actual);
-    }
-    /**
-     * Asserts that two objects refer to the same object. If they are not
-     * the same an AssertionFailedError is thrown.
-     */
-    static public void assertSame(Object expected, Object actual) {
-        assertSame(null, expected, actual);
-    }
-     /**
-      * Asserts that two objects do not refer to the same object. If they are
-      * an AssertionFailedError is thrown with the given message.
-      */
-    static public void assertNotSame(String message, Object expected, Object actual) {
-        if (expected == actual)
-            failSame(message);
-    }
-    /**
-     * Asserts that two objects do not refer to the same object. If they are
-     * the same an AssertionFailedError is thrown.
-     */
-    static public void assertNotSame(Object expected, Object actual) {
-        assertNotSame(null, expected, actual);
-    }
-
-    static private void failSame(String message) {
-        String formatted= "";
-         if (message != null)
-             formatted= message+" ";
-         fail(formatted+"expected not same");
-    }
-
-    static private void failNotSame(String message, Object expected, Object actual) {
-        String formatted= "";
-        if (message != null)
-            formatted= message+" ";
-        fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
-    }
-
-    static private void failNotEquals(String message, Object expected, Object actual) {
-        fail(format(message, expected, actual));
-    }
-
-    static String format(String message, Object expected, Object actual) {
-        String formatted= "";
-        if (message != null)
-            formatted= message+" ";
-        return formatted+"expected:<"+expected+"> but was:<"+actual+">";
-    }
-}
diff --git a/junit/src/main/java/junit/framework/AssertionFailedError.java b/junit/src/main/java/junit/framework/AssertionFailedError.java
deleted file mode 100644
index e9cb3a3..0000000
--- a/junit/src/main/java/junit/framework/AssertionFailedError.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package junit.framework;
-
-/**
- * Thrown when an assertion failed.
- */
-public class AssertionFailedError extends Error {
-
-    public AssertionFailedError () {
-    }
-    public AssertionFailedError (String message) {
-        super (message);
-    }
-}
diff --git a/junit/src/main/java/junit/framework/ComparisonFailure.java b/junit/src/main/java/junit/framework/ComparisonFailure.java
deleted file mode 100644
index ccd476b..0000000
--- a/junit/src/main/java/junit/framework/ComparisonFailure.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package junit.framework;
-
-/**
- * Thrown when an assert equals for Strings failed.
- *
- * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
- */
-public class ComparisonFailure extends AssertionFailedError {
-    private String fExpected;
-    private String fActual;
-
-    /**
-     * Constructs a comparison failure.
-     * @param message the identifying message or null
-     * @param expected the expected string value
-     * @param actual the actual string value
-     */
-    public ComparisonFailure (String message, String expected, String actual) {
-        super (message);
-        fExpected= expected;
-        fActual= actual;
-    }
-
-    /**
-     * Returns "..." in place of common prefix and "..." in
-     * place of common suffix between expected and actual.
-     *
-     * @see java.lang.Throwable#getMessage()
-     */
-    public String getMessage() {
-        if (fExpected == null || fActual == null)
-            return Assert.format(super.getMessage(), fExpected, fActual);
-
-        int end= Math.min(fExpected.length(), fActual.length());
-
-        int i= 0;
-        for (; i < end; i++) {
-            if (fExpected.charAt(i) != fActual.charAt(i))
-                break;
-        }
-        int j= fExpected.length()-1;
-        int k= fActual.length()-1;
-        for (; k >= i && j >= i; k--,j--) {
-            if (fExpected.charAt(j) != fActual.charAt(k))
-                break;
-        }
-        String actual, expected;
-
-        // equal strings
-        if (j < i && k < i) {
-            expected= fExpected;
-            actual= fActual;
-        } else {
-            expected= fExpected.substring(i, j+1);
-            actual= fActual.substring(i, k+1);
-            if (i <= end && i > 0) {
-                expected= "..."+expected;
-                actual= "..."+actual;
-            }
-
-            if (j < fExpected.length()-1)
-                expected= expected+"...";
-            if (k < fActual.length()-1)
-                actual= actual+"...";
-        }
-        return Assert.format(super.getMessage(), expected, actual);
-    }
-}
diff --git a/junit/src/main/java/junit/framework/Protectable.java b/junit/src/main/java/junit/framework/Protectable.java
deleted file mode 100644
index 9a63c96..0000000
--- a/junit/src/main/java/junit/framework/Protectable.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package junit.framework;
-
-/**
- * A <em>Protectable</em> can be run and can throw a Throwable.
- *
- * @see TestResult
- */
-public interface Protectable {
-
-    /**
-     * Run the the following method protected.
-     */
-    public abstract void protect() throws Throwable;
-}
diff --git a/junit/src/main/java/junit/framework/Test.java b/junit/src/main/java/junit/framework/Test.java
deleted file mode 100644
index 94f25cf..0000000
--- a/junit/src/main/java/junit/framework/Test.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package junit.framework;
-
-/**
- * A <em>Test</em> can be run and collect its results.
- *
- * @see TestResult
- */
-public interface Test {
-    /**
-     * Counts the number of test cases that will be run by this test.
-     */
-    public abstract int countTestCases();
-    /**
-     * Runs a test and collects its result in a TestResult instance.
-     */
-    public abstract void run(TestResult result);
-}
diff --git a/junit/src/main/java/junit/framework/TestCase.java b/junit/src/main/java/junit/framework/TestCase.java
deleted file mode 100644
index f29e8d2..0000000
--- a/junit/src/main/java/junit/framework/TestCase.java
+++ /dev/null
@@ -1,197 +0,0 @@
-package junit.framework;
-
-import java.lang.reflect.*;
-
-/**
- * A test case defines the fixture to run multiple tests. To define a test case<br>
- * 1) implement a subclass of TestCase<br>
- * 2) define instance variables that store the state of the fixture<br>
- * 3) initialize the fixture state by overriding <code>setUp</code><br>
- * 4) clean-up after a test by overriding <code>tearDown</code>.<br>
- * Each test runs in its own fixture so there
- * can be no side effects among test runs.
- * Here is an example:
- * <pre>
- * public class MathTest extends TestCase {
- *     protected double fValue1;
- *     protected double fValue2;
- *
- *    protected void setUp() {
- *         fValue1= 2.0;
- *         fValue2= 3.0;
- *     }
- * }
- * </pre>
- *
- * For each test implement a method which interacts
- * with the fixture. Verify the expected results with assertions specified
- * by calling <code>assertTrue</code> with a boolean.
- * <pre>
- *    public void testAdd() {
- *        double result= fValue1 + fValue2;
- *        assertTrue(result == 5.0);
- *    }
- * </pre>
- * Once the methods are defined you can run them. The framework supports
- * both a static type safe and more dynamic way to run a test.
- * In the static way you override the runTest method and define the method to
- * be invoked. A convenient way to do so is with an anonymous inner class.
- * <pre>
- * TestCase test= new MathTest("add") {
- *        public void runTest() {
- *            testAdd();
- *        }
- * };
- * test.run();
- * </pre>
- * The dynamic way uses reflection to implement <code>runTest</code>. It dynamically finds
- * and invokes a method.
- * In this case the name of the test case has to correspond to the test method
- * to be run.
- * <pre>
- * TestCase= new MathTest("testAdd");
- * test.run();
- * </pre>
- * The tests to be run can be collected into a TestSuite. JUnit provides
- * different <i>test runners</i> which can run a test suite and collect the results.
- * A test runner either expects a static method <code>suite</code> as the entry
- * point to get a test to run or it will extract the suite automatically.
- * <pre>
- * public static Test suite() {
- *      suite.addTest(new MathTest("testAdd"));
- *      suite.addTest(new MathTest("testDivideByZero"));
- *      return suite;
- *  }
- * </pre>
- * @see TestResult
- * @see TestSuite
- */
-
-public abstract class TestCase extends Assert implements Test {
-    /**
-     * the name of the test case
-     */
-    private String fName;
-
-    /**
-     * No-arg constructor to enable serialization. This method
-     * is not intended to be used by mere mortals without calling setName().
-     */
-    public TestCase() {
-        fName= null;
-    }
-    /**
-     * Constructs a test case with the given name.
-     */
-    public TestCase(String name) {
-        fName= name;
-    }
-    /**
-     * Counts the number of test cases executed by run(TestResult result).
-     */
-    public int countTestCases() {
-        return 1;
-    }
-    /**
-     * Creates a default TestResult object
-     *
-     * @see TestResult
-     */
-    protected TestResult createResult() {
-        return new TestResult();
-    }
-    /**
-     * A convenience method to run this test, collecting the results with a
-     * default TestResult object.
-     *
-     * @see TestResult
-     */
-    public TestResult run() {
-        TestResult result= createResult();
-        run(result);
-        return result;
-    }
-    /**
-     * Runs the test case and collects the results in TestResult.
-     */
-    public void run(TestResult result) {
-        result.run(this);
-    }
-    /**
-     * Runs the bare test sequence.
-     * @exception Throwable if any exception is thrown
-     */
-    public void runBare() throws Throwable {
-        setUp();
-        try {
-            runTest();
-        }
-        finally {
-            tearDown();
-        }
-    }
-    /**
-     * Override to run the test and assert its state.
-     * @exception Throwable if any exception is thrown
-     */
-    protected void runTest() throws Throwable {
-        assertNotNull(fName);
-        Method runMethod= null;
-        try {
-            // use getMethod to get all public inherited
-            // methods. getDeclaredMethods returns all
-            // methods of this class but excludes the
-            // inherited ones.
-            runMethod= getClass().getMethod(fName, (Class[]) null);
-        } catch (NoSuchMethodException e) {
-            fail("Method \""+fName+"\" not found");
-        }
-        if (!Modifier.isPublic(runMethod.getModifiers())) {
-            fail("Method \""+fName+"\" should be public");
-        }
-
-        try {
-            runMethod.invoke(this, (Object[]) null);
-        }
-        catch (InvocationTargetException e) {
-            e.fillInStackTrace();
-            throw e.getTargetException();
-        }
-        catch (IllegalAccessException e) {
-            e.fillInStackTrace();
-            throw e;
-        }
-    }
-    /**
-     * Sets up the fixture, for example, open a network connection.
-     * This method is called before a test is executed.
-     */
-    protected void setUp() throws Exception {
-    }
-    /**
-     * Tears down the fixture, for example, close a network connection.
-     * This method is called after a test is executed.
-     */
-    protected void tearDown() throws Exception {
-    }
-    /**
-     * Returns a string representation of the test case
-     */
-    public String toString() {
-        return getName() + "(" + getClass().getName() + ")";
-    }
-    /**
-     * Gets the name of a TestCase
-     * @return returns a String
-     */
-    public String getName() {
-        return fName;
-    }
-    /**
-     * Sets the name of a TestCase
-     * @param name The name to set
-     */
-    public void setName(String name) {
-        fName= name;
-    }
-}
diff --git a/junit/src/main/java/junit/framework/TestFailure.java b/junit/src/main/java/junit/framework/TestFailure.java
deleted file mode 100644
index 45614a3..0000000
--- a/junit/src/main/java/junit/framework/TestFailure.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package junit.framework;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-
-/**
- * A <code>TestFailure</code> collects a failed test together with
- * the caught exception.
- * @see TestResult
- */
-public class TestFailure extends Object {
-    protected Test fFailedTest;
-    protected Throwable fThrownException;
-
-
-    /**
-     * Constructs a TestFailure with the given test and exception.
-     */
-    public TestFailure(Test failedTest, Throwable thrownException) {
-        fFailedTest= failedTest;
-        fThrownException= thrownException;
-    }
-    /**
-     * Gets the failed test.
-     */
-    public Test failedTest() {
-        return fFailedTest;
-    }
-    /**
-     * Gets the thrown exception.
-     */
-    public Throwable thrownException() {
-        return fThrownException;
-    }
-    /**
-     * Returns a short description of the failure.
-     */
-    public String toString() {
-        StringBuffer buffer= new StringBuffer();
-        buffer.append(fFailedTest+": "+fThrownException.getMessage());
-        return buffer.toString();
-    }
-    public String trace() {
-        StringWriter stringWriter= new StringWriter();
-        PrintWriter writer= new PrintWriter(stringWriter);
-        thrownException().printStackTrace(writer);
-        StringBuffer buffer= stringWriter.getBuffer();
-        return buffer.toString();
-    }
-    public String exceptionMessage() {
-        return thrownException().getMessage();
-    }
-    public boolean isFailure() {
-        return thrownException() instanceof AssertionFailedError;
-    }
-}
diff --git a/junit/src/main/java/junit/framework/TestListener.java b/junit/src/main/java/junit/framework/TestListener.java
deleted file mode 100644
index 41c6ccf..0000000
--- a/junit/src/main/java/junit/framework/TestListener.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package junit.framework;
-
-/**
- * A Listener for test progress
- */
-public interface TestListener {
-    /**
-      * An error occurred.
-      */
-    public void addError(Test test, Throwable t);
-    /**
-      * A failure occurred.
-      */
-     public void addFailure(Test test, AssertionFailedError t);
-    /**
-     * A test ended.
-     */
-     public void endTest(Test test);
-    /**
-     * A test started.
-     */
-    public void startTest(Test test);
-}
diff --git a/junit/src/main/java/junit/framework/TestResult.java b/junit/src/main/java/junit/framework/TestResult.java
deleted file mode 100644
index bcca1de..0000000
--- a/junit/src/main/java/junit/framework/TestResult.java
+++ /dev/null
@@ -1,166 +0,0 @@
-package junit.framework;
-
-import java.util.Vector;
-import java.util.Enumeration;
-
-/**
- * A <code>TestResult</code> collects the results of executing
- * a test case. It is an instance of the Collecting Parameter pattern.
- * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
- * A failure is anticipated and checked for with assertions. Errors are
- * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
- *
- * @see Test
- */
-public class TestResult extends Object {
-    protected Vector fFailures;
-    protected Vector fErrors;
-    protected Vector fListeners;
-    protected int fRunTests;
-    private boolean fStop;
-
-    public TestResult() {
-        fFailures= new Vector();
-        fErrors= new Vector();
-        fListeners= new Vector();
-        fRunTests= 0;
-        fStop= false;
-    }
-    /**
-     * Adds an error to the list of errors. The passed in exception
-     * caused the error.
-     */
-    public synchronized void addError(Test test, Throwable t) {
-        fErrors.addElement(new TestFailure(test, t));
-        for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
-            ((TestListener)e.nextElement()).addError(test, t);
-        }
-    }
-    /**
-     * Adds a failure to the list of failures. The passed in exception
-     * caused the failure.
-     */
-    public synchronized void addFailure(Test test, AssertionFailedError t) {
-        fFailures.addElement(new TestFailure(test, t));
-        for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
-            ((TestListener)e.nextElement()).addFailure(test, t);
-        }
-    }
-    /**
-     * Registers a TestListener
-     */
-    public synchronized void addListener(TestListener listener) {
-        fListeners.addElement(listener);
-    }
-    /**
-     * Unregisters a TestListener
-     */
-    public synchronized void removeListener(TestListener listener) {
-        fListeners.removeElement(listener);
-    }
-    /**
-     * Returns a copy of the listeners.
-     */
-    private synchronized Vector cloneListeners() {
-        return (Vector)fListeners.clone();
-    }
-    /**
-     * Informs the result that a test was completed.
-     */
-    public void endTest(Test test) {
-        for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
-            ((TestListener)e.nextElement()).endTest(test);
-        }
-    }
-    /**
-     * Gets the number of detected errors.
-     */
-    public synchronized int errorCount() {
-        return fErrors.size();
-    }
-    /**
-     * Returns an Enumeration for the errors
-     */
-    public synchronized Enumeration errors() {
-        return fErrors.elements();
-    }
-    /**
-     * Gets the number of detected failures.
-     */
-    public synchronized int failureCount() {
-        return fFailures.size();
-    }
-    /**
-     * Returns an Enumeration for the failures
-     */
-    public synchronized Enumeration failures() {
-        return fFailures.elements();
-    }
-    /**
-     * Runs a TestCase.
-     */
-    protected void run(final TestCase test) {
-        startTest(test);
-        Protectable p= new Protectable() {
-            public void protect() throws Throwable {
-                test.runBare();
-            }
-        };
-        runProtected(test, p);
-
-        endTest(test);
-    }
-    /**
-     * Gets the number of run tests.
-     */
-    public synchronized int runCount() {
-        return fRunTests;
-    }
-    /**
-     * Runs a TestCase.
-     */
-    public void runProtected(final Test test, Protectable p) {
-        try {
-            p.protect();
-        }
-        catch (AssertionFailedError e) {
-            addFailure(test, e);
-        }
-        catch (ThreadDeath e) { // don't catch ThreadDeath by accident
-            throw e;
-        }
-        catch (Throwable e) {
-            addError(test, e);
-        }
-    }
-    /**
-     * Checks whether the test run should stop
-     */
-    public synchronized boolean shouldStop() {
-        return fStop;
-    }
-    /**
-     * Informs the result that a test will be started.
-     */
-    public void startTest(Test test) {
-        final int count= test.countTestCases();
-        synchronized(this) {
-            fRunTests+= count;
-        }
-        for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
-            ((TestListener)e.nextElement()).startTest(test);
-        }
-    }
-    /**
-     * Marks that the test run should stop.
-     */
-    public synchronized void stop() {
-        fStop= true;
-    }
-    /**
-     * Returns whether the entire test was successful or not.
-     */
-    public synchronized boolean wasSuccessful() {
-        return failureCount() == 0 && errorCount() == 0;
-    }
-}
diff --git a/junit/src/main/java/junit/framework/TestSuite.java b/junit/src/main/java/junit/framework/TestSuite.java
deleted file mode 100644
index d2df2b7..0000000
--- a/junit/src/main/java/junit/framework/TestSuite.java
+++ /dev/null
@@ -1,267 +0,0 @@
-package junit.framework;
-
-import java.util.Vector;
-import java.util.Enumeration;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.reflect.*;
-import java.lang.reflect.Constructor;
-
-/**
- * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
- * It runs a collection of test cases. Here is an example using
- * the dynamic test definition.
- * <pre>
- * TestSuite suite= new TestSuite();
- * suite.addTest(new MathTest("testAdd"));
- * suite.addTest(new MathTest("testDivideByZero"));
- * </pre>
- * Alternatively, a TestSuite can extract the tests to be run automatically.
- * To do so you pass the class of your TestCase class to the
- * TestSuite constructor.
- * <pre>
- * TestSuite suite= new TestSuite(MathTest.class);
- * </pre>
- * This constructor creates a suite with all the methods
- * starting with "test" that take no arguments.
- *
- * @see Test
- */
-public class TestSuite implements Test {
-
-    private Vector fTests= new Vector(10);
-    private String fName;
-
-    /**
-     * Constructs an empty TestSuite.
-     */
-    public TestSuite() {
-    }
-
-    /**
-     * Constructs a TestSuite from the given class with the given name.
-     * @see TestSuite#TestSuite(Class)
-     */
-    public TestSuite(Class theClass, String name) {
-        this(theClass);
-        setName(name);
-    }
-
-    /**
-     * Constructs a TestSuite from the given class. Adds all the methods
-     * starting with "test" as test cases to the suite.
-     * Parts of this method was written at 2337 meters in the Huffihutte,
-     * Kanton Uri
-     */
-     public TestSuite(final Class theClass) {
-        fName= theClass.getName();
-        try {
-            getTestConstructor(theClass); // Avoid generating multiple error messages
-        } catch (NoSuchMethodException e) {
-            addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
-            return;
-        }
-
-        if (!Modifier.isPublic(theClass.getModifiers())) {
-            addTest(warning("Class "+theClass.getName()+" is not public"));
-            return;
-        }
-
-        Class superClass= theClass;
-        Vector names= new Vector();
-        while (Test.class.isAssignableFrom(superClass)) {
-            Method[] methods= superClass.getDeclaredMethods();
-            for (int i= 0; i < methods.length; i++) {
-                addTestMethod(methods[i], names, theClass);
-            }
-            superClass= superClass.getSuperclass();
-        }
-        if (fTests.size() == 0)
-            addTest(warning("No tests found in "+theClass.getName()));
-    }
-
-       /**
-     * Constructs an empty TestSuite.
-     */
-    public TestSuite(String name) {
-        setName(name);
-    }
-
-    /**
-     * Adds a test to the suite.
-     */
-    public void addTest(Test test) {
-        fTests.addElement(test);
-    }
-
-    /**
-     * Adds the tests from the given class to the suite
-     */
-    public void addTestSuite(Class testClass) {
-        addTest(new TestSuite(testClass));
-    }
-
-    private void addTestMethod(Method m, Vector names, Class theClass) {
-        String name= m.getName();
-        if (names.contains(name))
-            return;
-        if (! isPublicTestMethod(m)) {
-            if (isTestMethod(m))
-                addTest(warning("Test method isn't public: "+m.getName()));
-            return;
-        }
-        names.addElement(name);
-        addTest(createTest(theClass, name));
-    }
-
-    /**
-     * ...as the moon sets over the early morning Merlin, Oregon
-     * mountains, our intrepid adventurers type...
-     */
-    static public Test createTest(Class theClass, String name) {
-        Constructor constructor;
-        try {
-            constructor= getTestConstructor(theClass);
-        } catch (NoSuchMethodException e) {
-            return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
-        }
-        Object test;
-        try {
-            if (constructor.getParameterTypes().length == 0) {
-                test= constructor.newInstance(new Object[0]);
-                if (test instanceof TestCase)
-                    ((TestCase) test).setName(name);
-            } else {
-                test= constructor.newInstance(new Object[]{name});
-            }
-        } catch (InstantiationException e) {
-            return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
-        } catch (InvocationTargetException e) {
-            return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
-        } catch (IllegalAccessException e) {
-            return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
-        }
-        return (Test) test;
-    }
-
-    /**
-     * Converts the stack trace into a string
-     */
-    private static String exceptionToString(Throwable t) {
-        StringWriter stringWriter= new StringWriter();
-        PrintWriter writer= new PrintWriter(stringWriter);
-        t.printStackTrace(writer);
-        return stringWriter.toString();
-
-    }
-
-    /**
-     * Counts the number of test cases that will be run by this test.
-     */
-    public int countTestCases() {
-        int count= 0;
-        for (Enumeration e= tests(); e.hasMoreElements(); ) {
-            Test test= (Test)e.nextElement();
-            count= count + test.countTestCases();
-        }
-        return count;
-    }
-
-    /**
-     * Gets a constructor which takes a single String as
-     * its argument or a no arg constructor.
-     */
-    public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
-        Class[] args= { String.class };
-        try {
-            return theClass.getConstructor(args);
-        } catch (NoSuchMethodException e) {
-            // fall through
-        }
-        return theClass.getConstructor(new Class[0]);
-    }
-
-    private boolean isPublicTestMethod(Method m) {
-        return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
-     }
-
-    private boolean isTestMethod(Method m) {
-        String name= m.getName();
-        Class[] parameters= m.getParameterTypes();
-        Class returnType= m.getReturnType();
-        return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
-     }
-
-    /**
-     * Runs the tests and collects their result in a TestResult.
-     */
-    public void run(TestResult result) {
-        for (Enumeration e= tests(); e.hasMoreElements(); ) {
-              if (result.shouldStop() )
-                  break;
-            Test test= (Test)e.nextElement();
-            runTest(test, result);
-        }
-    }
-
-    public void runTest(Test test, TestResult result) {
-        test.run(result);
-    }
-
-    /**
-     * Returns the test at the given index
-     */
-    public Test testAt(int index) {
-        return (Test)fTests.elementAt(index);
-    }
-
-    /**
-     * Returns the number of tests in this suite
-     */
-    public int testCount() {
-        return fTests.size();
-    }
-
-    /**
-     * Returns the tests as an enumeration
-     */
-    public Enumeration tests() {
-        return fTests.elements();
-    }
-
-    /**
-     */
-    public String toString() {
-        if (getName() != null)
-            return getName();
-        return super.toString();
-     }
-
-    /**
-     * Sets the name of the suite.
-     * @param name The name to set
-     */
-    public void setName(String name) {
-        fName= name;
-    }
-
-    /**
-     * Returns the name of the suite. Not all
-     * test suites have a name and this method
-     * can return null.
-     */
-    public String getName() {
-        return fName;
-    }
-
-    /**
-     * Returns a test which will fail and log a warning message.
-     */
-    private static Test warning(final String message) {
-        return new TestCase("warning") {
-            protected void runTest() {
-                fail(message);
-            }
-        };
-    }
-}
diff --git a/junit/src/main/java/junit/framework/package.html b/junit/src/main/java/junit/framework/package.html
deleted file mode 100644
index 770d470..0000000
--- a/junit/src/main/java/junit/framework/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-The junit test framework.
-</BODY>
-</HTML>
diff --git a/luni/src/main/files/cacerts/7672ac4b.0 b/luni/src/main/files/cacerts/7672ac4b.0
new file mode 100644
index 0000000..addaae4
--- /dev/null
+++ b/luni/src/main/files/cacerts/7672ac4b.0
@@ -0,0 +1,123 @@
+-----BEGIN CERTIFICATE-----
+MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET
+MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk
+BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4
+Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl
+cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0
+aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY
+F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N
+8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe
+rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K
+/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu
+7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC
+28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6
+lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E
+nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB
+0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09
+5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj
+WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN
+jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
+KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s
+ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM
+OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q
+619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn
+2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj
+o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v
+nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG
+5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq
+pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb
+dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0
+BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FR, O=Certinomis, OU=0002 433998903, CN=Certinomis - Autorit\xC3\xA9 Racine
+        Validity
+            Not Before: Sep 17 08:28:59 2008 GMT
+            Not After : Sep 17 08:28:59 2028 GMT
+        Subject: C=FR, O=Certinomis, OU=0002 433998903, CN=Certinomis - Autorit\xC3\xA9 Racine
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (4096 bit)
+                Modulus (4096 bit):
+                    00:9d:85:9f:86:d3:e3:af:c7:b2:6b:6e:33:e0:9e:
+                    b7:42:34:55:9d:f9:81:be:63:d8:23:76:0e:97:54:
+                    cd:99:4c:1a:f1:39:c7:88:d8:17:50:0c:9e:61:da:
+                    c0:4e:55:de:e7:5a:b8:7a:4e:77:87:0d:e5:b8:eb:
+                    fa:9e:5e:7b:1e:c4:cf:28:74:c7:93:f5:14:c6:22:
+                    28:04:f9:91:c3:ab:27:73:6a:0e:2e:4d:f3:2e:28:
+                    1f:70:df:55:2f:4e:ed:c7:71:6f:09:72:2e:ed:d5:
+                    32:97:d0:f1:58:77:d1:60:bc:4e:5e:db:9a:84:f6:
+                    47:61:45:2b:f6:50:a6:7f:6a:71:27:48:84:35:9e:
+                    ac:fe:69:a9:9e:7a:5e:35:25:fa:b4:a7:49:35:77:
+                    96:a7:36:5b:e1:cd:df:23:70:d8:5d:4c:a5:08:83:
+                    f1:a6:24:38:13:a8:ec:2f:a8:a1:67:c7:a6:2d:86:
+                    47:ee:8a:fc:ec:9b:0e:74:f4:2b:49:02:7b:90:75:
+                    8c:fc:99:39:01:39:d6:4a:89:e5:9e:76:ab:3e:96:
+                    28:38:26:8b:dd:8d:8c:c0:f6:01:1e:6f:a5:31:12:
+                    38:7d:95:c2:71:ee:ed:74:ae:e4:36:a2:43:75:d5:
+                    f1:00:9b:e2:e4:d7:cc:42:03:4b:78:7a:e5:7d:bb:
+                    b8:ae:2e:20:93:d3:e4:61:df:71:e1:76:67:97:3f:
+                    b6:df:6a:73:5a:64:22:e5:42:db:cf:81:03:93:d8:
+                    f4:e3:10:e0:72:f6:00:70:ac:f0:c1:7a:0f:05:7f:
+                    cf:34:69:45:b5:93:e4:19:db:52:16:23:05:89:0e:
+                    8d:48:e4:25:6f:b3:78:bf:62:f5:07:fa:95:24:c2:
+                    96:b2:e8:a3:23:c2:5d:03:fc:c3:d3:e5:7c:c9:75:
+                    23:d7:f4:f5:bc:de:e4:df:cd:80:bf:91:88:7d:a7:
+                    13:b4:39:ba:2c:ba:bd:d1:6b:cc:f3:a5:28:ed:44:
+                    9e:7d:52:a3:6f:96:2e:19:7e:1c:f3:5b:c7:16:8e:
+                    bb:60:7d:77:66:47:54:82:00:11:60:6c:32:c1:a8:
+                    38:1b:eb:6e:98:13:d6:ee:38:f5:f0:9f:0e:ef:fe:
+                    31:81:c1:d2:24:95:2f:53:7a:69:a2:f0:0f:86:45:
+                    8e:58:82:2b:4c:22:d4:5e:a0:e7:7d:26:27:48:df:
+                    25:46:8d:4a:28:7c:86:9e:f9:9b:1a:59:b9:65:bf:
+                    05:dd:b6:42:5d:3d:e6:00:48:82:5e:20:f7:11:82:
+                    de:ca:d8:9f:e6:37:47:26:1e:eb:78:f7:61:c3:41:
+                    64:58:02:41:f9:da:e0:d1:f8:f9:e8:fd:52:38:b6:
+                    f5:89:df
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Subject Key Identifier: 
+                0D:8C:B6:61:DA:44:B8:D1:14:7D:C3:BE:7D:5E:48:F0:CE:CA:6A:B0
+            X509v3 Certificate Policies: 
+                Policy: 1.2.250.1.86.2.2.0.1.1
+
+    Signature Algorithm: sha1WithRSAEncryption
+        24:3e:60:06:7e:1d:ef:3a:3e:db:ea:af:1c:9a:2c:01:0b:f4:
+        c5:b5:d9:49:31:f4:5d:41:8d:89:0c:4e:ff:6c:a2:fd:ff:e2:
+        06:c8:39:9f:f1:5a:a9:dd:22:58:15:a8:8a:d3:b1:e6:32:09:
+        82:03:6c:d7:3f:08:c7:f8:b9:ba:00:6d:b9:d6:fc:52:32:5d:
+        a4:7f:a4:31:94:bb:b6:4c:38:7f:28:30:35:ff:9f:23:53:b7:
+        b6:ee:14:70:00:40:2b:da:47:ab:34:7e:5e:a7:56:30:61:2b:
+        8b:43:ac:fd:b6:88:28:f5:6b:b6:3e:60:4a:ba:42:90:34:67:
+        8d:ea:eb:5f:45:54:3b:17:ac:8b:e4:c6:65:0f:ee:d0:8c:5d:
+        66:39:ce:32:a7:d8:10:97:c0:7e:34:9c:9f:94:f3:f6:86:1f:
+        cf:1b:73:ad:94:79:87:68:70:c3:33:a5:70:e7:d8:d5:38:94:
+        6f:63:79:eb:bf:0a:0e:08:e7:c5:2f:0f:42:a0:2b:14:40:ff:
+        21:e0:05:c5:27:e1:84:11:13:ba:d6:86:1d:41:0b:13:23:89:
+        d3:c9:0b:e8:8a:ba:7a:a3:a3:73:37:35:80:7d:12:b8:33:77:
+        40:38:c0:fa:5e:30:d2:f2:b6:a3:b1:d6:a2:95:97:81:9b:52:
+        ed:69:4c:ff:80:e4:53:db:54:5b:03:6d:54:5f:b1:b8:ef:24:
+        bd:6f:9f:11:c3:c7:64:c2:0f:28:62:85:66:5e:1a:7b:b2:b7:
+        ef:ae:35:c9:19:33:a8:b8:27:db:33:55:bf:68:e1:75:48:44:
+        56:fb:cd:d3:48:bb:47:89:3a:ac:69:f5:80:c6:e4:44:50:2f:
+        54:c4:aa:43:c5:31:31:58:bd:96:c5:ea:75:6c:9a:75:b1:4d:
+        f8:f7:97:ff:96:16:f2:97:4d:e8:f6:f3:11:f9:3a:7d:8a:38:
+        6e:04:cb:e1:d3:45:15:aa:a5:d1:1d:9d:5d:63:e8:24:e6:36:
+        14:e2:87:ad:1b:59:f5:44:9b:fb:d7:77:7c:1f:01:70:62:a1:
+        20:1a:a2:c5:1a:28:f4:21:03:ee:2e:d9:c1:80:ea:b9:d9:82:
+        d6:5b:76:c2:cb:3b:b5:d2:00:f0:a3:0e:e1:ad:6e:40:f7:db:
+        a0:b4:d0:46:ae:15:d7:44:c2:4d:35:f9:d2:0b:f2:17:f6:ac:
+        66:d5:24:b2:4f:d1:1c:99:c0:6e:f5:7d:eb:74:04:b8:f9:4d:
+        77:09:d7:b4:cf:07:30:09:f1:b8:00:56:d9:17:16:16:0a:2b:
+        86:df:8f:01:19:1a:e5:bb:82:63:ff:be:0b:76:16:5e:37:37:
+        e6:d8:74:97:a2:99:45:79
+SHA1 Fingerprint=2E:14:DA:EC:28:F0:FA:1E:8E:38:9A:4E:AB:EB:26:C0:0A:D3:83:C3
diff --git a/luni/src/main/files/cacerts/aeb67534.0 b/luni/src/main/files/cacerts/aeb67534.0
new file mode 100644
index 0000000..655f136
--- /dev/null
+++ b/luni/src/main/files/cacerts/aeb67534.0
@@ -0,0 +1,97 @@
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB
+8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy
+dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1
+YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3
+dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh
+IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD
+LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG
+EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g
+KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD
+ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu
+bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg
+ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R
+85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm
+4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV
+HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd
+QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t
+lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB
+o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4
+opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo
+dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW
+ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN
+AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y
+/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k
+SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy
+Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS
+Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl
+nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI=
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+             (Negative)11:d4:c2:14:2b:de:21:eb:57:9d:53:fb:0c:22:3b:ff
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=ES, O=Agencia Catalana de Certificacio (NIF Q-0801176-I), OU=Serveis Publics de Certificacio, OU=Vegeu https://www.catcert.net/verarrel (c)03, OU=Jerarquia Entitats de Certificacio Catalanes, CN=EC-ACC
+        Validity
+            Not Before: Jan  7 23:00:00 2003 GMT
+            Not After : Jan  7 22:59:59 2031 GMT
+        Subject: C=ES, O=Agencia Catalana de Certificacio (NIF Q-0801176-I), OU=Serveis Publics de Certificacio, OU=Vegeu https://www.catcert.net/verarrel (c)03, OU=Jerarquia Entitats de Certificacio Catalanes, CN=EC-ACC
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:b3:22:c7:4f:e2:97:42:95:88:47:83:40:f6:1d:
+                    17:f3:83:73:24:1e:51:f3:98:8a:c3:92:b8:ff:40:
+                    90:05:70:87:60:c9:00:a9:b5:94:65:19:22:15:17:
+                    c2:43:6c:66:44:9a:0d:04:3e:39:6f:a5:4b:7a:aa:
+                    63:b7:8a:44:9d:d9:63:91:84:66:e0:28:0f:ba:42:
+                    e3:6e:8e:f7:14:27:93:69:ee:91:0e:a3:5f:0e:b1:
+                    eb:66:a2:72:4f:12:13:86:65:7a:3e:db:4f:07:f4:
+                    a7:09:60:da:3a:42:99:c7:b2:7f:b3:16:95:1c:c7:
+                    f9:34:b5:94:85:d5:99:5e:a0:48:a0:7e:e7:17:65:
+                    b8:a2:75:b8:1e:f3:e5:42:7d:af:ed:f3:8a:48:64:
+                    5d:82:14:93:d8:c0:e4:ff:b3:50:72:f2:76:f6:b3:
+                    5d:42:50:79:d0:94:3e:6b:0c:00:be:d8:6b:0e:4e:
+                    2a:ec:3e:d2:cc:82:a2:18:65:33:13:77:9e:9a:5d:
+                    1a:13:d8:c3:db:3d:c8:97:7a:ee:70:ed:a7:e6:7c:
+                    db:71:cf:2d:94:62:df:6d:d6:f5:38:be:3f:a5:85:
+                    0a:19:b8:a8:d8:09:75:42:70:c4:ea:ef:cb:0e:c8:
+                    34:a8:12:22:98:0c:b8:13:94:b6:4b:ec:f0:d0:90:
+                    e7:27
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Alternative Name: 
+                email:ec_acc@catcert.net
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Subject Key Identifier: 
+                A0:C3:8B:44:AA:37:A5:45:BF:97:80:5A:D1:F1:78:A2:9B:E9:5D:8D
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.15096.1.3.1.10
+                  CPS: https://www.catcert.net/verarrel
+                  User Notice:
+                    Explicit Text: Vegeu https://www.catcert.net/verarrel 
+
+    Signature Algorithm: sha1WithRSAEncryption
+        a0:48:5b:82:01:f6:4d:48:b8:39:55:35:9c:80:7a:53:99:d5:
+        5a:ff:b1:71:3b:cc:39:09:94:5e:d6:da:ef:be:01:5b:5d:d3:
+        1e:d8:fd:7d:4f:cd:a0:41:e0:34:93:bf:cb:e2:86:9c:37:92:
+        90:56:1c:dc:eb:29:05:e5:c4:9e:c7:35:df:8a:0c:cd:c5:21:
+        43:e9:aa:88:e5:35:c0:19:42:63:5a:02:5e:a4:48:18:3a:85:
+        6f:dc:9d:bc:3f:9d:9c:c1:87:b8:7a:61:08:e9:77:0b:7f:70:
+        ab:7a:dd:d9:97:2c:64:1e:85:bf:bc:74:96:a1:c3:7a:12:ec:
+        0c:1a:6e:83:0c:3c:e8:72:46:9f:fb:48:d5:5e:97:e6:b1:a1:
+        f8:e4:ef:46:25:94:9c:89:db:69:38:be:ec:5c:0e:56:c7:65:
+        51:e5:50:88:88:bf:42:d5:2b:3d:e5:f9:ba:9e:2e:b3:ca:f4:
+        73:92:02:0b:be:4c:66:eb:20:fe:b9:cb:b5:99:7f:e6:b6:13:
+        fa:ca:4b:4d:d9:ee:53:46:06:3b:c6:4e:ad:93:5a:81:7e:6c:
+        2a:4b:6a:05:45:8c:f2:21:a4:31:90:87:6c:65:9c:9d:a5:60:
+        95:3a:52:7f:f5:d1:ab:08:6e:f3:ee:5b:f9:88:3d:7e:b8:6f:
+        6e:03:e4:42
+SHA1 Fingerprint=28:90:3A:63:5B:52:80:FA:E6:77:4C:0B:6D:A7:D6:BA:A6:4A:F2:E8
diff --git a/luni/src/main/files/cacerts/c33a80d4.0 b/luni/src/main/files/cacerts/c33a80d4.0
index c76b601..5744b48 100644
--- a/luni/src/main/files/cacerts/c33a80d4.0
+++ b/luni/src/main/files/cacerts/c33a80d4.0
@@ -1,31 +1,33 @@
 -----BEGIN CERTIFICATE-----
-MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
-FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
-VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
-biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
-dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
-MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
-MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
-A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
-b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
-cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
-bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
-VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
-ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
-uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
-9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
-hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
-pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
+MIIDNjCCAp+gAwIBAgIQNhIilsXjOKUgodJfTNcJVDANBgkqhkiG9w0BAQUFADCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIxMDEwMTIzNTk1OVow
+gc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcT
+CUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNV
+BAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRo
+YXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1z
+ZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
+aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560
+ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j
++ao6hnO2RlNYyIkFvYMRuHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/
+BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBlkKyID1bZ5jA01CbH0FDxkt5r1DmI
+CSLGpmODA/eZd9iy5Ri4XWPz1HP7bJyZePFLeH0ZJMMrAoT4vCLZiiLXoPxx7JGH
+IPG47LHlVYCsPVLIOQ7C8MAFT9aCdYy9X9LcdpoFEsmvcsPcJX6kTY4XpeCHf+Ga
+WuFg3GQjPEIuTQ==
 -----END CERTIFICATE-----
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 1 (0x1)
-        Signature Algorithm: md5WithRSAEncryption
+        Serial Number:
+            36:12:22:96:c5:e3:38:a5:20:a1:d2:5f:4c:d7:09:54
+        Signature Algorithm: sha1WithRSAEncryption
         Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
         Validity
             Not Before: Aug  1 00:00:00 1996 GMT
-            Not After : Dec 31 23:59:59 2020 GMT
+            Not After : Jan  1 23:59:59 2021 GMT
         Subject: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
@@ -44,13 +46,13 @@
         X509v3 extensions:
             X509v3 Basic Constraints: critical
                 CA:TRUE
-    Signature Algorithm: md5WithRSAEncryption
-        26:48:2c:16:c2:58:fa:e8:16:74:0c:aa:aa:5f:54:3f:f2:d7:
-        c9:78:60:5e:5e:6e:37:63:22:77:36:7e:b2:17:c4:34:b9:f5:
-        08:85:fc:c9:01:38:ff:4d:be:f2:16:42:43:e7:bb:5a:46:fb:
-        c1:c6:11:1f:f1:4a:b0:28:46:c9:c3:c4:42:7d:bc:fa:ab:59:
-        6e:d5:b7:51:88:11:e3:a4:85:19:6b:82:4c:a4:0c:12:ad:e9:
-        a4:ae:3f:f1:c3:49:65:9a:8c:c5:c8:3e:25:b7:94:99:bb:92:
-        32:71:07:f0:86:5e:ed:50:27:a6:0d:a6:23:f9:bb:cb:a6:07:
-        14:42
-SHA1 Fingerprint=62:7F:8D:78:27:65:63:99:D2:7D:7F:90:44:C9:FE:B3:F3:3E:FA:9A
+    Signature Algorithm: sha1WithRSAEncryption
+        65:90:ac:88:0f:56:d9:e6:30:34:d4:26:c7:d0:50:f1:92:de:
+        6b:d4:39:88:09:22:c6:a6:63:83:03:f7:99:77:d8:b2:e5:18:
+        b8:5d:63:f3:d4:73:fb:6c:9c:99:78:f1:4b:78:7d:19:24:c3:
+        2b:02:84:f8:bc:22:d9:8a:22:d7:a0:fc:71:ec:91:87:20:f1:
+        b8:ec:b1:e5:55:80:ac:3d:52:c8:39:0e:c2:f0:c0:05:4f:d6:
+        82:75:8c:bd:5f:d2:dc:76:9a:05:12:c9:af:72:c3:dc:25:7e:
+        a4:4d:8e:17:a5:e0:87:7f:e1:9a:5a:e1:60:dc:64:23:3c:42:
+        2e:4d
+SHA1 Fingerprint=E0:AB:05:94:20:72:54:93:05:60:62:02:36:70:F7:CD:2E:FC:66:66
diff --git a/luni/src/main/files/cacerts/c3a6a9ad.0 b/luni/src/main/files/cacerts/c3a6a9ad.0
new file mode 100644
index 0000000..b0b3360
--- /dev/null
+++ b/luni/src/main/files/cacerts/c3a6a9ad.0
@@ -0,0 +1,80 @@
+-----BEGIN CERTIFICATE-----
+MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB
+VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp
+bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R
+dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw
+MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy
+dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52
+ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM
+EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj
+lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ
+znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH
+2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1
+k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs
+2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD
+VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
+AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG
+KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+
+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R
+FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
+mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE
+DNuxUCAKGkq6ahq97BvIxYSazQ==
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 93214 (0x16c1e)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=AT, O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH, OU=A-Trust-nQual-03, CN=A-Trust-nQual-03
+        Validity
+            Not Before: Aug 17 22:00:00 2005 GMT
+            Not After : Aug 17 22:00:00 2015 GMT
+        Subject: C=AT, O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH, OU=A-Trust-nQual-03, CN=A-Trust-nQual-03
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:ad:3d:61:6e:03:f3:90:3b:c0:41:0b:84:80:cd:
+                    ec:2a:a3:9d:6b:bb:6e:c2:42:84:f7:51:14:e1:a0:
+                    a8:2d:51:a3:51:f2:de:23:f0:34:44:ff:94:eb:cc:
+                    05:23:95:40:b9:07:78:a5:25:f6:0a:bd:45:86:e8:
+                    d9:bd:c0:04:8e:85:44:61:ef:7f:a7:c9:fa:c1:25:
+                    cc:85:2c:63:3f:05:60:73:49:05:e0:60:78:95:10:
+                    4b:dc:f9:11:59:ce:71:7f:40:9b:8a:aa:24:df:0b:
+                    42:e2:db:56:bc:4a:d2:a5:0c:9b:b7:43:3e:dd:83:
+                    d3:26:10:02:cf:ea:23:c4:49:4e:e5:d3:e9:b4:88:
+                    ab:0c:ae:62:92:d4:65:87:d9:6a:d7:f4:85:9f:e4:
+                    33:22:25:a5:e5:c8:33:ba:c3:c7:41:dc:5f:c6:6a:
+                    cc:00:0e:6d:32:a8:b6:87:36:00:62:77:9b:1e:1f:
+                    34:cb:90:3c:78:88:74:05:eb:79:f5:93:71:65:ca:
+                    9d:c7:6b:18:2d:3d:5c:4e:e7:d5:f8:3f:31:7d:8f:
+                    87:ec:0a:22:2f:23:e9:fe:bb:7d:c9:e0:f4:ec:eb:
+                    7c:c4:b0:c3:2d:62:b5:9a:71:d6:b1:6a:e8:ec:d9:
+                    ed:d5:72:ec:be:57:01:ce:05:55:9f:de:d1:60:88:
+                    10:b3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                44:6A:95:67:55:79:11:4F
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+    Signature Algorithm: sha1WithRSAEncryption
+        55:d4:54:d1:59:48:5c:b3:93:85:aa:bf:63:2f:e4:80:ce:34:
+        a3:34:62:3e:f6:d8:ee:67:88:31:04:03:6f:0b:d4:07:fb:4e:
+        75:0f:d3:2e:d3:c0:17:c7:c6:28:ec:06:0d:11:24:0e:0e:a5:
+        5d:bf:8c:b2:13:96:71:dc:d4:ce:0e:0d:0a:68:32:6c:b9:41:
+        31:19:ab:b1:07:7b:4d:98:d3:5c:b0:d1:f0:a7:42:a0:b5:c4:
+        8e:af:fe:f1:3f:f4:ef:4f:46:00:76:eb:02:fb:f9:9d:d2:40:
+        96:c7:88:3a:b8:9f:11:79:f3:80:65:a8:bd:1f:d3:78:81:a0:
+        51:4c:37:b4:a6:5d:25:70:d1:66:c9:68:f9:2e:11:14:68:f1:
+        54:98:08:ac:26:92:0f:de:89:9e:d4:fa:b3:79:2b:d2:a3:79:
+        d4:ec:8b:ac:87:53:68:42:4c:51:51:74:1e:1b:27:2e:e3:f5:
+        1f:29:74:4d:ed:af:f7:e1:92:99:81:e8:be:3a:c7:17:50:f6:
+        b7:c6:fc:9b:b0:8a:6b:d6:88:03:91:8f:06:77:3a:85:02:dd:
+        98:d5:43:78:3f:c6:30:15:ac:9b:6b:cb:57:b7:89:51:8b:3a:
+        e8:c9:84:0c:db:b1:50:20:0a:1a:4a:ba:6a:1a:bd:ec:1b:c8:
+        c5:84:9a:cd
+SHA1 Fingerprint=D3:C0:63:F2:19:ED:07:3E:34:AD:5D:75:0B:32:76:29:FF:D5:9A:F2
diff --git a/luni/src/main/files/cacerts/d59297b8.0 b/luni/src/main/files/cacerts/d59297b8.0
new file mode 100644
index 0000000..0bb1e6d
--- /dev/null
+++ b/luni/src/main/files/cacerts/d59297b8.0
@@ -0,0 +1,78 @@
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
+MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
+U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
+DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
+dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
+YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
+OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
+zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
+VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
+hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
+ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
+awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
+OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
+coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
+okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
+t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
+1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
+SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 0 (0x0)
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=JP, O=SECOM Trust Systems CO.,LTD., OU=Security Communication RootCA2
+        Validity
+            Not Before: May 29 05:00:39 2009 GMT
+            Not After : May 29 05:00:39 2029 GMT
+        Subject: C=JP, O=SECOM Trust Systems CO.,LTD., OU=Security Communication RootCA2
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:d0:15:39:52:b1:52:b3:ba:c5:59:82:c4:5d:52:
+                    ae:3a:43:65:80:4b:c7:f2:96:bc:db:36:97:d6:a6:
+                    64:8c:a8:5e:f0:e3:0a:1c:f7:df:97:3d:4b:ae:f6:
+                    5d:ec:21:b5:41:ab:cd:b9:7e:76:9f:be:f9:3e:36:
+                    34:a0:3b:c1:f6:31:11:45:74:93:3d:57:80:c5:f9:
+                    89:99:ca:e5:ab:6a:d4:b5:da:41:90:10:c1:d6:d6:
+                    42:89:c2:bf:f4:38:12:95:4c:54:05:f7:36:e4:45:
+                    83:7b:14:65:d6:dc:0c:4d:d1:de:7e:0c:ab:3b:c4:
+                    15:be:3a:56:a6:5a:6f:76:69:52:a9:7a:b9:c8:eb:
+                    6a:9a:5d:52:d0:2d:0a:6b:35:16:09:10:84:d0:6a:
+                    ca:3a:06:00:37:47:e4:7e:57:4f:3f:8b:eb:67:b8:
+                    88:aa:c5:be:53:55:b2:91:c4:7d:b9:b0:85:19:06:
+                    78:2e:db:61:1a:fa:85:f5:4a:91:a1:e7:16:d5:8e:
+                    a2:39:df:94:b8:70:1f:28:3f:8b:fc:40:5e:63:83:
+                    3c:83:2a:1a:99:6b:cf:de:59:6a:3b:fc:6f:16:d7:
+                    1f:fd:4a:10:eb:4e:82:16:3a:ac:27:0c:53:f1:ad:
+                    d5:24:b0:6b:03:50:c1:2d:3c:16:dd:44:34:27:1a:
+                    75:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                0A:85:A9:77:65:05:98:7C:40:81:F8:0F:97:2C:38:F1:0A:EC:3C:CF
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+        4c:3a:a3:44:ac:b9:45:b1:c7:93:7e:c8:0b:0a:42:df:64:ea:
+        1c:ee:59:6c:08:ba:89:5f:6a:ca:4a:95:9e:7a:8f:07:c5:da:
+        45:72:82:71:0e:3a:d2:cc:6f:a7:b4:a1:23:bb:f6:24:9f:cb:
+        17:fe:8c:a6:ce:c2:d2:db:cc:8d:fc:71:fc:03:29:c1:6c:5d:
+        33:5f:64:b6:65:3b:89:6f:18:76:78:f5:dc:a2:48:1f:19:3f:
+        8e:93:eb:f1:fa:17:ee:cd:4e:e3:04:12:55:d6:e5:e4:dd:fb:
+        3e:05:7c:e2:1d:5e:c6:a7:bc:97:4f:68:3a:f5:e9:2e:0a:43:
+        b6:af:57:5c:62:68:7c:b7:fd:a3:8a:84:a0:ac:62:be:2b:09:
+        87:34:f0:6a:01:bb:9b:29:56:3c:fe:00:37:cf:23:6c:f1:4e:
+        aa:b6:74:46:12:6c:91:ee:34:d5:ec:9a:91:e7:44:be:90:31:
+        72:d5:49:02:f6:02:e5:f4:1f:eb:7c:d9:96:55:a9:ff:ec:8a:
+        f9:99:47:ff:35:5a:02:aa:04:cb:8a:5b:87:71:29:91:bd:a4:
+        b4:7a:0d:bd:9a:f5:57:23:00:07:21:17:3f:4a:39:d1:05:49:
+        0b:a7:b6:37:81:a5:5d:8c:aa:33:5e:81:28:7c:a7:7d:27:eb:
+        00:ae:8d:37
+SHA1 Fingerprint=5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74
diff --git a/luni/src/main/files/cacerts/ddc328ff.0 b/luni/src/main/files/cacerts/ddc328ff.0
index 0cd9896..8a6d14f 100644
--- a/luni/src/main/files/cacerts/ddc328ff.0
+++ b/luni/src/main/files/cacerts/ddc328ff.0
@@ -1,31 +1,32 @@
 -----BEGIN CERTIFICATE-----
-MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
-FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
-VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
-biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
-MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
-MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
-DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
-dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
-cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
-DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
-gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
-yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
-L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
-EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
-7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
-QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
-qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+MIIDIjCCAougAwIBAgIQNKT/9jCvTKU8MxdCoZRmdTANBgkqhkiG9w0BAQUFADCB
+xDELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhh
+d3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjEwMTAxMjM1OTU5WjCBxDELMAkGA1UE
+BhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZl
+ciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl
+/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF
+/rFrKbYvScg71CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982
+OsK1ZiIS1ocNAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADgYEAvkBpQW/G28GnvwfAReTQtUMeTJUzNelewj4o9qgNUNX/4gwP/FACjq6R
+ua00io2fJ3GqGcxL6ATK1BdrEhrWxl/WzV7/iXa/2EjYWb0IiokdV81FHlK6EpqE
++hiJX+j5MDVqAWC5mYCDhQpu2vTJj15zLTFKY6B08h+LItIpPus=
 -----END CERTIFICATE-----
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 1 (0x1)
-        Signature Algorithm: md5WithRSAEncryption
+        Serial Number:
+            34:a4:ff:f6:30:af:4c:a5:3c:33:17:42:a1:94:66:75
+        Signature Algorithm: sha1WithRSAEncryption
         Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com
         Validity
             Not Before: Aug  1 00:00:00 1996 GMT
-            Not After : Dec 31 23:59:59 2020 GMT
+            Not After : Jan  1 23:59:59 2021 GMT
         Subject: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
@@ -44,13 +45,13 @@
         X509v3 extensions:
             X509v3 Basic Constraints: critical
                 CA:TRUE
-    Signature Algorithm: md5WithRSAEncryption
-        07:fa:4c:69:5c:fb:95:cc:46:ee:85:83:4d:21:30:8e:ca:d9:
-        a8:6f:49:1a:e6:da:51:e3:60:70:6c:84:61:11:a1:1a:c8:48:
-        3e:59:43:7d:4f:95:3d:a1:8b:b7:0b:62:98:7a:75:8a:dd:88:
-        4e:4e:9e:40:db:a8:cc:32:74:b9:6f:0d:c6:e3:b3:44:0b:d9:
-        8a:6f:9a:29:9b:99:18:28:3b:d1:e3:40:28:9a:5a:3c:d5:b5:
-        e7:20:1b:8b:ca:a4:ab:8d:e9:51:d9:e2:4c:2c:59:a9:da:b9:
-        b2:75:1b:f6:42:f2:ef:c7:f2:18:f9:89:bc:a3:ff:8a:23:2e:
-        70:47
-SHA1 Fingerprint=23:E5:94:94:51:95:F2:41:48:03:B4:D5:64:D2:A3:A3:F5:D8:8B:8C
+    Signature Algorithm: sha1WithRSAEncryption
+        be:40:69:41:6f:c6:db:c1:a7:bf:07:c0:45:e4:d0:b5:43:1e:
+        4c:95:33:35:e9:5e:c2:3e:28:f6:a8:0d:50:d5:ff:e2:0c:0f:
+        fc:50:02:8e:ae:91:b9:ad:34:8a:8d:9f:27:71:aa:19:cc:4b:
+        e8:04:ca:d4:17:6b:12:1a:d6:c6:5f:d6:cd:5e:ff:89:76:bf:
+        d8:48:d8:59:bd:08:8a:89:1d:57:cd:45:1e:52:ba:12:9a:84:
+        fa:18:89:5f:e8:f9:30:35:6a:01:60:b9:99:80:83:85:0a:6e:
+        da:f4:c9:8f:5e:73:2d:31:4a:63:a0:74:f2:1f:8b:22:d2:29:
+        3e:eb
+SHA1 Fingerprint=9F:AD:91:A6:CE:6A:C6:C5:00:47:C4:4E:C9:D4:A5:0D:92:D8:49:79
diff --git a/luni/src/main/files/cacerts/fb126c6d.0 b/luni/src/main/files/cacerts/fb126c6d.0
new file mode 100644
index 0000000..8200caa
--- /dev/null
+++ b/luni/src/main/files/cacerts/fb126c6d.0
@@ -0,0 +1,106 @@
+-----BEGIN CERTIFICATE-----
+MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF
+UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ
+R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN
+MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G
+A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw
+JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+
+WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj
+SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl
+u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy
+A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk
+Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7
+MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr
+aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC
+IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A
+cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA
+YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA
+bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA
+bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
+aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA
+aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA
+ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA
+YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA
+ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA
+LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6
+Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y
+eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw
+CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G
+A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu
+Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn
+lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt
+b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg
+9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF
+ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC
+IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 994436456 (0x3b45e568)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=ES, O=Generalitat Valenciana, OU=PKIGVA, CN=Root CA Generalitat Valenciana
+        Validity
+            Not Before: Jul  6 16:22:47 2001 GMT
+            Not After : Jul  1 15:22:47 2021 GMT
+        Subject: C=ES, O=Generalitat Valenciana, OU=PKIGVA, CN=Root CA Generalitat Valenciana
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:c6:2a:ab:57:11:37:2f:22:8a:ca:03:74:1d:ca:
+                    ed:2d:a2:0b:bc:33:52:40:26:47:be:5a:69:a6:3b:
+                    72:36:17:4c:e8:df:b8:bb:2f:76:e1:40:46:74:65:
+                    02:90:52:08:b4:ff:a8:8c:c1:e0:c7:89:56:10:39:
+                    33:ef:68:b4:5f:5f:da:6d:23:a1:89:5e:22:a3:4a:
+                    06:f0:27:f0:57:b9:f8:e9:4e:32:77:0a:3f:41:64:
+                    f3:eb:65:ee:76:fe:54:aa:7d:1d:20:ae:f3:d7:74:
+                    c2:0a:5f:f5:08:28:52:08:cc:55:5d:d2:0f:db:9a:
+                    81:a5:bb:a1:b3:c1:94:cd:54:e0:32:75:31:91:1a:
+                    62:b2:de:75:e2:cf:4f:89:d9:91:90:0f:41:1b:b4:
+                    5a:4a:77:bd:67:83:e0:93:e7:5e:a7:0c:e7:81:d3:
+                    f4:52:ac:53:b2:03:c7:44:26:fb:79:e5:cb:34:60:
+                    50:10:7b:1b:db:6b:d7:47:ab:5f:7c:68:ca:6e:9d:
+                    41:03:10:ee:6b:99:7b:5e:25:a8:c2:ab:e4:c0:f3:
+                    5c:9c:e3:be:ce:31:4c:64:1e:5e:80:a2:f5:83:7e:
+                    0c:d6:ca:8c:55:8e:be:e0:be:49:07:0f:a3:24:41:
+                    7a:58:1d:84:ea:58:12:c8:e1:b7:ed:ef:93:de:94:
+                    08:31
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            Authority Information Access: 
+                OCSP - URI:http://ocsp.pki.gva.es
+
+            X509v3 Basic Constraints: critical
+                CA:TRUE, pathlen:2
+            X509v3 Certificate Policies: 
+                Policy: 1.3.6.1.4.1.8149.2.1.0
+                  User Notice:
+                    Explicit Text: 
+                  CPS: http://www.pki.gva.es/cps
+
+            X509v3 Subject Key Identifier: 
+                7B:35:D3:40:D2:1C:78:19:66:EF:74:10:28:DC:3E:4F:B2:78:04:FC
+            X509v3 Authority Key Identifier: 
+                keyid:7B:35:D3:40:D2:1C:78:19:66:EF:74:10:28:DC:3E:4F:B2:78:04:FC
+                DirName:/C=ES/O=Generalitat Valenciana/OU=PKIGVA/CN=Root CA Generalitat Valenciana
+                serial:3B:45:E5:68
+
+    Signature Algorithm: sha1WithRSAEncryption
+        24:61:4e:f5:b5:c8:42:02:2a:b3:5c:75:ad:c5:6d:ca:e7:94:
+        3f:a5:68:95:88:c1:54:c0:10:69:a2:12:2f:18:3f:25:50:a8:
+        7c:4a:ea:c6:09:d9:f4:75:c6:40:da:af:50:9d:3d:a5:16:bb:
+        6d:31:c6:c7:73:0a:48:fe:20:72:ed:6f:cc:e8:83:61:16:46:
+        90:01:95:4b:7d:8e:9a:52:09:2f:f6:6f:1c:e4:a1:71:cf:8c:
+        2a:5a:17:73:83:47:4d:0f:36:fb:04:4d:49:51:e2:14:c9:64:
+        61:fb:d4:14:e0:f4:9e:b7:34:8f:0a:26:bd:97:5c:f4:79:3a:
+        4a:30:19:cc:ad:4f:a0:98:8a:b4:31:97:2a:e2:73:6d:7e:78:
+        b8:f8:88:89:4f:b1:22:91:64:4b:f5:50:de:03:db:e5:c5:76:
+        e7:13:66:75:7e:65:fb:01:9f:93:87:88:9d:f9:46:57:7c:4d:
+        60:af:98:73:13:23:a4:20:91:81:fa:d0:61:66:b8:7d:d1:af:
+        d6:6f:1e:6c:3d:e9:11:fd:a9:f9:82:22:86:99:33:71:5a:ea:
+        19:57:3d:91:cd:a9:c0:a3:6e:07:13:a6:c9:ed:f8:68:a3:9e:
+        c3:5a:72:09:87:28:d1:c4:73:c4:73:18:5f:50:75:16:31:9f:
+        b7:e8:7c:c3
+SHA1 Fingerprint=A0:73:E5:C5:BD:43:61:0D:86:4C:21:13:0A:85:58:57:CC:9C:EA:46
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index d3af278..4541f1b 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -1618,9 +1618,13 @@
                 throw corruptStream(tc);
         }
 
-        Enum<?> result = Enum.valueOf((Class) classDesc.forClass(), name);
+        Enum<?> result;
+        try {
+            result = Enum.valueOf((Class) classDesc.forClass(), name);
+        } catch (IllegalArgumentException e) {
+            throw new InvalidObjectException(e.getMessage());
+        }
         registerObjectRead(result, newHandle, unshared);
-
         return result;
     }
 
diff --git a/luni/src/main/java/java/io/Serializable.java b/luni/src/main/java/java/io/Serializable.java
index 9cd7816..f1256bb 100644
--- a/luni/src/main/java/java/io/Serializable.java
+++ b/luni/src/main/java/java/io/Serializable.java
@@ -18,20 +18,83 @@
 package java.io;
 
 /**
- * An empty marker interface for classes that want to support serialization and
- * deserialization based on the {@code ObjectOutputStream} and {@code
- * ObjectInputStream} classes. Implementing this interface is enough to make
- * most classes serializable. If a class needs more fine-grained control over
- * the serialization process (for example to implement compatibility with older
- * versions of the class), it can achieve this by providing the following two
- * methods (signatures must match exactly):
- * <p>
- * {@code private void writeObject(java.io.ObjectOutputStream out) throws
- * IOException}
- * <p>
- * {@code private void readObject(java.io.ObjectInputStream in) throws
- * IOException, ClassNotFoundException}
+ * Marks classes that can be serialized by {@link ObjectOutputStream} and
+ * deserialized by {@link ObjectInputStream}.
+ *
+ * <p><strong>Warning:</strong> this interface limits how its implementing
+ * classes can change in the future. By implementing {@code Serializable} you
+ * expose your flexible in-memory implementation details as a rigid binary
+ * representation. Simple code changes--like renaming private fields--are
+ * not safe when the changed class is serializable.
+ *
+ * <h3>The Serialized Form</h3>
+ * By default, the serialization mechanism encodes an object's class name, the
+ * names of its non-transient fields (including non-public fields), and the
+ * values of all of those fields. The output is an opaque sequence of bytes.
+ * Those bytes can be decoded into a new, equivalent instance as long as the
+ * decoder has compatible versions of the originating classes.
+ *
+ * <p>Changing the class name, field names or field types breaks serialization
+ * compatibility and complicates interoperability between old and new versions
+ * of the serializable class. Adding or removing fields also complicates
+ * serialization between versions of a class because it requires your code to
+ * cope with missing fields.
+ *
+ * <p>Every serializable class is assigned a version identifier called a {@code
+ * serialVersionUID}. By default, this identifier is computed by hashing the
+ * class declaration and its members. This identifier is included in the
+ * serialized form so that version conflicts can be detected during
+ * deserialization. If the local {@code serialVersionUID} differs from the
+ * {@code serialVersionUID} in the serialized data, deserialization will fail
+ * with an {@link InvalidClassException}.
+ *
+ * <p>You can avoid this failure by declaring an explicit {@code
+ * serialVersionUID}. Declaring an explicit {@code serialVersionUID} tells the
+ * serialization mechanism that the class is forward and backward compatible
+ * with all versions that share that {@code serialVersionUID}. Declaring a
+ * {@code serialVersionUID} looks like this: <pre>   {@code
+ *
+ *     private static final long serialVersionUID = 0L;
+ * }</pre>
+ * If you declare a {@code serialVersionUID}, you should increment it each
+ * time your class changes incompatibly with the previous version. Typically
+ * this is when you add, change or remove a non-transient field.
+ *
+ * <p>You can take control of your serialized form by implementing these two
+ * methods with these exact signatures in your serializable classes:
+ * <pre>   {@code
+ *
+ *   private void writeObject(java.io.ObjectOutputStream out)
+ *       throws IOException {
+ *     // write 'this' to 'out'...
+ *   }
+ *
+ *   private void readObject(java.io.ObjectInputStream in)
+ *       throws IOException, ClassNotFoundException {
+ *     // populate the fields of 'this' from the data in 'in'...
+ *   }
+ * }</pre>
+ * It is impossible to maintain serialization compatibility across a class name
+ * change. For this reason, implementing {@code Serializable} in anonymous
+ * inner classes is highly discouraged: simply reordering the members in the
+ * file could change the generated class name and break serialization
+ * compatibility.
+ *
+ * <p>You can exclude member fields from serialization by giving them the {@code
+ * transient} modifier. Upon deserialization, the transient field's value will
+ * be null, 0, or false according to its type.
+ *
+ * <h3>Implement Serializable Judiciously</h3>
+ * Refer to <i>Effective Java</i>'s chapter on serialization for thorough
+ * coverage of the serialization API. The book explains how to use this
+ * interface without harming your application's maintainability.
+ *
+ * <h3>Recommended Alternatives</h3>
+ * <strong>JSON</strong> is concise, human-readable and efficient. Android
+ * includes both a {@link android.util.JsonReader streaming API} and a {@link
+ * org.json.JSONObject tree API} to read and write JSON. Use a binding library
+ * like <a href="http://code.google.com/p/google-gson/">GSON</a> to read and
+ * write Java objects directly.
  */
 public interface Serializable {
-    /* empty */
 }
diff --git a/luni/src/main/java/java/lang/AssertionError.java b/luni/src/main/java/java/lang/AssertionError.java
index 3d0d2a4..077e57c 100644
--- a/luni/src/main/java/java/lang/AssertionError.java
+++ b/luni/src/main/java/java/lang/AssertionError.java
@@ -52,8 +52,10 @@
      *            optionally the cause.
      */
     public AssertionError(Object detailMessage) {
-        super(String.valueOf(detailMessage),
-                (detailMessage instanceof Throwable ? (Throwable) detailMessage : null));
+        super(String.valueOf(detailMessage));
+        if (detailMessage instanceof Throwable) {
+            initCause((Throwable) detailMessage);
+        }
     }
 
     /**
diff --git a/luni/src/main/java/java/lang/Daemons.java b/luni/src/main/java/java/lang/Daemons.java
index 88d4d93..a7d0964 100644
--- a/luni/src/main/java/java/lang/Daemons.java
+++ b/luni/src/main/java/java/lang/Daemons.java
@@ -58,7 +58,8 @@
             if (thread != null) {
                 throw new IllegalStateException("already running");
             }
-            thread = new Thread(this, getClass().getSimpleName());
+            thread = new Thread(ThreadGroup.mSystem, this,
+                getClass().getSimpleName());
             thread.setDaemon(true);
             thread.start();
         }
@@ -179,6 +180,9 @@
             try {
                 finalizingStartedNanos = System.nanoTime();
                 finalizingObject = object;
+                synchronized (FinalizerWatchdogDaemon.INSTANCE) {
+                    FinalizerWatchdogDaemon.INSTANCE.notify();
+                }
                 object.finalize();
             } catch (Throwable ex) {
                 // The RI silently swallows these, but Android has always logged.
@@ -199,37 +203,40 @@
 
         @Override public void run() {
             while (isRunning()) {
-                Object object = FinalizerDaemon.INSTANCE.finalizingObject;
-                long startedNanos = FinalizerDaemon.INSTANCE.finalizingStartedNanos;
+                try {
+                    Object object = FinalizerDaemon.INSTANCE.finalizingObject;
+                    long startedNanos = FinalizerDaemon.INSTANCE.finalizingStartedNanos;
 
-                long sleepMillis = MAX_FINALIZE_MILLIS;
-                if (object != null) {
+                    if (object == null) {
+                        synchronized (this) {
+                            // wait until something is being finalized
+                            // http://code.google.com/p/android/issues/detail?id=22778
+                            wait();
+                            continue;
+                        }
+                    }
+
                     long elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
-                    sleepMillis -= elapsedMillis;
-                }
-
-                if (sleepMillis > 0) {
-                    try {
+                    long sleepMillis = MAX_FINALIZE_MILLIS - elapsedMillis;
+                    if (sleepMillis > 0) {
                         Thread.sleep(sleepMillis);
-                    } catch (InterruptedException e) {
+                        elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
+                    }
+
+                    if (object != FinalizerDaemon.INSTANCE.finalizingObject
+                            || VMRuntime.getRuntime().isDebuggerActive()) {
                         continue;
                     }
-                }
 
-                if (object == null
-                        || object != FinalizerDaemon.INSTANCE.finalizingObject
-                        || VMRuntime.getRuntime().isDebuggerActive()) {
-                    continue;
+                    // The current object has exceeded the finalization deadline; abort!
+                    Exception syntheticException = new TimeoutException();
+                    syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
+                    System.logE(object.getClass().getName() + ".finalize() timed out after "
+                            + elapsedMillis + " ms; limit is " + MAX_FINALIZE_MILLIS + " ms",
+                            syntheticException);
+                    System.exit(2);
+                } catch (InterruptedException ignored) {
                 }
-
-                // The current object has exceeded the finalization deadline; abort!
-                long elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
-                Exception syntheticException = new TimeoutException();
-                syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
-                System.logE(object.getClass().getName() + ".finalize() timed out after "
-                        + elapsedMillis + " ms; limit is " + MAX_FINALIZE_MILLIS + " ms",
-                        syntheticException);
-                System.exit(2);
             }
         }
     }
diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java
index c480185..1e820a9 100644
--- a/luni/src/main/java/java/lang/ProcessManager.java
+++ b/luni/src/main/java/java/lang/ProcessManager.java
@@ -253,11 +253,17 @@
         }
 
         public void destroy() {
-            try {
-                Libcore.os.kill(pid, SIGKILL);
-            } catch (ErrnoException e) {
-                System.logI("Failed to destroy process " + pid, e);
+            // If the process hasn't already exited, send it SIGKILL.
+            synchronized (exitValueMutex) {
+                if (exitValue == null) {
+                    try {
+                        Libcore.os.kill(pid, SIGKILL);
+                    } catch (ErrnoException e) {
+                        System.logI("Failed to destroy process " + pid, e);
+                    }
+                }
             }
+            // Close any open streams.
             IoUtils.closeQuietly(inputStream);
             IoUtils.closeQuietly(errorStream);
             IoUtils.closeQuietly(outputStream);
@@ -266,10 +272,8 @@
         public int exitValue() {
             synchronized (exitValueMutex) {
                 if (exitValue == null) {
-                    throw new IllegalThreadStateException(
-                            "Process has not yet terminated.");
+                    throw new IllegalThreadStateException("Process has not yet terminated: " + pid);
                 }
-
                 return exitValue;
             }
         }
diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java
index b3a0796..320f157 100644
--- a/luni/src/main/java/java/lang/Runtime.java
+++ b/luni/src/main/java/java/lang/Runtime.java
@@ -284,7 +284,7 @@
                 }
 
                 // Get out of here finally...
-                nativeExit(code, true);
+                nativeExit(code);
             }
         }
     }
@@ -393,7 +393,7 @@
         throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
     }
 
-    private static native void nativeExit(int code, boolean isExit);
+    private static native void nativeExit(int code);
 
     private static native String nativeLoad(String filename, ClassLoader loader);
 
@@ -595,7 +595,7 @@
      */
     public void halt(int code) {
         // Get out of here...
-        nativeExit(code, false);
+        nativeExit(code);
     }
 
     /**
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index f452186..de99161 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -616,7 +616,8 @@
      *
      * <p>Security managers do <i>not</i> provide a secure environment for
      * executing untrusted code and are unsupported on Android. Untrusted code
-     * cannot be safely isolated within a single VM on Android.
+     * cannot be safely isolated within a single VM on Android, so this method
+     * <i>always</i> throws a {@code SecurityException}.
      *
      * @param sm a security manager
      * @throws SecurityException always
diff --git a/luni/src/main/java/java/lang/ref/SoftReference.java b/luni/src/main/java/java/lang/ref/SoftReference.java
index 8880171..12c3491 100644
--- a/luni/src/main/java/java/lang/ref/SoftReference.java
+++ b/luni/src/main/java/java/lang/ref/SoftReference.java
@@ -33,56 +33,47 @@
 package java.lang.ref;
 
 /**
- * Implements a soft reference, which is the least-weak of the three types of
- * references. Once the garbage collector has decided that an object {@code obj}
- * is softly-reachable, the following
- * may happen, either immediately or at a later point:
+ * A reference that is cleared when its referent is not strongly reachable and
+ * there is memory pressure.
  *
+ * <h3>Avoid Soft References for Caching</h3>
+ * In practice, soft references are inefficient for caching. The runtime doesn't
+ * have enough information on which references to clear and which to keep. Most
+ * fatally, it doesn't know what to do when given the choice between clearing a
+ * soft reference and growing the heap.
+ *
+ * <p>The lack of information on the value to your application of each reference
+ * limits the usefulness of soft references. References that are cleared too
+ * early cause unnecessary work; those that are cleared too late waste memory.
+ *
+ * <p>Most applications should use an {@code android.util.LruCache} instead of
+ * soft references. LruCache has an effective eviction policy and lets the user
+ * tune how much memory is allotted.
+ *
+ * <h3>Garbage Collection of Soft References</h3>
+ * When the garbage collector encounters an object {@code obj} that is
+ * softly-reachable, the following happens:
  * <ul>
- *   <li>
- *     A set {@code ref} of references is determined. {@code ref} contains the
- *     following elements:
- *     <ul>
- *       <li>
- *         All soft references pointing to {@code obj}.
- *       </li>
- *       <li>
- *         All soft references pointing to objects from which {@code obj} is
- *         strongly reachable.
- *       </li>
- *     </ul>
+ *   <li>A set {@code refs} of references is determined. {@code refs} contains
+ *       the following elements:
+ *       <ul>
+ *         <li>All soft references pointing to {@code obj}.</li>
+ *         <li>All soft references pointing to objects from which {@code obj} is
+ *           strongly reachable.</li>
+ *       </ul>
  *   </li>
- *   <li>
- *     All references in {@code ref} are atomically cleared.
- *   </li>
- *   <li>
- *     At the same time or some time in the future, all references in {@code
- *     ref} will be enqueued with their corresponding reference queues, if any.
- *   </li>
+ *   <li>All references in {@code refs} are atomically cleared.</li>
+ *   <li>At the same time or some time in the future, all references in {@code
+ *       refs} will be enqueued with their corresponding reference queues, if
+ *       any.</li>
  * </ul>
+ * The system may delay clearing and enqueueing soft references, yet all {@code
+ * SoftReference}s pointing to softly reachable objects will be cleared before
+ * the runtime throws an {@link OutOfMemoryError}.
  *
- * The system may decide not to clear and enqueue soft references until a later
- * time, yet all {@code SoftReference}s pointing to softly reachable objects are
- * guaranteed to be cleared before the VM will throw an {@link
- * java.lang.OutOfMemoryError}.
- *
- * Soft references are useful for caches that should automatically have
- * their entries removed once they are not referenced any more (from outside),
- * and there is a need for memory. The difference between a {@code
- * SoftReference} and a {@code WeakReference} is the point of time at which the
- * decision is made to clear and enqueue the reference:
- *
- * <ul>
- *   <li>
- *     A {@code SoftReference} should be cleared and enqueued <em>as late as
- *     possible</em>, that is, in case the VM is in danger of running out of
- *     memory.
- *   </li>
- *   <li>
- *     A {@code WeakReference} may be cleared and enqueued as soon as is
- *     known to be weakly-referenced.
- *   </li>
- * </ul>
+ * <p>Unlike a {@code WeakReference}, a {@code SoftReference} will not be
+ * cleared and enqueued until the runtime must reclaim memory to satisfy an
+ * allocation.
  */
 public class SoftReference<T> extends Reference<T> {
 
diff --git a/luni/src/main/java/java/lang/reflect/AccessibleObject.java b/luni/src/main/java/java/lang/reflect/AccessibleObject.java
index 6dde0c2..9c6b8c7 100644
--- a/luni/src/main/java/java/lang/reflect/AccessibleObject.java
+++ b/luni/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -183,10 +183,10 @@
         StringBuilder result = new StringBuilder();
 
         if (types.length != 0) {
-            result.append(types[0].getName());
+            appendTypeName(result, types[0]);
             for (int i = 1; i < types.length; i++) {
                 result.append(',');
-                result.append(types[i].getName());
+                appendTypeName(result, types[i]);
             }
         }
 
@@ -228,23 +228,21 @@
     private static native Object[] getClassSignatureAnnotation(Class clazz);
 
     /**
-     * Appends the specified class name to the buffer. The class may represent
-     * a simple type, a reference type or an array type.
-     *
-     * @param sb buffer
-     * @param obj the class which name should be appended to the buffer
-     *
-     * @throws NullPointerException if any of the arguments is null
+     * Appends the best {@link #toString} name for {@code c} to {@code out}.
+     * This works around the fact that {@link Class#getName} is lousy for
+     * primitive arrays (it writes "[C" instead of "char[]") and {@link
+     * Class#getCanonicalName()} is lousy for nested classes (it uses a "."
+     * separator rather than a "$" separator).
      */
-    void appendArrayType(StringBuilder sb, Class<?> obj) {
+    void appendTypeName(StringBuilder out, Class<?> c) {
         int dimensions = 0;
-        while (obj.isArray()) {
-            obj = obj.getComponentType();
+        while (c.isArray()) {
+            c = c.getComponentType();
             dimensions++;
         }
-        sb.append(obj.getName());
+        out.append(c.getName());
         for (int d = 0; d < dimensions; d++) {
-            sb.append("[]");
+            out.append("[]");
         }
     }
 
diff --git a/luni/src/main/java/java/lang/reflect/Array.java b/luni/src/main/java/java/lang/reflect/Array.java
index 3ecfae0..088a434 100644
--- a/luni/src/main/java/java/lang/reflect/Array.java
+++ b/luni/src/main/java/java/lang/reflect/Array.java
@@ -33,446 +33,343 @@
 package java.lang.reflect;
 
 /**
- * This class provides static methods to create and access arrays dynamically.
+ * Provides static methods to create and access arrays dynamically.
  */
 public final class Array {
+    private Array() {
+    }
 
-    /**
-     * Prevent this class from being instantiated.
-     */
-    private Array(){
-        //do nothing
+    private static IllegalArgumentException notAnArray(Object o) {
+        throw new IllegalArgumentException("Not an array: " + o.getClass());
+    }
+
+    private static IllegalArgumentException incompatibleType(Object o) {
+        throw new IllegalArgumentException("Array has incompatible type: " + o.getClass());
+    }
+
+    private static RuntimeException badArray(Object array) {
+        if (array == null) {
+            throw new NullPointerException("array == null");
+        } else if (!array.getClass().isArray()) {
+            throw notAnArray(array);
+        } else {
+            throw incompatibleType(array);
+        }
     }
 
     /**
-     * Returns the element of the array at the specified index. This reproduces
-     * the effect of {@code array[index]}. If the array component is a primitive
-     * type, the result is automatically boxed.
+     * Returns the element of the array at the specified index. Equivalent to {@code array[index]}.
+     * If the array component is a primitive type, the result is automatically boxed.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element, possibly boxed
-     *
-     * @throws NullPointerException
-     *             if the array is null
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code  index < 0 || index >= array.length}
      */
-    public static Object get(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof Object[])
+    public static Object get(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof Object[]) {
             return ((Object[]) array)[index];
-
-        if (array instanceof boolean[])
+        }
+        if (array instanceof boolean[]) {
             return ((boolean[]) array)[index] ? Boolean.TRUE : Boolean.FALSE;
-
-        if (array instanceof byte[])
+        }
+        if (array instanceof byte[]) {
             return Byte.valueOf(((byte[]) array)[index]);
-
-        if (array instanceof char[])
+        }
+        if (array instanceof char[]) {
             return Character.valueOf(((char[]) array)[index]);
-
-        if (array instanceof short[])
+        }
+        if (array instanceof short[]) {
             return Short.valueOf(((short[]) array)[index]);
-
-        if (array instanceof int[])
+        }
+        if (array instanceof int[]) {
             return Integer.valueOf(((int[]) array)[index]);
-
-        if (array instanceof long[])
+        }
+        if (array instanceof long[]) {
             return Long.valueOf(((long[]) array)[index]);
-
-        if (array instanceof float[])
+        }
+        if (array instanceof float[]) {
             return new Float(((float[]) array)[index]);
-
-        if (array instanceof double[])
+        }
+        if (array instanceof double[]) {
             return new Double(((double[]) array)[index]);
-
-        if (array == null)
-            throw new NullPointerException();
-
-        throw new IllegalArgumentException("Not an array");
+        }
+        if (array == null) {
+            throw new NullPointerException("array == null");
+        }
+        throw notAnArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code boolean}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the boolean at the given index in the given boolean array.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static boolean getBoolean(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static boolean getBoolean(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof boolean[]) {
             return ((boolean[]) array)[index];
-        } else if (array == null) {
-            throw new NullPointerException();
-        } else if (array.getClass().isArray()) {
-            throw new IllegalArgumentException("Wrong array type");
-        } else {
-            throw new IllegalArgumentException("Not an array");
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code byte}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the byte at the given index in the given byte array.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static byte getByte(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static byte getByte(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof byte[]) {
             return ((byte[]) array)[index];
-        } else {
-            return getBoolean(array, index) ? (byte)1 : (byte)0;
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code char}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the char at the given index in the given char array.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static char getChar(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static char getChar(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof char[]) {
             return ((char[]) array)[index];
-        } else if (array == null) {
-            throw new NullPointerException();
-        } else if (array.getClass().isArray()) {
-            throw new IllegalArgumentException("Wrong array type");
-        } else {
-            throw new IllegalArgumentException("Not an array");
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code double}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the double at the given index in the given array.
+     * Applies to byte, char, float, double, int, long, and short arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static double getDouble(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static double getDouble(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof double[]) {
             return ((double[]) array)[index];
-        } else {
-            return getFloat(array, index);
+        } else if (array instanceof byte[]) {
+            return ((byte[]) array)[index];
+        } else if (array instanceof char[]) {
+            return ((char[]) array)[index];
+        } else if (array instanceof float[]) {
+            return ((float[]) array)[index];
+        } else if (array instanceof int[]) {
+            return ((int[]) array)[index];
+        } else if (array instanceof long[]) {
+            return ((long[]) array)[index];
+        } else if (array instanceof short[]) {
+            return ((short[]) array)[index];
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code float}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the float at the given index in the given array.
+     * Applies to byte, char, float, int, long, and short arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static float getFloat(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static float getFloat(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof float[]) {
             return ((float[]) array)[index];
-        } else {
-            return getLong(array, index);
+        } else if (array instanceof byte[]) {
+            return ((byte[]) array)[index];
+        } else if (array instanceof char[]) {
+            return ((char[]) array)[index];
+        } else if (array instanceof int[]) {
+            return ((int[]) array)[index];
+        } else if (array instanceof long[]) {
+            return ((long[]) array)[index];
+        } else if (array instanceof short[]) {
+            return ((short[]) array)[index];
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to an
-     * {@code int}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the int at the given index in the given array.
+     * Applies to byte, char, int, and short arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static int getInt(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static int getInt(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof int[]) {
             return ((int[]) array)[index];
-        } else {
-            return getShort(array, index);
+        } else if (array instanceof byte[]) {
+            return ((byte[]) array)[index];
+        } else if (array instanceof char[]) {
+            return ((char[]) array)[index];
+        } else if (array instanceof short[]) {
+            return ((short[]) array)[index];
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the length of the array. This reproduces the effect of {@code
-     * array.length}
+     * Returns the length of the array. Equivalent to {@code array.length}.
      *
-     * @param array
-     *            the array
-     *
-     * @return the length of the array
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array
      */
     public static int getLength(Object array) {
-        if (array instanceof Object[])
+        if (array instanceof Object[]) {
             return ((Object[]) array).length;
-
-        if (array instanceof boolean[])
+        } else if (array instanceof boolean[]) {
             return ((boolean[]) array).length;
-
-        if (array instanceof byte[])
+        } else if (array instanceof byte[]) {
             return ((byte[]) array).length;
-
-        if (array instanceof char[])
+        } else if (array instanceof char[]) {
             return ((char[]) array).length;
-
-        if (array instanceof short[])
-            return ((short[]) array).length;
-
-        if (array instanceof int[])
-            return ((int[]) array).length;
-
-        if (array instanceof long[])
-            return ((long[]) array).length;
-
-        if (array instanceof float[])
-            return ((float[]) array).length;
-
-        if (array instanceof double[])
+        } else if (array instanceof double[]) {
             return ((double[]) array).length;
-
-        if (array == null)
-            throw new NullPointerException();
-
-        throw new IllegalArgumentException("Not an array");
-    }
+        } else if (array instanceof float[]) {
+            return ((float[]) array).length;
+        } else if (array instanceof int[]) {
+            return ((int[]) array).length;
+        } else if (array instanceof long[]) {
+            return ((long[]) array).length;
+        } else if (array instanceof short[]) {
+            return ((short[]) array).length;
+        }
+        throw badArray(array);
+      }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code long}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the long at the given index in the given array.
+     * Applies to byte, char, int, long, and short arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static long getLong(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static long getLong(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof long[]) {
             return ((long[]) array)[index];
-        } else {
-            return getInt(array, index);
+        } else if (array instanceof byte[]) {
+            return ((byte[]) array)[index];
+        } else if (array instanceof char[]) {
+            return ((char[]) array)[index];
+        } else if (array instanceof int[]) {
+            return ((int[]) array)[index];
+        } else if (array instanceof short[]) {
+            return ((short[]) array)[index];
         }
+        throw badArray(array);
     }
 
     /**
-     * Returns the element of the array at the specified index, converted to a
-     * {@code short}, if possible. This reproduces the effect of {@code
-     * array[index]}
+     * Returns the short at the given index in the given array.
+     * Applies to byte and short arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     *
-     * @return the requested element
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the element at the
      *             index position can not be converted to the return type
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code index < 0 || index >= array.length}
      */
-    public static short getShort(Object array, int index)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof short[])
+    public static short getShort(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof short[]) {
             return ((short[]) array)[index];
-
-        return getByte(array, index);
+        } else if (array instanceof byte[]) {
+            return ((byte[]) array)[index];
+        }
+        throw badArray(array);
     }
 
     /**
      * Returns a new multidimensional array of the specified component type and
-     * dimensions. This reproduces the effect of {@code new
-     * componentType[d0][d1]...[dn]} for a dimensions array of { d0, d1, ... ,
-     * dn }.
+     * dimensions. Equivalent to {@code new componentType[d0][d1]...[dn]} for a
+     * dimensions array of { d0, d1, ... , dn }.
      *
-     * @param componentType
-     *            the component type of the new array
-     * @param dimensions
-     *            the dimensions of the new array
-     *
-     * @return the new array
-     *
-     * @throws NullPointerException
-     *             if the component type is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws NegativeArraySizeException
      *             if any of the dimensions are negative
      * @throws IllegalArgumentException
      *             if the array of dimensions is of size zero, or exceeds the
      *             limit of the number of dimension for an array (currently 255)
      */
-    public static Object newInstance(Class<?> componentType, int... dimensions)
-            throws NegativeArraySizeException, IllegalArgumentException {
-        if (dimensions.length <= 0 || dimensions.length > 255)
-            throw new IllegalArgumentException("Bad number of dimensions");
-
-        if (componentType == void.class)
-            throw new IllegalArgumentException();
-
-        if (componentType == null)
-            throw new NullPointerException();
-
+    public static Object newInstance(Class<?> componentType, int... dimensions) throws NegativeArraySizeException, IllegalArgumentException {
+        if (dimensions.length <= 0 || dimensions.length > 255) {
+            throw new IllegalArgumentException("Bad number of dimensions: " + dimensions.length);
+        }
+        if (componentType == void.class) {
+            throw new IllegalArgumentException("Can't allocate an array of void");
+        }
+        if (componentType == null) {
+            throw new NullPointerException("componentType == null");
+        }
         return createMultiArray(componentType, dimensions);
     }
 
     /*
      * Create a multi-dimensional array of objects with the specified type.
      */
-    native private static Object createMultiArray(Class<?> componentType,
-        int[] dimensions) throws NegativeArraySizeException;
+    private static native Object createMultiArray(Class<?> componentType, int[] dimensions) throws NegativeArraySizeException;
 
     /**
-     * Returns a new array of the specified component type and length. This
-     * reproduces the effect of {@code new componentType[size]}.
-     *
-     * @param componentType
-     *            the component type of the new array
-     * @param size
-     *            the length of the new array
-     *
-     * @return the new array
+     * Returns a new array of the specified component type and length.
+     * Equivalent to {@code new componentType[size]}.
      *
      * @throws NullPointerException
      *             if the component type is null
      * @throws NegativeArraySizeException
      *             if {@code size < 0}
      */
-    public static Object newInstance(Class<?> componentType, int size)
-            throws NegativeArraySizeException {
+    public static Object newInstance(Class<?> componentType, int size) throws NegativeArraySizeException {
         if (!componentType.isPrimitive()) {
             return createObjectArray(componentType, size);
-        }
-        if (componentType == boolean.class) {
+        } else if (componentType == boolean.class) {
             return new boolean[size];
-        }
-        if (componentType == byte.class) {
+        } else if (componentType == byte.class) {
             return new byte[size];
-        }
-        if (componentType == char.class) {
+        } else if (componentType == char.class) {
             return new char[size];
-        }
-        if (componentType == short.class) {
+        } else if (componentType == short.class) {
             return new short[size];
-        }
-        if (componentType == int.class) {
+        } else if (componentType == int.class) {
             return new int[size];
-        }
-        if (componentType == long.class) {
+        } else if (componentType == long.class) {
             return new long[size];
-        }
-        if (componentType == float.class) {
+        } else if (componentType == float.class) {
             return new float[size];
-        }
-        if (componentType == double.class) {
+        } else if (componentType == double.class) {
             return new double[size];
-        }
-        if (componentType == void.class) {
-            throw new IllegalArgumentException();
+        } else if (componentType == void.class) {
+            throw new IllegalArgumentException("Can't allocate an array of void");
         }
         throw new AssertionError();
     }
@@ -480,81 +377,58 @@
     /*
      * Create a one-dimensional array of objects with the specified type.
      */
-    native private static Object createObjectArray(Class<?> componentType,
-        int length) throws NegativeArraySizeException;
+    private static native Object createObjectArray(Class<?> componentType, int length) throws NegativeArraySizeException;
 
     /**
-     * Sets the element of the array at the specified index to the value. This
-     * reproduces the effect of {@code array[index] = value}. If the array
+     * Sets the element of the array at the specified index to the value.
+     * Equivalent to {@code array[index] = value}. If the array
      * component is a primitive type, the value is automatically unboxed.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if {@code array} is not an array or the value cannot be
      *             converted to the array type by a widening conversion
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code  index < 0 || index >= array.length}
      */
-    public static void set(Object array, int index, Object value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static void set(Object array, int index, Object value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (!array.getClass().isArray()) {
-            throw new IllegalArgumentException("Not an array type");
+            throw notAnArray(array);
         }
 
         if (array instanceof Object[]) {
-            if (value != null &&
-                !array.getClass().getComponentType().isInstance(value)) {
-                // incompatible object type for this array
-                throw new IllegalArgumentException("Wrong array type");
+            if (value != null && !array.getClass().getComponentType().isInstance(value)) {
+                throw incompatibleType(array);
             }
-
             ((Object[]) array)[index] = value;
         } else {
             if (value == null) {
                 throw new IllegalArgumentException("Primitive array can't take null values.");
             }
-
-            if (value instanceof Boolean)
+            if (value instanceof Boolean) {
                 setBoolean(array, index, ((Boolean) value).booleanValue());
-            else if (value instanceof Byte)
+            } else if (value instanceof Byte) {
                 setByte(array, index, ((Byte) value).byteValue());
-            else if (value instanceof Character)
+            } else if (value instanceof Character) {
                 setChar(array, index, ((Character) value).charValue());
-            else if (value instanceof Short)
+            } else if (value instanceof Short) {
                 setShort(array, index, ((Short) value).shortValue());
-            else if (value instanceof Integer)
+            } else if (value instanceof Integer) {
                 setInt(array, index, ((Integer) value).intValue());
-            else if (value instanceof Long)
+            } else if (value instanceof Long) {
                 setLong(array, index, ((Long) value).longValue());
-            else if (value instanceof Float)
+            } else if (value instanceof Float) {
                 setFloat(array, index, ((Float) value).floatValue());
-            else if (value instanceof Double)
+            } else if (value instanceof Double) {
                 setDouble(array, index, ((Double) value).doubleValue());
+            }
         }
     }
 
     /**
-     * Sets the element of the array at the specified index to the {@code
-     * boolean} value. This reproduces the effect of {@code array[index] =
-     * value}.
+     * Sets {@code array[index] = value}. Applies to boolean arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if the {@code array} is not an array or the value cannot be
      *             converted to the array type by a widening conversion
@@ -565,212 +439,171 @@
         if (array instanceof boolean[]) {
             ((boolean[]) array)[index] = value;
         } else {
-            setByte(array, index, value ? (byte)1 : (byte)0);
+            throw badArray(array);
         }
     }
 
     /**
-     * Sets the element of the array at the specified index to the {@code byte}
-     * value. This reproduces the effect of {@code array[index] = value}.
+     * Sets {@code array[index] = value}. Applies to byte, double, float, int, long, and short arrays.
      *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
+     * @throws NullPointerException if {@code array == null}
      * @throws IllegalArgumentException
      *             if the {@code array} is not an array or the value cannot be
      *             converted to the array type by a widening conversion
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code  index < 0 || index >= array.length}
      */
-    public static void setByte(Object array, int index, byte value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+    public static void setByte(Object array, int index, byte value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof byte[]) {
             ((byte[]) array)[index] = value;
-        } else {
-            setShort(array, index, value);
-        }
-    }
-
-    /**
-     * Set the element of the array at the specified index to the {@code char}
-     * value. This reproduces the effect of {@code array[index] = value}.
-     *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
-     * @throws IllegalArgumentException
-     *             if the {@code array} is not an array or the value cannot be
-     *             converted to the array type by a widening conversion
-     * @throws ArrayIndexOutOfBoundsException
-     *             if {@code  index < 0 || index >= array.length}
-     */
-    public static void setChar(Object array, int index, char value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof char[]) {
-            ((char[]) array)[index] = value;
-        } else if (array == null) {
-            throw new NullPointerException();
-        } else if (!array.getClass().isArray()) {
-            throw new IllegalArgumentException("Not an array");
-        } else {
-            throw new IllegalArgumentException("Wrong array type");
-        }
-    }
-
-    /**
-     * Set the element of the array at the specified index to the {@code double}
-     * value. This reproduces the effect of {@code array[index] = value}.
-     *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
-     * @throws IllegalArgumentException
-     *             if the {@code array} is not an array or the value cannot be
-     *             converted to the array type by a widening conversion
-     * @throws ArrayIndexOutOfBoundsException
-     *             if {@code  index < 0 || index >= array.length}
-     */
-    public static void setDouble(Object array, int index, double value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof double[]) {
+        } else if (array instanceof double[]) {
             ((double[]) array)[index] = value;
-        } else if (array == null) {
-            throw new NullPointerException();
-        } else if (!array.getClass().isArray()) {
-            throw new IllegalArgumentException("Not an array");
-        } else {
-            throw new IllegalArgumentException("Wrong array type");
-        }
-    }
-
-    /**
-     * Set the element of the array at the specified index to the {@code float}
-     * value. This reproduces the effect of {@code array[index] = value}.
-     *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
-     * @throws IllegalArgumentException
-     *             if the {@code array} is not an array or the value cannot be
-     *             converted to the array type by a widening conversion
-     * @throws ArrayIndexOutOfBoundsException
-     *             if {@code  index < 0 || index >= array.length}
-     */
-    public static void setFloat(Object array, int index, float value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof float[]) {
+        } else if (array instanceof float[]) {
             ((float[]) array)[index] = value;
-        } else {
-            setDouble(array, index, value);
-        }
-    }
-
-    /**
-     * Set the element of the array at the specified index to the {@code int}
-     * value. This reproduces the effect of {@code array[index] = value}.
-     *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
-     * @throws IllegalArgumentException
-     *             if the {@code array} is not an array or the value cannot be
-     *             converted to the array type by a widening conversion
-     * @throws ArrayIndexOutOfBoundsException
-     *             if {@code  index < 0 || index >= array.length}
-     */
-    public static void setInt(Object array, int index, int value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof int[]) {
+        } else if (array instanceof int[]) {
             ((int[]) array)[index] = value;
-        } else {
-            setLong(array, index, value);
-        }
-    }
-
-    /**
-     * Set the element of the array at the specified index to the {@code long}
-     * value. This reproduces the effect of {@code array[index] = value}.
-     *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
-     * @throws IllegalArgumentException
-     *             if the {@code array} is not an array or the value cannot be
-     *             converted to the array type by a widening conversion
-     * @throws ArrayIndexOutOfBoundsException
-     *             if {@code  index < 0 || index >= array.length}
-     */
-    public static void setLong(Object array, int index, long value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof long[]) {
+        } else if (array instanceof long[]) {
             ((long[]) array)[index] = value;
-        } else {
-            setFloat(array, index, value);
-        }
-    }
-
-    /**
-     * Set the element of the array at the specified index to the {@code short}
-     * value. This reproduces the effect of {@code array[index] = value}.
-     *
-     * @param array
-     *            the array
-     * @param index
-     *            the index
-     * @param value
-     *            the new value
-     *
-     * @throws NullPointerException
-     *             if the {@code array} is {@code null}
-     * @throws IllegalArgumentException
-     *             if the {@code array} is not an array or the value cannot be
-     *             converted to the array type by a widening conversion
-     * @throws ArrayIndexOutOfBoundsException
-     *             if {@code  index < 0 || index >= array.length}
-     */
-    public static void setShort(Object array, int index, short value)
-            throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
-        if (array instanceof short[]) {
+        } else if (array instanceof short[]) {
             ((short[]) array)[index] = value;
         } else {
-            setInt(array, index, value);
+            throw badArray(array);
         }
     }
 
+    /**
+     * Sets {@code array[index] = value}. Applies to char, double, float, int, and long arrays.
+     *
+     * @throws NullPointerException if {@code array == null}
+     * @throws IllegalArgumentException
+     *             if the {@code array} is not an array or the value cannot be
+     *             converted to the array type by a widening conversion
+     * @throws ArrayIndexOutOfBoundsException
+     *             if {@code  index < 0 || index >= array.length}
+     */
+    public static void setChar(Object array, int index, char value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof char[]) {
+            ((char[]) array)[index] = value;
+        } else if (array instanceof double[]) {
+            ((double[]) array)[index] = value;
+        } else if (array instanceof float[]) {
+            ((float[]) array)[index] = value;
+        } else if (array instanceof int[]) {
+            ((int[]) array)[index] = value;
+        } else if (array instanceof long[]) {
+            ((long[]) array)[index] = value;
+        } else {
+            throw badArray(array);
+        }
+    }
+
+    /**
+     * Sets {@code array[index] = value}. Applies to double arrays.
+     *
+     * @throws NullPointerException if {@code array == null}
+     * @throws IllegalArgumentException
+     *             if the {@code array} is not an array or the value cannot be
+     *             converted to the array type by a widening conversion
+     * @throws ArrayIndexOutOfBoundsException
+     *             if {@code  index < 0 || index >= array.length}
+     */
+    public static void setDouble(Object array, int index, double value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof double[]) {
+            ((double[]) array)[index] = value;
+        } else {
+            throw badArray(array);
+        }
+    }
+
+    /**
+     * Sets {@code array[index] = value}. Applies to double and float arrays.
+     *
+     * @throws NullPointerException if {@code array == null}
+     * @throws IllegalArgumentException
+     *             if the {@code array} is not an array or the value cannot be
+     *             converted to the array type by a widening conversion
+     * @throws ArrayIndexOutOfBoundsException
+     *             if {@code  index < 0 || index >= array.length}
+     */
+    public static void setFloat(Object array, int index, float value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof float[]) {
+            ((float[]) array)[index] = value;
+        } else if (array instanceof double[]) {
+            ((double[]) array)[index] = value;
+        } else {
+            throw badArray(array);
+        }
+    }
+
+    /**
+     * Sets {@code array[index] = value}. Applies to double, float, int, and long arrays.
+     *
+     * @throws NullPointerException if {@code array == null}
+     * @throws IllegalArgumentException
+     *             if the {@code array} is not an array or the value cannot be
+     *             converted to the array type by a widening conversion
+     * @throws ArrayIndexOutOfBoundsException
+     *             if {@code  index < 0 || index >= array.length}
+     */
+    public static void setInt(Object array, int index, int value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof int[]) {
+            ((int[]) array)[index] = value;
+        } else if (array instanceof double[]) {
+            ((double[]) array)[index] = value;
+        } else if (array instanceof float[]) {
+            ((float[]) array)[index] = value;
+        } else if (array instanceof long[]) {
+            ((long[]) array)[index] = value;
+        } else {
+            throw badArray(array);
+        }
+    }
+
+    /**
+     * Sets {@code array[index] = value}. Applies to double, float, and long arrays.
+     *
+     * @throws NullPointerException if {@code array == null}
+     * @throws IllegalArgumentException
+     *             if the {@code array} is not an array or the value cannot be
+     *             converted to the array type by a widening conversion
+     * @throws ArrayIndexOutOfBoundsException
+     *             if {@code  index < 0 || index >= array.length}
+     */
+    public static void setLong(Object array, int index, long value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof long[]) {
+            ((long[]) array)[index] = value;
+        } else if (array instanceof double[]) {
+            ((double[]) array)[index] = value;
+        } else if (array instanceof float[]) {
+            ((float[]) array)[index] = value;
+        } else {
+            throw badArray(array);
+        }
+    }
+
+    /**
+     * Sets {@code array[index] = value}. Applies to double, float, int, long, and short arrays.
+     *
+     * @throws NullPointerException if {@code array == null}
+     * @throws IllegalArgumentException
+     *             if the {@code array} is not an array or the value cannot be
+     *             converted to the array type by a widening conversion
+     * @throws ArrayIndexOutOfBoundsException
+     *             if {@code  index < 0 || index >= array.length}
+     */
+    public static void setShort(Object array, int index, short value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        if (array instanceof short[]) {
+            ((short[]) array)[index] = value;
+        } else if (array instanceof double[]) {
+            ((double[]) array)[index] = value;
+        } else if (array instanceof float[]) {
+            ((float[]) array)[index] = value;
+        } else if (array instanceof int[]) {
+            ((int[]) array)[index] = value;
+        } else if (array instanceof long[]) {
+            ((long[]) array)[index] = value;
+        } else {
+            throw badArray(array);
+        }
+    }
 }
diff --git a/luni/src/main/java/java/lang/reflect/Constructor.java b/luni/src/main/java/java/lang/reflect/Constructor.java
index b03e28b..9a0e03c 100644
--- a/luni/src/main/java/java/lang/reflect/Constructor.java
+++ b/luni/src/main/java/java/lang/reflect/Constructor.java
@@ -142,7 +142,7 @@
             sb.append("> ");
         }
         // append constructor name
-        appendArrayType(sb, getDeclaringClass());
+        appendTypeName(sb, getDeclaringClass());
         // append parameters
         sb.append('(');
         appendArrayGenericType(sb,
diff --git a/luni/src/main/java/java/lang/reflect/Field.java b/luni/src/main/java/java/lang/reflect/Field.java
index 87c955a..0aacb11 100644
--- a/luni/src/main/java/java/lang/reflect/Field.java
+++ b/luni/src/main/java/java/lang/reflect/Field.java
@@ -869,9 +869,9 @@
         if (result.length() != 0) {
             result.append(' ');
         }
-        appendArrayType(result, type);
+        appendTypeName(result, type);
         result.append(' ');
-        result.append(declaringClass.getName());
+        appendTypeName(result, declaringClass);
         result.append('.');
         result.append(name);
         return result.toString();
diff --git a/luni/src/main/java/java/lang/reflect/Method.java b/luni/src/main/java/java/lang/reflect/Method.java
index 8a9c0f1..044dbff9 100644
--- a/luni/src/main/java/java/lang/reflect/Method.java
+++ b/luni/src/main/java/java/lang/reflect/Method.java
@@ -188,7 +188,7 @@
         appendGenericType(sb, Types.getType(genericReturnType));
         sb.append(' ');
         // append method name
-        appendArrayType(sb, getDeclaringClass());
+        appendTypeName(sb, getDeclaringClass());
         sb.append(".").append(getName());
         // append parameters
         sb.append('(');
diff --git a/luni/src/main/java/java/net/AddressCache.java b/luni/src/main/java/java/net/AddressCache.java
index 08f5d81..194761a 100644
--- a/luni/src/main/java/java/net/AddressCache.java
+++ b/luni/src/main/java/java/net/AddressCache.java
@@ -21,18 +21,20 @@
 /**
  * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative
  * cache entries.
+ *
+ * TODO: benchmark and optimize InetAddress until we get to the point where we can just rely on
+ * the C library level caching. The main thing caching at this level buys us is avoiding repeated
+ * conversions from 'struct sockaddr's to InetAddress[].
  */
 class AddressCache {
     /**
      * When the cache contains more entries than this, we start dropping the oldest ones.
      * This should be a power of two to avoid wasted space in our custom map.
      */
-    private static final int MAX_ENTRIES = 512;
+    private static final int MAX_ENTRIES = 16;
 
-    // Default time-to-live for positive cache entries. 600 seconds (10 minutes).
-    private static final long DEFAULT_POSITIVE_TTL_NANOS = 600 * 1000000000L;
-    // Default time-to-live for negative cache entries. 10 seconds.
-    private static final long DEFAULT_NEGATIVE_TTL_NANOS = 10 * 1000000000L;
+    // The TTL for the Java-level cache is short, just 2s.
+    private static final long TTL_NANOS = 2 * 1000000000L;
 
     // The actual cache.
     private final BasicLruCache<String, AddressCacheEntry> cache
@@ -47,13 +49,13 @@
          * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal
          * because -- unlike System.currentTimeMillis -- it can never go backwards.
          *
-         * Unless we need to cope with DNS TTLs of 292 years, we don't need to worry about overflow.
+         * We don't need to worry about overflow with a TTL_NANOS of 2s.
          */
         final long expiryNanos;
 
-        AddressCacheEntry(Object value, long expiryNanos) {
+        AddressCacheEntry(Object value) {
             this.value = value;
-            this.expiryNanos = expiryNanos;
+            this.expiryNanos = System.nanoTime() + TTL_NANOS;
         }
     }
 
@@ -85,28 +87,7 @@
      * certain length of time.
      */
     public void put(String hostname, InetAddress[] addresses) {
-        put(hostname, addresses, true);
-    }
-
-    /**
-     * Associates the given 'detailMessage' with 'hostname'. The association will expire after a
-     * certain length of time.
-     */
-    public void put(String hostname, String detailMessage) {
-        put(hostname, detailMessage, false);
-    }
-
-    /**
-     * Associates the given 'addresses' with 'hostname'. The association will expire after a
-     * certain length of time.
-     */
-    public void put(String hostname, Object value, boolean isPositive) {
-        // Calculate the expiry time.
-        String propertyName = isPositive ? "networkaddress.cache.ttl" : "networkaddress.cache.negative.ttl";
-        long defaultTtlNanos = isPositive ? DEFAULT_POSITIVE_TTL_NANOS : DEFAULT_NEGATIVE_TTL_NANOS;
-        long expiryNanos = System.nanoTime() + defaultTtlNanos;
-        // Update the cache.
-        cache.put(hostname, new AddressCacheEntry(value, expiryNanos));
+        cache.put(hostname, new AddressCacheEntry(addresses));
     }
 
     /**
@@ -114,26 +95,6 @@
      * negative cache entry.)
      */
     public void putUnknownHost(String hostname, String detailMessage) {
-        put(hostname, detailMessage);
-    }
-
-    private long customTtl(String propertyName, long defaultTtlNanos) {
-        String ttlString = System.getProperty(propertyName, null);
-        if (ttlString == null) {
-            return System.nanoTime() + defaultTtlNanos;
-        }
-        try {
-            long ttlS = Long.parseLong(ttlString);
-            // For the system properties, -1 means "cache forever" and 0 means "don't cache".
-            if (ttlS == -1) {
-                return Long.MAX_VALUE;
-            } else if (ttlS == 0) {
-                return Long.MIN_VALUE;
-            } else {
-                return System.nanoTime() + ttlS * 1000000000L;
-            }
-        } catch (NumberFormatException ex) {
-            return System.nanoTime() + defaultTtlNanos;
-        }
+        cache.put(hostname, new AddressCacheEntry(detailMessage));
     }
 }
diff --git a/luni/src/main/java/java/net/Authenticator.java b/luni/src/main/java/java/net/Authenticator.java
index fc66c89..a68784a 100644
--- a/luni/src/main/java/java/net/Authenticator.java
+++ b/luni/src/main/java/java/net/Authenticator.java
@@ -33,27 +33,13 @@
     // the default authenticator that needs to be set
     private static Authenticator thisAuthenticator;
 
-    private static final NetPermission requestPasswordAuthenticationPermission = new NetPermission(
-            "requestPasswordAuthentication");
-
-    private static final NetPermission setDefaultAuthenticatorPermission = new NetPermission(
-            "setDefaultAuthenticator");
-
-    // the requester connection info
     private String host;
-
     private InetAddress addr;
-
     private int port;
-
     private String protocol;
-
     private String prompt;
-
     private String scheme;
-
     private URL url;
-
     private RequestorType rt;
 
     /**
diff --git a/luni/src/main/java/java/net/ExtendedResponseCache.java b/luni/src/main/java/java/net/ExtendedResponseCache.java
new file mode 100644
index 0000000..a70e734
--- /dev/null
+++ b/luni/src/main/java/java/net/ExtendedResponseCache.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 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 java.net;
+
+/**
+ * A response cache that supports statistics tracking and updating stored
+ * responses. Implementations of {@link ResponseCache} should implement this
+ * interface to receive additional support from the HTTP engine.
+ *
+ * @hide
+ */
+public interface ExtendedResponseCache {
+
+    /*
+     * This hidden interface is defined in a non-hidden package (java.net) so
+     * its @hide tag will be parsed by Doclava. This hides this interface from
+     * implementing classes' documentation.
+     */
+
+    /**
+     * Track an HTTP response being satisfied by {@code source}.
+     * @hide
+     */
+    void trackResponse(ResponseSource source);
+
+    /**
+     * Track an conditional GET that was satisfied by this cache.
+     * @hide
+     */
+    void trackConditionalCacheHit();
+
+    /**
+     * Updates stored HTTP headers using a hit on a conditional GET.
+     * @hide
+     */
+    void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection);
+}
diff --git a/luni/src/main/java/java/net/HttpURLConnection.java b/luni/src/main/java/java/net/HttpURLConnection.java
index a54e11a..2023887 100644
--- a/luni/src/main/java/java/net/HttpURLConnection.java
+++ b/luni/src/main/java/java/net/HttpURLConnection.java
@@ -65,7 +65,6 @@
  * }</pre>
  *
  * <h3>Secure Communication with HTTPS</h3>
-
  * Calling {@link URL#openConnection()} on a URL with the "https"
  * scheme will return an {@code HttpsURLConnection}, which allows for
  * overriding the default {@link javax.net.ssl.HostnameVerifier
@@ -131,9 +130,9 @@
  * for multiple request/response pairs. As a result, HTTP connections may be
  * held open longer than necessary. Calls to {@link #disconnect()} may return
  * the socket to a pool of connected sockets. This behavior can be disabled by
- * setting the "http.keepAlive" system property to "false" before issuing any
- * HTTP requests. The "http.maxConnections" property may be used to control how
- * many idle connections to each server will be held.
+ * setting the {@code http.keepAlive} system property to {@code false} before
+ * issuing any HTTP requests. The {@code http.maxConnections} property may be
+ * used to control how many idle connections to each server will be held.
  *
  * <p>By default, this implementation of {@code HttpURLConnection} requests that
  * servers use gzip compression. Since {@link #getContentLength()} returns the
@@ -237,12 +236,22 @@
  * until a connection is established.
  *
  * <h3>Response Caching</h3>
- * <p>{@code HttpURLConnection} supports a VM-wide HTTP response cache.
- * Implement {@link ResponseCache} and use {@link ResponseCache#setDefault} to
- * install a custom cache. Implementing this API is onerous: correct
- * implementations should follow all caching rules defined by <a
- * href="http://tools.ietf.org/html/rfc2616#section-13">Section 13 of RFC
- * 2616</a>.
+ * Android 4.0 (Ice Cream Sandwich) includes a response cache. See {@code
+ * android.net.http.HttpResponseCache} for instructions on enabling HTTP caching
+ * in your application.
+ *
+ * <h3>Avoiding Bugs In Earlier Releases</h3>
+ * Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In
+ * particular, calling {@code close()} on a readable {@code InputStream} could
+ * <a href="http://code.google.com/p/android/issues/detail?id=2939">poison the
+ * connection pool</a>. Work around this by disabling connection pooling:
+ * <pre>   {@code
+ * private void disableConnectionReuseIfNecessary() {
+ *   // Work around pre-Froyo bugs in HTTP connection reuse.
+ *   if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
+ *     System.setProperty("http.keepAlive", "false");
+ *   }
+ * }}</pre>
  *
  * <p>Each instance of {@code HttpURLConnection} may be used for one
  * request/response pair. Instances of this class are not thread safe.
@@ -514,10 +523,13 @@
     }
 
     /**
-     * Closes the connection to the HTTP server.
+     * Releases this connection so that its resources may be either reused or
+     * closed.
      *
-     * @see URLConnection#connect()
-     * @see URLConnection#connected
+     * <p>Unlike other Java implementations, this will not necessarily close
+     * socket connections that can be reused. You can disable all connection
+     * reuse by setting the {@code http.keepAlive} system property to {@code
+     * false} before issuing any HTTP requests.
      */
     public abstract void disconnect();
 
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index c12a350..bbe34c2 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -116,15 +116,9 @@
  * brackets.
  *
  * <h4>DNS caching</h4>
- * <p>On Android, addresses are cached for 600 seconds (10 minutes) by default. Failed lookups are
- * cached for 10 seconds. The underlying C library or OS may cache for longer, but you can control
- * the Java-level caching with the usual {@code "networkaddress.cache.ttl"} and
- * {@code "networkaddress.cache.negative.ttl"} system properties. These are parsed as integer
- * numbers of seconds, where the special value 0 means "don't cache" and -1 means "cache forever".
- *
- * <p>Note also that on Android &ndash; unlike the RI &ndash; the cache is not unbounded. The
- * current implementation caches around 512 entries, removed on a least-recently-used basis.
- * (Obviously, you should not rely on these details.)
+ * <p>In Android 4.0 (Ice Cream Sandwich) and earlier, DNS caching was performed both by
+ * InetAddress and by the C library, which meant that DNS TTLs could not be honored correctly.
+ * In later releases, caching is done solely by the C library and DNS TTLs are honored.
  *
  * @see Inet4Address
  * @see Inet6Address
@@ -416,11 +410,15 @@
             addressCache.put(host, addresses);
             return addresses;
         } catch (GaiException gaiException) {
-            // TODO: bionic currently returns EAI_NODATA, which is indistinguishable from a real
-            // failure. We need to fix bionic before we can report a more useful error.
-            // if (gaiException.error == EAI_SYSTEM) {
-            //    throw new SecurityException("Permission denied (missing INTERNET permission?)");
-            // }
+            // If the failure appears to have been a lack of INTERNET permission, throw a clear
+            // SecurityException to aid in debugging this common mistake.
+            // http://code.google.com/p/android/issues/detail?id=15722
+            if (gaiException.getCause() instanceof ErrnoException) {
+                if (((ErrnoException) gaiException.getCause()).errno == EACCES) {
+                    throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException);
+                }
+            }
+            // Otherwise, throw an UnknownHostException.
             String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error);
             addressCache.putUnknownHost(host, detailMessage);
             throw gaiException.rethrowAsUnknownHostException(detailMessage);
@@ -447,13 +445,14 @@
     }
 
     /**
-     * Returns a string containing a concise, human-readable description of this
-     * IP address.
+     * Returns a string containing the host name (if available) and host address.
+     * For example: {@code "www.google.com/74.125.224.115"} or {@code "/127.0.0.1"}.
      *
-     * @return the description, as host/address.
+     * <p>IPv6 addresses may additionally include an interface name or scope id.
+     * For example: {@code "www.google.com/2001:4860:4001:803::1013%eth0"} or
+     * {@code "/2001:4860:4001:803::1013%2"}.
      */
-    @Override
-    public String toString() {
+    @Override public String toString() {
         return (hostName == null ? "" : hostName) + "/" + getHostAddress();
     }
 
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index 01b5301..49dcfc4 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -177,13 +177,11 @@
     }
 
     /**
-     * Gets a string representation of this socket included the address and the
-     * port number.
-     *
-     * @return the address and port number as a textual representation.
+     * Returns a string containing the address (or the hostname for an
+     * unresolved {@code InetSocketAddress}) and port number.
+     * For example: {@code "www.google.com/74.125.224.115:80"} or {@code "/127.0.0.1:80"}.
      */
-    @Override
-    public String toString() {
+    @Override public String toString() {
         return ((addr != null) ? addr.toString() : hostname) + ":" + port;
     }
 
diff --git a/luni/src/main/java/java/net/InterfaceAddress.java b/luni/src/main/java/java/net/InterfaceAddress.java
index 612eeb4..dea618c 100644
--- a/luni/src/main/java/java/net/InterfaceAddress.java
+++ b/luni/src/main/java/java/net/InterfaceAddress.java
@@ -96,13 +96,11 @@
     }
 
     /**
-     * Returns a string representation for this interface address.
-     * The string is of the form: InetAddress / prefix length [ broadcast address ].
-     *
-     * @return a string representation of this interface address.
+     * Returns a string containing this interface's address, prefix length, and broadcast address.
+     * For example: {@code "/172.18.103.112/23 [/172.18.103.255]"} or
+     * {@code "/0:0:0:0:0:0:0:1%1/128 [null]"}.
      */
-    @Override
-    public String toString() {
+    @Override public String toString() {
         return address + "/" + prefixLength + " [" + broadcastAddress + "]";
     }
 
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index c6d9af4..e06b811 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -266,34 +266,37 @@
     private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
         String[] interfaceNames = new File("/sys/class/net").list();
         NetworkInterface[] interfaces = new NetworkInterface[interfaceNames.length];
+        boolean[] done = new boolean[interfaces.length];
         for (int i = 0; i < interfaceNames.length; ++i) {
             interfaces[i] = NetworkInterface.getByName(interfaceNames[i]);
+            // http://b/5833739: getByName can return null if the interface went away between our
+            // readdir(2) and our stat(2), so mark interfaces that disappeared as 'done'.
+            if (interfaces[i] == null) {
+                done[i] = true;
+            }
         }
 
         List<NetworkInterface> result = new ArrayList<NetworkInterface>();
-        boolean[] peeked = new boolean[interfaces.length];
         for (int counter = 0; counter < interfaces.length; counter++) {
-            // If this interface has been touched, continue.
-            if (peeked[counter]) {
+            // If this interface has been dealt with already, continue.
+            if (done[counter]) {
                 continue;
             }
             int counter2 = counter;
             // Checks whether the following interfaces are children.
             for (; counter2 < interfaces.length; counter2++) {
-                if (peeked[counter2]) {
+                if (done[counter2]) {
                     continue;
                 }
                 if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) {
-                    // Tagged as peeked
-                    peeked[counter2] = true;
                     interfaces[counter].children.add(interfaces[counter2]);
                     interfaces[counter2].parent = interfaces[counter];
                     interfaces[counter].addresses.addAll(interfaces[counter2].addresses);
-                }
+                    done[counter2] = true;
+                  }
             }
-            // Tagged as peeked
             result.add(interfaces[counter]);
-            peeked[counter] = true;
+            done[counter] = true;
         }
         return result;
     }
@@ -334,6 +337,11 @@
         return name.hashCode();
     }
 
+    /**
+     * Returns a string containing details of this network interface.
+     * The exact format is deliberately unspecified. Callers that require a specific
+     * format should build a string themselves, using this class' accessor methods.
+     */
     @Override public String toString() {
         StringBuilder sb = new StringBuilder(25);
         sb.append("[");
diff --git a/luni/src/main/java/java/net/ProxySelector.java b/luni/src/main/java/java/net/ProxySelector.java
index 1166db7..816c70f 100644
--- a/luni/src/main/java/java/net/ProxySelector.java
+++ b/luni/src/main/java/java/net/ProxySelector.java
@@ -29,11 +29,11 @@
  * <tr class="TableHeadingColor"><th colspan="4">Hostname patterns</th></tr>
  * <tr><th>URL scheme</th><th>property name</th><th>description</th><th>default</th></tr>
  * <tr><td>ftp</td><td>ftp.nonProxyHosts</td><td>Hostname pattern for FTP servers to connect to
- *     directly (without a proxy).</td><td>*</td></tr>
+ *     directly (without a proxy).</td><td></td></tr>
  * <tr><td>http</td><td>http.nonProxyHosts</td><td>Hostname pattern for HTTP servers to connect to
- *     directly (without a proxy).</td><td>*</td></tr>
+ *     directly (without a proxy).</td><td></td></tr>
  * <tr><td>https</td><td>https.nonProxyHosts</td><td>Hostname pattern for HTTPS servers to connect
- *     to directly (without a proxy).</td><td>*</td></tr>
+ *     to directly (without a proxy).</td><td></td></tr>
  * <tr><td colspan="4"><br></td></tr>
  *
  * <tr class="TableHeadingColor"><th colspan="4">{@linkplain Proxy.Type#HTTP HTTP Proxies}</th></tr>
diff --git a/luni/src/main/java/libcore/net/http/ResponseSource.java b/luni/src/main/java/java/net/ResponseSource.java
similarity index 89%
rename from luni/src/main/java/libcore/net/http/ResponseSource.java
rename to luni/src/main/java/java/net/ResponseSource.java
index 731e37f..fb974e9 100644
--- a/luni/src/main/java/libcore/net/http/ResponseSource.java
+++ b/luni/src/main/java/java/net/ResponseSource.java
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
-package libcore.net.http;
+package java.net;
 
-enum ResponseSource {
+/**
+ * Where the HTTP client should look for a response.
+ *
+ * @hide
+ */
+public enum ResponseSource {
 
     /**
      * Return the response from the cache immediately.
diff --git a/luni/src/main/java/java/net/URL.java b/luni/src/main/java/java/net/URL.java
index b25d223..9d44498 100644
--- a/luni/src/main/java/java/net/URL.java
+++ b/luni/src/main/java/java/net/URL.java
@@ -340,8 +340,8 @@
      * devices. Two URLs could be equal on some networks and unequal on
      * others.</li>
      * </ul>
-     * <p>This problem is fixed in Android in the Ice Cream Sandwich release. In
-     * that release, URLs are only equal if their host names are equal (ignoring
+     * <p>This problem is fixed in Android 4.0 (Ice Cream Sandwich). In that
+     * release, URLs are only equal if their host names are equal (ignoring
      * case).
      */
     @Override public boolean equals(Object o) {
diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java
index a8d967c..7cf71d5 100644
--- a/luni/src/main/java/java/net/URLConnection.java
+++ b/luni/src/main/java/java/net/URLConnection.java
@@ -972,57 +972,54 @@
     }
 
     /**
-     * Sets the timeout value in milliseconds for establishing the connection to
-     * the resource pointed by this {@code URLConnection} instance. A {@code
-     * SocketTimeoutException} is thrown if the connection could not be
-     * established in this time. Default is {@code 0} which stands for an
-     * infinite timeout.
+     * Sets the maximum time to wait for a connect to complete before giving up.
+     * Connecting to a server will fail with a {@link SocketTimeoutException} if
+     * the timeout elapses before a connection is established. The default value
+     * of {@code 0} disables connect timeouts; connect attempts may wait
+     * indefinitely.
      *
-     * @param timeout
-     *            the connecting timeout in milliseconds.
-     * @throws IllegalArgumentException
-     *             if the parameter {@code timeout} is less than zero.
+     * <p><strong>Warning:</strong> if the hostname resolves to multiple IP
+     * addresses, this client will try each in <a
+     * href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order. If
+     * connecting to each of these addresses fails, multiple timeouts will
+     * elapse before the connect attempt throws an exception. Host names that
+     * support both IPv6 and IPv4 always have at least 2 IP addresses.
+     *
+     * @param timeoutMillis the connect timeout in milliseconds. Non-negative.
      */
-    public void setConnectTimeout(int timeout) {
-        if (timeout < 0) {
-            throw new IllegalArgumentException("timeout < 0");
+    public void setConnectTimeout(int timeoutMillis) {
+        if (timeoutMillis < 0) {
+            throw new IllegalArgumentException("timeoutMillis < 0");
         }
-        this.connectTimeout = timeout;
+        this.connectTimeout = timeoutMillis;
     }
 
     /**
-     * Returns the configured connecting timeout.
-     *
-     * @return the connecting timeout value in milliseconds.
+     * Returns the connect timeout in milliseconds, or {@code 0} if connect
+     * attempts never timeout.
      */
     public int getConnectTimeout() {
         return connectTimeout;
     }
 
     /**
-     * Sets the timeout value in milliseconds for reading from the input stream
-     * of an established connection to the resource. A {@code
-     * SocketTimeoutException} is thrown if the connection could not be
-     * established in this time. Default is {@code 0} which stands for an
-     * infinite timeout.
+     * Sets the maximum time to wait for an input stream read to complete before
+     * giving up. Reading will fail with a {@link SocketTimeoutException} if the
+     * timeout elapses before data becomes available. The default value of
+     * {@code 0} disables read timeouts; read attempts will block indefinitely.
      *
-     * @param timeout
-     *            the reading timeout in milliseconds.
-     * @throws IllegalArgumentException
-     *             if the parameter {@code timeout} is less than zero.
+     * @param timeoutMillis the read timeout in milliseconds. Non-negative.
      */
-    public void setReadTimeout(int timeout) {
-        if (timeout < 0) {
-            throw new IllegalArgumentException("timeout < 0");
+    public void setReadTimeout(int timeoutMillis) {
+        if (timeoutMillis < 0) {
+            throw new IllegalArgumentException("timeoutMillis < 0");
         }
-        this.readTimeout = timeout;
+        this.readTimeout = timeoutMillis;
     }
 
     /**
-     * Returns the configured timeout for reading from the input stream of an
-     * established connection to the resource.
-     *
-     * @return the reading timeout value in milliseconds.
+     * Returns the read timeout in milliseconds, or {@code 0} if reads never
+     * timeout.
      */
     public int getReadTimeout() {
         return readTimeout;
diff --git a/luni/src/main/java/java/net/URLDecoder.java b/luni/src/main/java/java/net/URLDecoder.java
index e34fe4a..175175d 100644
--- a/luni/src/main/java/java/net/URLDecoder.java
+++ b/luni/src/main/java/java/net/URLDecoder.java
@@ -42,7 +42,7 @@
      */
     @Deprecated
     public static String decode(String s) {
-        return UriCodec.decode(s, true, Charset.defaultCharset());
+        return UriCodec.decode(s, true, Charset.defaultCharset(), true);
     }
 
     /**
@@ -64,6 +64,6 @@
      *             if the specified encoding scheme is invalid.
      */
     public static String decode(String s, String encoding) throws UnsupportedEncodingException {
-        return UriCodec.decode(s, true, Charset.forName(encoding));
+        return UriCodec.decode(s, true, Charset.forName(encoding), true);
     }
 }
diff --git a/luni/src/main/java/java/nio/CharSequenceAdapter.java b/luni/src/main/java/java/nio/CharSequenceAdapter.java
index 3bed096..12bac31 100644
--- a/luni/src/main/java/java/nio/CharSequenceAdapter.java
+++ b/luni/src/main/java/java/nio/CharSequenceAdapter.java
@@ -102,18 +102,15 @@
         return ByteOrder.nativeOrder();
     }
 
-    @Override
-    protected char[] protectedArray() {
+    @Override char[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/CharToByteBufferAdapter.java b/luni/src/main/java/java/nio/CharToByteBufferAdapter.java
index a271274..b9100a2 100644
--- a/luni/src/main/java/java/nio/CharToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/CharToByteBufferAdapter.java
@@ -125,18 +125,15 @@
         return byteBuffer.order();
     }
 
-    @Override
-    protected char[] protectedArray() {
+    @Override char[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/DirectByteBuffer.java b/luni/src/main/java/java/nio/DirectByteBuffer.java
index ccc5c6b..0ddef14 100644
--- a/luni/src/main/java/java/nio/DirectByteBuffer.java
+++ b/luni/src/main/java/java/nio/DirectByteBuffer.java
@@ -205,7 +205,7 @@
         block.free();
     }
 
-    @Override protected byte[] protectedArray() {
+    @Override byte[] protectedArray() {
         byte[] array = this.block.array();
         if (array == null) {
             throw new UnsupportedOperationException();
@@ -213,12 +213,12 @@
         return array;
     }
 
-    @Override protected int protectedArrayOffset() {
-        protectedArray(); // Check we have an array.
+    @Override int protectedArrayOffset() {
+        protectedArray(); // Throw if we don't have an array.
         return offset;
     }
 
-    @Override protected boolean protectedHasArray() {
-        return protectedArray() != null;
+    @Override boolean protectedHasArray() {
+        return this.block.array() != null;
     }
 }
diff --git a/luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java b/luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java
index 9f7dda7..8b1e084 100644
--- a/luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java
@@ -125,18 +125,15 @@
         return byteBuffer.order();
     }
 
-    @Override
-    protected double[] protectedArray() {
+    @Override double[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/FloatToByteBufferAdapter.java b/luni/src/main/java/java/nio/FloatToByteBufferAdapter.java
index f674fe3..0ed944b 100644
--- a/luni/src/main/java/java/nio/FloatToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/FloatToByteBufferAdapter.java
@@ -124,18 +124,15 @@
         return byteBuffer.order();
     }
 
-    @Override
-    protected float[] protectedArray() {
+    @Override float[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/IntToByteBufferAdapter.java b/luni/src/main/java/java/nio/IntToByteBufferAdapter.java
index 1694e77..1af5f86 100644
--- a/luni/src/main/java/java/nio/IntToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/IntToByteBufferAdapter.java
@@ -125,18 +125,15 @@
         return byteBuffer.order();
     }
 
-    @Override
-    protected int[] protectedArray() {
+    @Override int[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/LongToByteBufferAdapter.java b/luni/src/main/java/java/nio/LongToByteBufferAdapter.java
index 5f51d96..e8bf8df 100644
--- a/luni/src/main/java/java/nio/LongToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/LongToByteBufferAdapter.java
@@ -125,18 +125,15 @@
         return byteBuffer.order();
     }
 
-    @Override
-    protected long[] protectedArray() {
+    @Override long[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/MappedByteBufferAdapter.java b/luni/src/main/java/java/nio/MappedByteBufferAdapter.java
index 73b2991..59cfe8e 100644
--- a/luni/src/main/java/java/nio/MappedByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/MappedByteBufferAdapter.java
@@ -360,18 +360,15 @@
         return result;
     }
 
-    @Override
-    byte[] protectedArray() {
+    @Override byte[] protectedArray() {
         return wrapped.protectedArray();
     }
 
-    @Override
-    int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return wrapped.protectedArrayOffset();
     }
 
-    @Override
-    boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return wrapped.protectedHasArray();
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java
index 6f30dbc..75e7b9b 100644
--- a/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected char[] protectedArray() {
+    @Override char[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java
index 460bbf4..9812789 100644
--- a/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected double[] protectedArray() {
+    @Override double[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java
index 775e1d8..3c74966 100644
--- a/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected float[] protectedArray() {
+    @Override float[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java b/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java
index 382794e..c5073b0 100644
--- a/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected byte[] protectedArray() {
+    @Override byte[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java
index a137b3b..ef73251 100644
--- a/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected int[] protectedArray() {
+    @Override int[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java
index 87b0c88..a28815d 100644
--- a/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected long[] protectedArray() {
+    @Override long[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java
index 07b9c90..075ff38 100644
--- a/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java
@@ -64,18 +64,15 @@
         return true;
     }
 
-    @Override
-    protected short[] protectedArray() {
+    @Override short[] protectedArray() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new ReadOnlyBufferException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java
index df125b3..58fc5ae 100644
--- a/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java
@@ -75,18 +75,15 @@
         return false;
     }
 
-    @Override
-    protected char[] protectedArray() {
+    @Override char[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java
index a2913ae..f9328d6 100644
--- a/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java
@@ -76,18 +76,15 @@
         return false;
     }
 
-    @Override
-    protected double[] protectedArray() {
+    @Override double[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java
index da1e406..eee3aa7 100644
--- a/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java
@@ -76,18 +76,15 @@
         return false;
     }
 
-    @Override
-    protected float[] protectedArray() {
+    @Override float[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java b/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java
index 5358a13..02296c6 100644
--- a/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java
@@ -77,18 +77,15 @@
         return false;
     }
 
-    @Override
-    protected byte[] protectedArray() {
+    @Override byte[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java
index 8ec60c0..e8e67c2 100644
--- a/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java
@@ -75,18 +75,15 @@
         return false;
     }
 
-    @Override
-    protected int[] protectedArray() {
+    @Override int[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java
index d02ec57..df5f09f 100644
--- a/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java
@@ -75,18 +75,15 @@
         return false;
     }
 
-    @Override
-    protected long[] protectedArray() {
+    @Override long[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java
index 6f16dae..2a43e91 100644
--- a/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java
@@ -77,18 +77,15 @@
         return false;
     }
 
-    @Override
-    protected short[] protectedArray() {
+    @Override short[] protectedArray() {
         return backingArray;
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         return offset;
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return true;
     }
 
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 786e05f..05a6497 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -170,7 +170,7 @@
                     synchronized (keysLock) {
                         preparePollFds();
                     }
-                    int rc;
+                    int rc = -1;
                     try {
                         if (isBlock) {
                             begin();
@@ -178,7 +178,9 @@
                         try {
                             rc = Libcore.os.poll(pollFds.array(), (int) timeout);
                         } catch (ErrnoException errnoException) {
-                            throw errnoException.rethrowAsIOException();
+                            if (errnoException.errno != EINTR) {
+                                throw errnoException.rethrowAsIOException();
+                            }
                         }
                     } finally {
                         if (isBlock) {
diff --git a/luni/src/main/java/java/nio/ShortToByteBufferAdapter.java b/luni/src/main/java/java/nio/ShortToByteBufferAdapter.java
index 3fa2a63..ee36709 100644
--- a/luni/src/main/java/java/nio/ShortToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ShortToByteBufferAdapter.java
@@ -124,18 +124,15 @@
         return byteBuffer.order();
     }
 
-    @Override
-    protected short[] protectedArray() {
+    @Override short[] protectedArray() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected int protectedArrayOffset() {
+    @Override int protectedArrayOffset() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected boolean protectedHasArray() {
+    @Override boolean protectedHasArray() {
         return false;
     }
 
diff --git a/luni/src/main/java/java/util/AbstractList.java b/luni/src/main/java/java/util/AbstractList.java
index 3b522c0..d35e543 100644
--- a/luni/src/main/java/java/util/AbstractList.java
+++ b/luni/src/main/java/java/util/AbstractList.java
@@ -398,7 +398,7 @@
      * @throws IllegalArgumentException
      *                if the object cannot be added to this List
      * @throws IndexOutOfBoundsException
-     *                if {@code location < 0 || >= size()}
+     *                if {@code location < 0 || location > size()}
      */
     public void add(int location, E object) {
         throw new UnsupportedOperationException();
@@ -443,7 +443,7 @@
      * @throws IllegalArgumentException
      *             if an object cannot be added to this list.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || > size()}
+     *             if {@code location < 0 || location > size()}
      */
     public boolean addAll(int location, Collection<? extends E> collection) {
         Iterator<? extends E> it = collection.iterator();
@@ -507,7 +507,7 @@
      *            the index of the element to return.
      * @return the element at the specified index.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location >= size()}
      */
     public abstract E get(int location);
 
@@ -632,7 +632,7 @@
      * @throws UnsupportedOperationException
      *             if removing from this list is not supported.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location >= size()}
      */
     public E remove(int location) {
         throw new UnsupportedOperationException();
@@ -675,7 +675,7 @@
      * @throws IllegalArgumentException
      *             if an object cannot be added to this list.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location >= size()}
      */
     public E set(int location, E object) {
         throw new UnsupportedOperationException();
diff --git a/luni/src/main/java/java/util/ArrayList.java b/luni/src/main/java/java/util/ArrayList.java
index 3797e15..dc7b198 100644
--- a/luni/src/main/java/java/util/ArrayList.java
+++ b/luni/src/main/java/java/util/ArrayList.java
@@ -134,7 +134,7 @@
      * @param object
      *            the object to add.
      * @throws IndexOutOfBoundsException
-     *             when {@code location < 0 || > size()}
+     *             when {@code location < 0 || location > size()}
      */
     @Override public void add(int index, E object) {
         Object[] a = array;
@@ -213,7 +213,7 @@
      * @return {@code true} if this {@code ArrayList} is modified, {@code false}
      *         otherwise.
      * @throws IndexOutOfBoundsException
-     *             when {@code location < 0 || > size()}
+     *             when {@code location < 0 || location > size()}
      */
     @Override
     public boolean addAll(int index, Collection<? extends E> collection) {
@@ -390,7 +390,7 @@
      *            the index of the object to remove.
      * @return the removed object.
      * @throws IndexOutOfBoundsException
-     *             when {@code location < 0 || >= size()}
+     *             when {@code location < 0 || location >= size()}
      */
     @Override public E remove(int index) {
         Object[] a = array;
@@ -469,7 +469,7 @@
      *            the object to add.
      * @return the previous element at the index.
      * @throws IndexOutOfBoundsException
-     *             when {@code location < 0 || >= size()}
+     *             when {@code location < 0 || location >= size()}
      */
     @Override public E set(int index, E object) {
         Object[] a = array;
diff --git a/luni/src/main/java/java/util/BitSet.java b/luni/src/main/java/java/util/BitSet.java
index a841315..a4ee4c1 100644
--- a/luni/src/main/java/java/util/BitSet.java
+++ b/luni/src/main/java/java/util/BitSet.java
@@ -602,7 +602,7 @@
         while (++arrayIndex < longCount && bits[arrayIndex] == ALL_ONES) {
         }
         if (arrayIndex == longCount) {
-            return size();
+            return 64 * longCount;
         }
         return 64 * arrayIndex + Long.numberOfTrailingZeros(~bits[arrayIndex]);
     }
diff --git a/luni/src/main/java/java/util/Currency.java b/luni/src/main/java/java/util/Currency.java
index 3b80e99..ceda24e 100644
--- a/luni/src/main/java/java/util/Currency.java
+++ b/luni/src/main/java/java/util/Currency.java
@@ -79,7 +79,7 @@
             String currencyCode = ICU.getCurrencyCode(country);
             if (currencyCode == null) {
                 throw new IllegalArgumentException("Unsupported ISO 3166 country: " + locale);
-            } else if (currencyCode.equals("None")) {
+            } else if (currencyCode.equals("XXX")) {
                 return null;
             }
             Currency result = getInstance(currencyCode);
diff --git a/luni/src/main/java/java/util/LinkedList.java b/luni/src/main/java/java/util/LinkedList.java
index a1dc8d8..325b16d 100644
--- a/luni/src/main/java/java/util/LinkedList.java
+++ b/luni/src/main/java/java/util/LinkedList.java
@@ -272,7 +272,7 @@
      * @param object
      *            the object to add.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location > size()}
      */
     @Override
     public void add(int location, E object) {
@@ -336,7 +336,7 @@
      * @throws IllegalArgumentException
      *             if an object cannot be added to this list.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || > size()}
+     *             if {@code location < 0 || location > size()}
      */
     @Override
     public boolean addAll(int location, Collection<? extends E> collection) {
@@ -619,7 +619,7 @@
      *            the index at which to start the iteration
      * @return a ListIterator on the elements of this {@code LinkedList}
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location > size()}
      * @see ListIterator
      */
     @Override
@@ -634,7 +634,7 @@
      *            the index of the object to remove
      * @return the removed object
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location >= size()}
      */
     @Override
     public E remove(int location) {
@@ -855,7 +855,7 @@
      * @throws IllegalArgumentException
      *             if an object cannot be added to this list.
      * @throws IndexOutOfBoundsException
-     *             if {@code location < 0 || >= size()}
+     *             if {@code location < 0 || location >= size()}
      */
     @Override
     public E set(int location, E object) {
diff --git a/luni/src/main/java/java/util/List.java b/luni/src/main/java/java/util/List.java
index c27ca3f..8a9e1e3 100644
--- a/luni/src/main/java/java/util/List.java
+++ b/luni/src/main/java/java/util/List.java
@@ -84,7 +84,7 @@
      * @throws IllegalArgumentException
      *                if an object cannot be added to this {@code List}.
      * @throws IndexOutOfBoundsException
-     *                if {@code location < 0 || > size()}
+     *                if {@code location < 0 || location > size()}
      */
     public boolean addAll(int location, Collection<? extends E> collection);
 
@@ -159,7 +159,7 @@
      *            the index of the element to return.
      * @return the element at the specified location.
      * @throws IndexOutOfBoundsException
-     *                if {@code location < 0 || >= size()}
+     *                if {@code location < 0 || location >= size()}
      */
     public E get(int location);
 
@@ -244,7 +244,7 @@
      * @throws UnsupportedOperationException
      *                if removing from this {@code List} is not supported.
      * @throws IndexOutOfBoundsException
-     *                if {@code location < 0 || >= size()}
+     *                if {@code location < 0 || location >= size()}
      */
     public E remove(int location);
 
@@ -301,7 +301,7 @@
      * @throws IllegalArgumentException
      *                if an object cannot be added to this {@code List}.
      * @throws IndexOutOfBoundsException
-     *                if {@code location < 0 || >= size()}
+     *                if {@code location < 0 || location >= size()}
      */
     public E set(int location, E object);
 
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index 74126cc..6b20a1c 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -69,7 +69,8 @@
  * <tr><td>cupcake/donut/eclair</td> <td>ICU 3.8</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-5">CLDR 1.5</a></td> <td><a href="http://www.unicode.org/versions/Unicode5.0.0/">Unicode 5.0</a></td></tr>
  * <tr><td>froyo</td>                <td>ICU 4.2</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-7">CLDR 1.7</a></td> <td><a href="http://www.unicode.org/versions/Unicode5.1.0/">Unicode 5.1</a></td></tr>
  * <tr><td>gingerbread/honeycomb</td><td>ICU 4.4</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-8">CLDR 1.8</a></td> <td><a href="http://www.unicode.org/versions/Unicode5.2.0/">Unicode 5.2</a></td></tr>
- * <tr><td>ice cream sandwich</td><td>ICU 4.6</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-9">CLDR 1.9</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
+ * <tr><td>ice cream sandwich</td>   <td>ICU 4.6</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-9">CLDR 1.9</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
+ * <tr><td>jelly bean</td>           <td>ICU 4.8</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-2-0">CLDR 2.0</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
  * </table>
  *
  * <a name="default_locale"><h3>Be wary of the default locale</h3></a>
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index e7253f7..969e164 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -269,7 +269,8 @@
     public abstract int getRawOffset();
 
     /**
-     * Returns a {@code TimeZone} corresponding to the given {@code id}, or {@code GMT} on failure.
+     * Returns a {@code TimeZone} corresponding to the given {@code id}, or {@code GMT}
+     * for unknown ids.
      *
      * <p>An ID can be an Olson name of the form <i>Area</i>/<i>Location</i>, such
      * as {@code America/Los_Angeles}. The {@link #getAvailableIDs} method returns
@@ -287,6 +288,9 @@
      * zone IDs used in Java 1.1.
      */
     public static synchronized TimeZone getTimeZone(String id) {
+        if (id == null) {
+            throw new NullPointerException("id == null");
+        }
         TimeZone zone = ZoneInfoDB.getTimeZone(id);
         if (zone != null) {
             return zone;
diff --git a/luni/src/main/java/java/util/TreeMap.java b/luni/src/main/java/java/util/TreeMap.java
index 7809110..9e19933 100644
--- a/luni/src/main/java/java/util/TreeMap.java
+++ b/luni/src/main/java/java/util/TreeMap.java
@@ -132,7 +132,7 @@
      *
      * <p>The constructed map <strong>will always use</strong> {@code
      * copyFrom}'s ordering. Because the {@code TreeMap} constructor overloads
-     * are ambigous, prefer to construct a map and populate it in two steps:
+     * are ambiguous, prefer to construct a map and populate it in two steps:
      * <pre>   {@code
      *   TreeMap<String, Integer> customOrderedMap
      *       = new TreeMap<String, Integer>(copyFrom.comparator());
@@ -1364,11 +1364,12 @@
         }
 
         public Comparator<? super K> comparator() {
-          if (ascending) {
-            return TreeMap.this.comparator();
-          } else {
-            return Collections.reverseOrder(comparator);
-          }
+            Comparator<? super K> forward = TreeMap.this.comparator();
+            if (ascending) {
+                return forward;
+            } else {
+                return Collections.reverseOrder(forward);
+            }
         }
 
         /*
@@ -1665,7 +1666,7 @@
     private static final long serialVersionUID = 919286545866124006L;
 
     private void writeObject(ObjectOutputStream stream) throws IOException {
-        stream.putFields().put("comparator", comparator != NATURAL_ORDER ? comparator : null);
+        stream.putFields().put("comparator", comparator());
         stream.writeFields();
         stream.writeInt(size);
         for (Map.Entry<K, V> entry : entrySet()) {
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index e576d85..363f57e 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -318,7 +318,7 @@
          */
         long scanOffset = mRaf.length() - ENDHDR;
         if (scanOffset < 0) {
-            throw new ZipException("too short to be Zip");
+            throw new ZipException("File too short to be a zip file: " + mRaf.length());
         }
 
         long stopOffset = scanOffset - 65536;
@@ -346,10 +346,10 @@
 
         // Pull out the information we need.
         BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN);
-        short diskNumber = it.readShort();
-        short diskWithCentralDir = it.readShort();
-        short numEntries = it.readShort();
-        short totalNumEntries = it.readShort();
+        int diskNumber = it.readShort() & 0xffff;
+        int diskWithCentralDir = it.readShort() & 0xffff;
+        int numEntries = it.readShort() & 0xffff;
+        int totalNumEntries = it.readShort() & 0xffff;
         it.skip(4); // Ignore centralDirSize.
         int centralDirOffset = it.readInt();
 
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index c9f25c0..d082fc7 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -227,6 +227,7 @@
             return null;
         }
 
+        // Read the signature to see whether there's another local file header.
         Streams.readFully(in, hdrBuf, 0, 4);
         int hdr = Memory.peekInt(hdrBuf, 0, ByteOrder.LITTLE_ENDIAN);
         if (hdr == CENSIG) {
@@ -237,28 +238,28 @@
             return null;
         }
 
-        // Read the local header
+        // Read the local file header.
         Streams.readFully(in, hdrBuf, 0, (LOCHDR - LOCVER));
-        int version = Memory.peekShort(hdrBuf, 0, ByteOrder.LITTLE_ENDIAN) & 0xff;
+        int version = peekShort(0) & 0xff;
         if (version > ZIPLocalHeaderVersionNeeded) {
             throw new ZipException("Cannot read local header version " + version);
         }
-        short flags = Memory.peekShort(hdrBuf, LOCFLG - LOCVER, ByteOrder.LITTLE_ENDIAN);
+        int flags = peekShort(LOCFLG - LOCVER);
         hasDD = ((flags & ZipFile.GPBF_DATA_DESCRIPTOR_FLAG) != 0);
-        int ceTime = Memory.peekShort(hdrBuf, LOCTIM - LOCVER, ByteOrder.LITTLE_ENDIAN) & 0xffff;
-        int ceModDate = Memory.peekShort(hdrBuf, LOCTIM - LOCVER + 2, ByteOrder.LITTLE_ENDIAN) & 0xffff;
-        int ceCompressionMethod = Memory.peekShort(hdrBuf, LOCHOW - LOCVER, ByteOrder.LITTLE_ENDIAN) & 0xffff;
+        int ceLastModifiedTime = peekShort(LOCTIM - LOCVER);
+        int ceLastModifiedDate = peekShort(LOCTIM - LOCVER + 2);
+        int ceCompressionMethod = peekShort(LOCHOW - LOCVER);
         long ceCrc = 0, ceCompressedSize = 0, ceSize = -1;
         if (!hasDD) {
             ceCrc = ((long) Memory.peekInt(hdrBuf, LOCCRC - LOCVER, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
             ceCompressedSize = ((long) Memory.peekInt(hdrBuf, LOCSIZ - LOCVER, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
             ceSize = ((long) Memory.peekInt(hdrBuf, LOCLEN - LOCVER, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
         }
-        int nameLength = Memory.peekShort(hdrBuf, LOCNAM - LOCVER, ByteOrder.LITTLE_ENDIAN) & 0xffff;
+        int nameLength = peekShort(LOCNAM - LOCVER);
         if (nameLength == 0) {
             throw new ZipException("Entry is not named");
         }
-        int extraLength = Memory.peekShort(hdrBuf, LOCEXT - LOCVER, ByteOrder.LITTLE_ENDIAN) & 0xffff;
+        int extraLength = peekShort(LOCEXT - LOCVER);
 
         if (nameLength > nameBuf.length) {
             nameBuf = new byte[nameLength];
@@ -268,8 +269,8 @@
         }
         Streams.readFully(in, nameBuf, 0, nameLength);
         currentEntry = createZipEntry(ModifiedUtf8.decode(nameBuf, charBuf, 0, nameLength));
-        currentEntry.time = ceTime;
-        currentEntry.modDate = ceModDate;
+        currentEntry.time = ceLastModifiedTime;
+        currentEntry.modDate = ceLastModifiedDate;
         currentEntry.setMethod(ceCompressionMethod);
         if (ceSize != -1) {
             currentEntry.setCrc(ceCrc);
@@ -284,6 +285,10 @@
         return currentEntry;
     }
 
+    private int peekShort(int offset) {
+        return Memory.peekShort(hdrBuf, offset, ByteOrder.LITTLE_ENDIAN) & 0xffff;
+    }
+
     /**
      * Reads up to the specified number of uncompressed bytes into the buffer
      * starting at the offset.
diff --git a/luni/src/main/java/java/util/zip/ZipOutputStream.java b/luni/src/main/java/java/util/zip/ZipOutputStream.java
index 624847a..9a56fa2 100644
--- a/luni/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipOutputStream.java
@@ -21,8 +21,8 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.Charsets;
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 
 /**
  * This class provides an implementation of {@code FilterOutputStream} that
@@ -78,7 +78,7 @@
 
     private String comment;
 
-    private final ArrayList<String> entries = new ArrayList<String>();
+    private final HashSet<String> entries = new HashSet<String>();
 
     private int compressMethod = DEFLATED;
 
@@ -284,6 +284,10 @@
         if (entries.contains(ze.name)) {
             throw new ZipException("Entry already exists: " + ze.name);
         }
+        if (entries.size() == 64*1024-1) {
+            // TODO: support Zip64.
+            throw new ZipException("Too many entries for the zip file format's 16-bit entry count");
+        }
         nameBytes = ze.name.getBytes(Charsets.UTF_8);
         nameLength = nameBytes.length;
         if (nameLength > 0xffff) {
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index c8cb601..1dacd46 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -886,17 +886,25 @@
         if (input == null) {
             throw new IllegalArgumentException("input == null");
         }
-        if (inputOffset < 0 || inputLen < 0
-                || inputLen > input.length
-                || inputOffset > input.length - inputLen) {
-            throw new IllegalArgumentException("Incorrect inputOffset/inputLen parameters");
-        }
+        checkInputOffsetAndCount(input.length, inputOffset, inputLen);
         if (input.length == 0) {
             return null;
         }
         return spiImpl.engineUpdate(input, inputOffset, inputLen);
     }
 
+    private static void checkInputOffsetAndCount(int inputArrayLength,
+                                                 int inputOffset,
+                                                 int inputLen) {
+        if ((inputOffset | inputLen) < 0
+                || inputOffset > inputArrayLength
+                || inputArrayLength - inputOffset < inputLen) {
+            throw new IllegalArgumentException("input.length=" + inputArrayLength
+                                               + "; inputOffset=" + inputOffset
+                                               + "; inputLen=" + inputLen);
+        }
+    }
+
     /**
      * Continues a multi-part transformation (encryption or decryption). The
      * transformed bytes are stored in the {@code output} buffer.
@@ -972,12 +980,9 @@
             throw new IllegalArgumentException("output == null");
         }
         if (outputOffset < 0) {
-            throw new IllegalArgumentException("outputOffset < 0");
+            throw new IllegalArgumentException("outputOffset < 0. outputOffset=" + outputOffset);
         }
-        if (inputOffset < 0 || inputLen < 0 || inputLen > input.length
-                || inputOffset > input.length - inputLen) {
-            throw new IllegalArgumentException("Incorrect inputOffset/inputLen parameters");
-        }
+        checkInputOffsetAndCount(input.length, inputOffset, inputLen);
         if (input.length == 0) {
             return 0;
         }
@@ -1075,7 +1080,7 @@
             throw new IllegalStateException();
         }
         if (outputOffset < 0) {
-            throw new IllegalArgumentException("outputOffset < 0");
+            throw new IllegalArgumentException("outputOffset < 0. outputOffset=" + outputOffset);
         }
         return spiImpl.engineDoFinal(null, 0, 0, output, outputOffset);
     }
@@ -1137,9 +1142,7 @@
         if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
             throw new IllegalStateException();
         }
-        if (inputOffset < 0 || inputLen < 0 || inputOffset + inputLen > input.length) {
-            throw new IllegalArgumentException("Incorrect inputOffset/inputLen parameters");
-        }
+        checkInputOffsetAndCount(input.length, inputOffset, inputLen);
         return spiImpl.engineDoFinal(input, inputOffset, inputLen);
     }
 
@@ -1217,9 +1220,7 @@
         if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
             throw new IllegalStateException();
         }
-        if (inputOffset < 0 || inputLen < 0 || inputOffset + inputLen > input.length) {
-            throw new IllegalArgumentException("Incorrect inputOffset/inputLen parameters");
-        }
+        checkInputOffsetAndCount(input.length, inputOffset, inputLen);
         return spiImpl.engineDoFinal(input, inputOffset, inputLen, output,
                 outputOffset);
     }
diff --git a/luni/src/main/java/javax/crypto/CipherInputStream.java b/luni/src/main/java/javax/crypto/CipherInputStream.java
index 4de37db..39dcfda 100644
--- a/luni/src/main/java/javax/crypto/CipherInputStream.java
+++ b/luni/src/main/java/javax/crypto/CipherInputStream.java
@@ -39,8 +39,9 @@
 
     private final Cipher cipher;
     private final byte[] inputBuffer = new byte[I_BUFFER_SIZE];
-    private int index; // index of the bytes to return from outputBuffer
     private byte[] outputBuffer;
+    private int outputIndex; // index of the first byte to return from outputBuffer
+    private int outputLength; // count of the bytes to return from outputBuffer
     private boolean finished;
 
     /**
@@ -84,27 +85,35 @@
     @Override
     public int read() throws IOException {
         if (finished) {
-            return ((outputBuffer == null) || (index == outputBuffer.length))
-                            ? -1
-                            : outputBuffer[index++] & 0xFF;
+            return (outputIndex == outputLength) ? -1 : outputBuffer[outputIndex++] & 0xFF;
         }
-        if ((outputBuffer != null) && (index < outputBuffer.length)) {
-            return outputBuffer[index++] & 0xFF;
+        if (outputIndex < outputLength) {
+            return outputBuffer[outputIndex++] & 0xFF;
         }
-        index = 0;
-        outputBuffer = null;
-        int byteCount;
-        while (outputBuffer == null) {
-            if ((byteCount = in.read(inputBuffer)) == -1) {
+        outputIndex = 0;
+        outputLength = 0;
+        while (outputLength == 0) {
+            // check output size on each iteration since pending state
+            // in the cipher can cause this to vary from call to call
+            int outputSize = cipher.getOutputSize(inputBuffer.length);
+            if ((outputBuffer == null) || (outputBuffer.length < outputSize)) {
+                this.outputBuffer = new byte[outputSize];
+            }
+            int byteCount = in.read(inputBuffer);
+            if (byteCount == -1) {
                 try {
-                    outputBuffer = cipher.doFinal();
+                    outputLength = cipher.doFinal(outputBuffer, 0);
                 } catch (Exception e) {
                     throw new IOException(e.getMessage());
                 }
                 finished = true;
                 break;
             }
-            outputBuffer = cipher.update(inputBuffer, 0, byteCount);
+            try {
+                outputLength = cipher.update(inputBuffer, 0, byteCount, outputBuffer, 0);
+            } catch (ShortBufferException e) {
+                throw new AssertionError(e);  // should not happen since we sized with getOutputSize
+            }
         }
         return read();
     }
@@ -113,10 +122,10 @@
      * Reads the next {@code len} bytes from this input stream into buffer
      * {@code buf} starting at offset {@code off}.
      * <p>
-     * if {@code b} is {@code null}, the next {@code len} bytes are read and
+     * if {@code buf} is {@code null}, the next {@code len} bytes are read and
      * discarded.
      *
-     * @return the number of bytes filled into buffer {@code b}, or {@code -1}
+     * @return the number of bytes filled into buffer {@code buf}, or {@code -1}
      *         of the of the stream is reached.
      * @throws IOException
      *             if an error occurs.
diff --git a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
index 2fed280..e68baca 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
@@ -22,180 +22,147 @@
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import javax.security.auth.x500.X500Principal;
 
 /**
- * A HostnameVerifier that works the same way as Curl and Firefox.
+ * A HostnameVerifier consistent with <a
+ * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>.
  *
- * <p>The hostname must match either the first CN, or any of the subject-alts.
- * A wildcard can occur in the CN, and in any of the subject-alts.
- *
- * @author Julius Davies
+ * @hide accessible via HttpsURLConnection.getDefaultHostnameVerifier()
  */
-class DefaultHostnameVerifier implements HostnameVerifier {
-
-    /**
-     * This contains a list of 2nd-level domains that aren't allowed to
-     * have wildcards when combined with country-codes.
-     * For example: [*.co.uk].
-     *
-     * <p>The [*.co.uk] problem is an interesting one.  Should we just hope
-     * that CA's would never foolishly allow such a certificate to happen?
-     * Looks like we're the only implementation guarding against this.
-     * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
-     */
-    private static final String[] BAD_COUNTRY_2LDS =
-          { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
-            "lg", "ne", "net", "or", "org" };
-
-    static {
-        // Just in case developer forgot to manually sort the array.  :-)
-        Arrays.sort(BAD_COUNTRY_2LDS);
-    }
+public final class DefaultHostnameVerifier implements HostnameVerifier {
+    private static final int ALT_DNS_NAME = 2;
+    private static final int ALT_IPA_NAME = 7;
 
     public final boolean verify(String host, SSLSession session) {
-        Certificate[] certs;
         try {
-            certs = session.getPeerCertificates();
+            Certificate[] certificates = session.getPeerCertificates();
+            return verify(host, (X509Certificate) certificates[0]);
         } catch (SSLException e) {
             return false;
         }
+    }
 
-        X509Certificate x509 = (X509Certificate) certs[0];
+    public boolean verify(String host, X509Certificate certificate) {
+        return InetAddress.isNumeric(host)
+                ? verifyIpAddress(host, certificate)
+                : verifyHostName(host, certificate);
+    }
 
-        // We can be case-insensitive when comparing the host we used to
-        // establish the socket to the hostname in the certificate.
-        String hostName = host.trim().toLowerCase(Locale.ENGLISH);
+    /**
+     * Returns true if {@code certificate} matches {@code ipAddress}.
+     */
+    private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) {
+        for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) {
+            if (ipAddress.equalsIgnoreCase(altName)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-        // Verify the first CN provided. Other CNs are ignored. Firefox, wget,
-        // curl, and Sun Java work this way.
-        String firstCn = getFirstCn(x509);
-        if (matches(hostName, firstCn)) {
-            return true;
+    /**
+     * Returns true if {@code certificate} matches {@code hostName}.
+     */
+    private boolean verifyHostName(String hostName, X509Certificate certificate) {
+        hostName = hostName.toLowerCase(Locale.US);
+        boolean hasDns = false;
+        for (String altName : getSubjectAltNames(certificate, ALT_DNS_NAME)) {
+            hasDns = true;
+            if (verifyHostName(hostName, altName)) {
+                return true;
+            }
         }
 
-        for (String cn : getDNSSubjectAlts(x509)) {
-            if (matches(hostName, cn)) {
-                return true;
+        if (!hasDns) {
+            X500Principal principal = certificate.getSubjectX500Principal();
+            String cn = new DistinguishedNameParser(principal).find("cn");
+            if (cn != null) {
+                return verifyHostName(hostName, cn);
             }
         }
 
         return false;
     }
 
+    private List<String> getSubjectAltNames(X509Certificate certificate, int type) {
+        List<String> result = new ArrayList<String>();
+        try {
+            Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
+            if (subjectAltNames == null) {
+                return Collections.emptyList();
+            }
+            for (Object subjectAltName : subjectAltNames) {
+                List<?> entry = (List<?>) subjectAltName;
+                if (entry == null || entry.size() < 2) {
+                    continue;
+                }
+                Integer altNameType = (Integer) entry.get(0);
+                if (altNameType == null) {
+                    continue;
+                }
+                if (altNameType == type) {
+                    String altName = (String) entry.get(1);
+                    if (altName != null) {
+                        result.add(altName);
+                    }
+                }
+            }
+            return result;
+        } catch (CertificateParsingException e) {
+            return Collections.emptyList();
+        }
+    }
+
     /**
-     * Returns true if {@code hostname} matches {@code cn}.
+     * Returns true if {@code hostName} matches the name or pattern {@code cn}.
      *
-     * @param hostName a trimmed, lowercase hostname to verify
-     * @param cn a certificate CN or DNS subject alt. Either a literal name or
-     *     a wildcard of the form "*.google.com".
+     * @param hostName lowercase host name.
+     * @param cn certificate host name. May include wildcards like
+     *     {@code *.android.com}.
      */
-    private boolean matches(String hostName, String cn) {
-        if (cn == null) {
+    public boolean verifyHostName(String hostName, String cn) {
+        if (hostName == null || hostName.isEmpty() || cn == null || cn.isEmpty()) {
             return false;
         }
 
-        // Don't trim the CN, though!
-        cn = cn.toLowerCase(Locale.ENGLISH);
+        cn = cn.toLowerCase(Locale.US);
 
-        if (cn.startsWith("*.")) {
-            // When a wildcard matches, also check that the wildcard is legit
-            //   - Wildcards must contain at least two dots: "*.google.com"
-            //   - Wildcards must be for private domains. No "*.co.uk" etc.
-            //   - Wildcards must not match IP addresses: "*.8.8"
-            int matchLength = cn.length() - 1;
-            return hostName.regionMatches(hostName.length() - matchLength, cn, 1, matchLength)
-                    && cn.indexOf('.', 2) != -1
-                    && acceptableCountryWildcard(cn)
-                    && !InetAddress.isNumeric(hostName);
-        } else {
+        if (!cn.contains("*")) {
             return hostName.equals(cn);
         }
-    }
 
-    private boolean acceptableCountryWildcard(String cn) {
-        int cnLen = cn.length();
-        if (cnLen >= 7 && cnLen <= 9) {
-            // Look for the '.' in the 3rd-last position:
-            if (cn.charAt(cnLen - 3) == '.') {
-                // Trim off the [*.] and the [.XX].
-                String s = cn.substring(2, cnLen - 3);
-                // And test against the sorted array of bad 2lds:
-                int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
-                return x < 0;
+        if (cn.startsWith("*.") && hostName.regionMatches(0, cn, 2, cn.length() - 2)) {
+            return true; // "*.foo.com" matches "foo.com"
+        }
+
+        int asterisk = cn.indexOf('*');
+        int dot = cn.indexOf('.');
+        if (asterisk > dot) {
+            return false; // malformed; wildcard must be in the first part of the cn
+        }
+
+        if (!hostName.regionMatches(0, cn, 0, asterisk)) {
+            return false; // prefix before '*' doesn't match
+        }
+
+        int suffixLength = cn.length() - (asterisk + 1);
+        int suffixStart = hostName.length() - suffixLength;
+        if (hostName.indexOf('.', asterisk) < suffixStart) {
+            // TODO: remove workaround for *.clients.google.com http://b/5426333
+            if (!hostName.endsWith(".clients.google.com")) {
+                return false; // wildcard '*' can't match a '.'
             }
         }
+
+        if (!hostName.regionMatches(suffixStart, cn, asterisk + 1, suffixLength)) {
+            return false; // suffix after '*' doesn't match
+        }
+
         return true;
     }
-
-    private String getFirstCn(X509Certificate cert) {
-        /*
-         * Sebastian Hauer's original StrictSSLProtocolSocketFactory used
-         * getName() and had the following comment:
-         *
-         *      Parses a X.500 distinguished name for the value of the
-         *     "Common Name" field.  This is done a bit sloppy right
-         *     now and should probably be done a bit more according to
-         *     <code>RFC 2253</code>.
-         *
-         * I've noticed that toString() seems to do a better job than
-         * getName() on these X500Principal objects, so I'm hoping that
-         * addresses Sebastian's concern.
-         *
-         * For example, getName() gives me this:
-         * 1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
-         *
-         * whereas toString() gives me this:
-         * EMAILADDRESS=juliusdavies@cucbc.com
-         *
-         * Looks like toString() even works with non-ascii domain names!
-         * I tested it with "&#x82b1;&#x5b50;.co.jp" and it worked fine.
-         */
-        String subjectPrincipal = cert.getSubjectX500Principal().toString();
-        for (String token : subjectPrincipal.split(",")) {
-            int x = token.indexOf("CN=");
-            if (x >= 0) {
-                return token.substring(x + 3);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns all SubjectAlt DNS names from an X509Certificate.
-     *
-     * <p>Note: Java doesn't appear able to extract international characters
-     * from the SubjectAlts.  It can only extract international characters
-     * from the CN field.
-     *
-     * <p>(Or maybe the version of OpenSSL I'm using to test isn't storing the
-     * international characters correctly in the SubjectAlts?).
-     */
-    private List<String> getDNSSubjectAlts(X509Certificate cert) {
-        Collection<List<?>> subjectAlternativeNames;
-        try {
-            subjectAlternativeNames = cert.getSubjectAlternativeNames();
-        } catch (CertificateParsingException cpe) {
-            System.logI("Error parsing certificate", cpe);
-            return Collections.emptyList();
-        }
-
-        if (subjectAlternativeNames == null) {
-            return Collections.emptyList();
-        }
-
-        List<String> subjectAltList = new ArrayList<String>();
-        for (List<?> pair : subjectAlternativeNames) {
-            int type = (Integer) pair.get(0);
-            // If type is 2, then we've got a dNSName
-            if (type == 2) {
-                subjectAltList.add((String) pair.get(1));
-            }
-        }
-        return subjectAltList;
-    }
 }
diff --git a/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java b/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java
new file mode 100644
index 0000000..fa8ed1b
--- /dev/null
+++ b/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java
@@ -0,0 +1,403 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package javax.net.ssl;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A distinguished name (DN) parser. This parser only supports extracting a
+ * string value from a DN. It doesn't support values in the hex-string style.
+ *
+ * @hide
+ */
+public final class DistinguishedNameParser {
+    private final String dn;
+    private final int length;
+    private int pos;
+    private int beg;
+    private int end;
+
+    /** tmp vars to store positions of the currently parsed item */
+    private int cur;
+
+    /** distinguished name chars */
+    private char[] chars;
+
+    public DistinguishedNameParser(X500Principal principal) {
+        this.dn = principal.getName(X500Principal.RFC2253);
+        this.length = this.dn.length();
+    }
+
+    // gets next attribute type: (ALPHA 1*keychar) / oid
+    private String nextAT() {
+        // skip preceding space chars, they can present after
+        // comma or semicolon (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+        if (pos == length) {
+            return null; // reached the end of DN
+        }
+
+        // mark the beginning of attribute type
+        beg = pos;
+
+        // attribute type chars
+        pos++;
+        for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) {
+            // we don't follow exact BNF syntax here:
+            // accept any char except space and '='
+        }
+        if (pos >= length) {
+            throw new IllegalStateException("Unexpected end of DN: " + dn);
+        }
+
+        // mark the end of attribute type
+        end = pos;
+
+        // skip trailing space chars between attribute type and '='
+        // (compatibility with RFC 1779)
+        if (chars[pos] == ' ') {
+            for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) {
+            }
+
+            if (chars[pos] != '=' || pos == length) {
+                throw new IllegalStateException("Unexpected end of DN: " + dn);
+            }
+        }
+
+        pos++; //skip '=' char
+
+        // skip space chars between '=' and attribute value
+        // (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+
+        // in case of oid attribute type skip its prefix: "oid." or "OID."
+        // (compatibility with RFC 1779)
+        if ((end - beg > 4) && (chars[beg + 3] == '.')
+                && (chars[beg] == 'O' || chars[beg] == 'o')
+                && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i')
+                && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) {
+            beg += 4;
+        }
+
+        return new String(chars, beg, end - beg);
+    }
+
+    // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION
+    private String quotedAV() {
+        pos++;
+        beg = pos;
+        end = beg;
+        while (true) {
+
+            if (pos == length) {
+                throw new IllegalStateException("Unexpected end of DN: " + dn);
+            }
+
+            if (chars[pos] == '"') {
+                // enclosing quotation was found
+                pos++;
+                break;
+            } else if (chars[pos] == '\\') {
+                chars[end] = getEscaped();
+            } else {
+                // shift char: required for string with escaped chars
+                chars[end] = chars[pos];
+            }
+            pos++;
+            end++;
+        }
+
+        // skip trailing space chars before comma or semicolon.
+        // (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+
+        return new String(chars, beg, end - beg);
+    }
+
+    // gets hex string attribute value: "#" hexstring
+    private String hexAV() {
+        if (pos + 4 >= length) {
+            // encoded byte array  must be not less then 4 c
+            throw new IllegalStateException("Unexpected end of DN: " + dn);
+        }
+
+        beg = pos; // store '#' position
+        pos++;
+        while (true) {
+
+            // check for end of attribute value
+            // looks for space and component separators
+            if (pos == length || chars[pos] == '+' || chars[pos] == ','
+                    || chars[pos] == ';') {
+                end = pos;
+                break;
+            }
+
+            if (chars[pos] == ' ') {
+                end = pos;
+                pos++;
+                // skip trailing space chars before comma or semicolon.
+                // (compatibility with RFC 1779)
+                for (; pos < length && chars[pos] == ' '; pos++) {
+                }
+                break;
+            } else if (chars[pos] >= 'A' && chars[pos] <= 'F') {
+                chars[pos] += 32; //to low case
+            }
+
+            pos++;
+        }
+
+        // verify length of hex string
+        // encoded byte array  must be not less then 4 and must be even number
+        int hexLen = end - beg; // skip first '#' char
+        if (hexLen < 5 || (hexLen & 1) == 0) {
+            throw new IllegalStateException("Unexpected end of DN: " + dn);
+        }
+
+        // get byte encoding from string representation
+        byte[] encoded = new byte[hexLen / 2];
+        for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
+            encoded[i] = (byte) getByte(p);
+        }
+
+        return new String(chars, beg, hexLen);
+    }
+
+    // gets string attribute value: *( stringchar / pair )
+    private String escapedAV() {
+        beg = pos;
+        end = pos;
+        while (true) {
+            if (pos >= length) {
+                // the end of DN has been found
+                return new String(chars, beg, end - beg);
+            }
+
+            switch (chars[pos]) {
+            case '+':
+            case ',':
+            case ';':
+                // separator char has beed found
+                return new String(chars, beg, end - beg);
+            case '\\':
+                // escaped char
+                chars[end++] = getEscaped();
+                pos++;
+                break;
+            case ' ':
+                // need to figure out whether space defines
+                // the end of attribute value or not
+                cur = end;
+
+                pos++;
+                chars[end++] = ' ';
+
+                for (; pos < length && chars[pos] == ' '; pos++) {
+                    chars[end++] = ' ';
+                }
+                if (pos == length || chars[pos] == ',' || chars[pos] == '+'
+                        || chars[pos] == ';') {
+                    // separator char or the end of DN has beed found
+                    return new String(chars, beg, cur - beg);
+                }
+                break;
+            default:
+                chars[end++] = chars[pos];
+                pos++;
+            }
+        }
+    }
+
+    // returns escaped char
+    private char getEscaped() {
+        pos++;
+        if (pos == length) {
+            throw new IllegalStateException("Unexpected end of DN: " + dn);
+        }
+
+        switch (chars[pos]) {
+        case '"':
+        case '\\':
+        case ',':
+        case '=':
+        case '+':
+        case '<':
+        case '>':
+        case '#':
+        case ';':
+        case ' ':
+        case '*':
+        case '%':
+        case '_':
+            //FIXME: escaping is allowed only for leading or trailing space char
+            return chars[pos];
+        default:
+            // RFC doesn't explicitly say that escaped hex pair is
+            // interpreted as UTF-8 char. It only contains an example of such DN.
+            return getUTF8();
+        }
+    }
+
+    // decodes UTF-8 char
+    // see http://www.unicode.org for UTF-8 bit distribution table
+    private char getUTF8() {
+        int res = getByte(pos);
+        pos++; //FIXME tmp
+
+        if (res < 128) { // one byte: 0-7F
+            return (char) res;
+        } else if (res >= 192 && res <= 247) {
+
+            int count;
+            if (res <= 223) { // two bytes: C0-DF
+                count = 1;
+                res = res & 0x1F;
+            } else if (res <= 239) { // three bytes: E0-EF
+                count = 2;
+                res = res & 0x0F;
+            } else { // four bytes: F0-F7
+                count = 3;
+                res = res & 0x07;
+            }
+
+            int b;
+            for (int i = 0; i < count; i++) {
+                pos++;
+                if (pos == length || chars[pos] != '\\') {
+                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+                }
+                pos++;
+
+                b = getByte(pos);
+                pos++; //FIXME tmp
+                if ((b & 0xC0) != 0x80) {
+                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+                }
+
+                res = (res << 6) + (b & 0x3F);
+            }
+            return (char) res;
+        } else {
+            return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+        }
+    }
+
+    // Returns byte representation of a char pair
+    // The char pair is composed of DN char in
+    // specified 'position' and the next char
+    // According to BNF syntax:
+    // hexchar    = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
+    //                    / "a" / "b" / "c" / "d" / "e" / "f"
+    private int getByte(int position) {
+        if (position + 1 >= length) {
+            throw new IllegalStateException("Malformed DN: " + dn);
+        }
+
+        int b1, b2;
+
+        b1 = chars[position];
+        if (b1 >= '0' && b1 <= '9') {
+            b1 = b1 - '0';
+        } else if (b1 >= 'a' && b1 <= 'f') {
+            b1 = b1 - 87; // 87 = 'a' - 10
+        } else if (b1 >= 'A' && b1 <= 'F') {
+            b1 = b1 - 55; // 55 = 'A' - 10
+        } else {
+            throw new IllegalStateException("Malformed DN: " + dn);
+        }
+
+        b2 = chars[position + 1];
+        if (b2 >= '0' && b2 <= '9') {
+            b2 = b2 - '0';
+        } else if (b2 >= 'a' && b2 <= 'f') {
+            b2 = b2 - 87; // 87 = 'a' - 10
+        } else if (b2 >= 'A' && b2 <= 'F') {
+            b2 = b2 - 55; // 55 = 'A' - 10
+        } else {
+            throw new IllegalStateException("Malformed DN: " + dn);
+        }
+
+        return (b1 << 4) + b2;
+    }
+
+    /**
+     * Parses the DN and returns the attribute value for an attribute type.
+     *
+     * @param attributeType attribute type to look for (e.g. "ca")
+     * @return value of the attribute that first found, or null if none found
+     */
+    public String find(String attributeType) {
+        // Initialize internal state.
+        pos = 0;
+        beg = 0;
+        end = 0;
+        cur = 0;
+        chars = dn.toCharArray();
+
+        String attType = nextAT();
+        if (attType == null) {
+            return null;
+        }
+        while (true) {
+            String attValue = "";
+
+            if (pos == length) {
+                return null;
+            }
+
+            switch (chars[pos]) {
+            case '"':
+                attValue = quotedAV();
+                break;
+            case '#':
+                attValue = hexAV();
+                break;
+            case '+':
+            case ',':
+            case ';': // compatibility with RFC 1779: semicolon can separate RDNs
+                //empty attribute value
+                break;
+            default:
+                attValue = escapedAV();
+            }
+
+            if (attributeType.equalsIgnoreCase(attType)) {
+                return attValue;
+            }
+
+            if (pos >= length) {
+                return null;
+            }
+
+            if (chars[pos] == ',' || chars[pos] == ';') {
+            } else if (chars[pos] != '+') {
+                throw new IllegalStateException("Malformed DN: " + dn);
+            }
+
+            pos++;
+            attType = nextAT();
+            if (attType == null) {
+                throw new IllegalStateException("Malformed DN: " + dn);
+            }
+        }
+    }
+}
diff --git a/luni/src/main/java/javax/net/ssl/SSLSocket.java b/luni/src/main/java/javax/net/ssl/SSLSocket.java
index bc80b24..5049f81 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -24,7 +24,7 @@
 
 /**
  * The extension of {@code Socket} providing secure protocols like SSL (Secure
- * Socket Layer") or TLS (Transport Layer Security).
+ * Sockets Layer) or TLS (Transport Layer Security).
  */
 public abstract class SSLSocket extends Socket {
 
diff --git a/luni/src/main/java/javax/net/ssl/package.html b/luni/src/main/java/javax/net/ssl/package.html
index 14753c8..ea6a701 100644
--- a/luni/src/main/java/javax/net/ssl/package.html
+++ b/luni/src/main/java/javax/net/ssl/package.html
@@ -5,15 +5,19 @@
 <html>
 <body>
 <p>
-This package provides all the classes and interfaces needed to implement and program the Secure Socket
-abstraction based on the SSL protocol SSSLv3.0 or TLSv1.2.
-All the details of the SSL handshake protocol are accounted for, and a client or a server can specify the cipher 
-set to use.
-
-X.509 certificates are verified, and, if desired, the client and the server each have the option of verifying
-the entire certificate chain until the root Certificate Authority is reached.
-
-Android uses code from The Legion of the Bouncy Castle (http://www.bouncycastle.org) and OpenSSL (http://openssl.org).
+This package provides classes and interfaces needed to use the Secure
+Sockets Layer (SSL) protocol and the successor Transport Layer
+Security (TLS) protocol.  The API allows for both client and server
+sockets, the selection of desired SSL and TLS protocol versions, and
+the selection of desired cipher suites. The {@link
+javax.net.ssl.X509TrustManager X509TrustManager} interface allows
+customization of certificate chain verification. The
+{@link javax.net.ssl.X509KeyManager X509KeyManager} interface and
+{@link javax.net.ssl.X509ExtendedKeyManager X509ExtendedKeyManager}
+class allows the specification of a server's required certificate or a
+client's optional client certificate. Android uses code
+from <a href="http://www.bouncycastle.org">The Legion of the Bouncy
+Castle</a> and <a href="http://openssl.org">OpenSSL</a>.
 
 </p>
 </body>
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index b68730a..cb9c880 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -108,7 +108,7 @@
                 return localeData;
             }
         }
-        LocaleData newLocaleData = makeLocaleData(locale);
+        LocaleData newLocaleData = initLocaleData(locale);
         synchronized (localeDataCache) {
             LocaleData localeData = localeDataCache.get(localeName);
             if (localeData != null) {
@@ -119,24 +119,6 @@
         }
     }
 
-    private static LocaleData makeLocaleData(Locale locale) {
-        String language = locale.getLanguage();
-        String country = locale.getCountry();
-        String variant = locale.getVariant();
-        // Start with data from the parent (next-most-specific) locale...
-        LocaleData result = new LocaleData();
-        if (!variant.isEmpty()) {
-            result.overrideWithDataFrom(get(new Locale(language, country, "")));
-        } else if (!country.isEmpty()) {
-            result.overrideWithDataFrom(get(new Locale(language, "", "")));
-        } else if (!language.isEmpty()) {
-            result.overrideWithDataFrom(get(Locale.ROOT));
-        }
-        // Override with data from this locale.
-        result.overrideWithDataFrom(initLocaleData(locale));
-        return result;
-    }
-
     @Override public String toString() {
         return "LocaleData[" +
                 "firstDayOfWeek=" + firstDayOfWeek + "," +
@@ -178,120 +160,6 @@
                 "percentPattern=" + percentPattern + "]";
     }
 
-    private void overrideWithDataFrom(LocaleData overrides) {
-        if (overrides.firstDayOfWeek != null) {
-            firstDayOfWeek = overrides.firstDayOfWeek;
-        }
-        if (overrides.minimalDaysInFirstWeek != null) {
-            minimalDaysInFirstWeek = overrides.minimalDaysInFirstWeek;
-        }
-        if (overrides.amPm != null) {
-            amPm = overrides.amPm;
-        }
-        if (overrides.eras != null) {
-            eras = overrides.eras;
-        }
-        if (overrides.longMonthNames != null) {
-            longMonthNames = overrides.longMonthNames;
-        }
-        if (overrides.shortMonthNames != null) {
-            shortMonthNames = overrides.shortMonthNames;
-        }
-        if (overrides.longStandAloneMonthNames != null) {
-            longStandAloneMonthNames = overrides.longStandAloneMonthNames;
-        }
-        if (overrides.shortStandAloneMonthNames != null) {
-            shortStandAloneMonthNames = overrides.shortStandAloneMonthNames;
-        }
-        if (overrides.longWeekdayNames != null) {
-            longWeekdayNames = overrides.longWeekdayNames;
-        }
-        if (overrides.shortWeekdayNames != null) {
-            shortWeekdayNames = overrides.shortWeekdayNames;
-        }
-        if (overrides.longStandAloneWeekdayNames != null) {
-            longStandAloneWeekdayNames = overrides.longStandAloneWeekdayNames;
-        }
-        if (overrides.shortStandAloneWeekdayNames != null) {
-            shortStandAloneWeekdayNames = overrides.shortStandAloneWeekdayNames;
-        }
-        if (overrides.fullTimeFormat != null) {
-            fullTimeFormat = overrides.fullTimeFormat;
-        }
-        if (overrides.longTimeFormat != null) {
-            longTimeFormat = overrides.longTimeFormat;
-        }
-        if (overrides.mediumTimeFormat != null) {
-            mediumTimeFormat = overrides.mediumTimeFormat;
-        }
-        if (overrides.shortTimeFormat != null) {
-            shortTimeFormat = overrides.shortTimeFormat;
-        }
-        if (overrides.fullDateFormat != null) {
-            fullDateFormat = overrides.fullDateFormat;
-        }
-        if (overrides.longDateFormat != null) {
-            longDateFormat = overrides.longDateFormat;
-        }
-        if (overrides.mediumDateFormat != null) {
-            mediumDateFormat = overrides.mediumDateFormat;
-        }
-        if (overrides.shortDateFormat != null) {
-            shortDateFormat = overrides.shortDateFormat;
-        }
-        if (overrides.zeroDigit != '\0') {
-            zeroDigit = overrides.zeroDigit;
-        }
-        if (overrides.decimalSeparator != '\0') {
-            decimalSeparator = overrides.decimalSeparator;
-        }
-        if (overrides.groupingSeparator != '\0') {
-            groupingSeparator = overrides.groupingSeparator;
-        }
-        if (overrides.patternSeparator != '\0') {
-            patternSeparator = overrides.patternSeparator;
-        }
-        if (overrides.percent != '\0') {
-            percent = overrides.percent;
-        }
-        if (overrides.perMill != '\0') {
-            perMill = overrides.perMill;
-        }
-        if (overrides.monetarySeparator != '\0') {
-            monetarySeparator = overrides.monetarySeparator;
-        }
-        if (overrides.minusSign != '\0') {
-            minusSign = overrides.minusSign;
-        }
-        if (overrides.exponentSeparator != null) {
-            exponentSeparator = overrides.exponentSeparator;
-        }
-        if (overrides.NaN != null) {
-            NaN = overrides.NaN;
-        }
-        if (overrides.infinity != null) {
-            infinity = overrides.infinity;
-        }
-        if (overrides.currencySymbol != null) {
-            currencySymbol = overrides.currencySymbol;
-        }
-        if (overrides.internationalCurrencySymbol != null) {
-            internationalCurrencySymbol = overrides.internationalCurrencySymbol;
-        }
-        if (overrides.numberPattern != null) {
-            numberPattern = overrides.numberPattern;
-        }
-        if (overrides.integerPattern != null) {
-            integerPattern = overrides.integerPattern;
-        }
-        if (overrides.currencyPattern != null) {
-            currencyPattern = overrides.currencyPattern;
-        }
-        if (overrides.percentPattern != null) {
-            percentPattern = overrides.percentPattern;
-        }
-    }
-
     public String getDateFormat(int style) {
         switch (style) {
         case DateFormat.SHORT:
diff --git a/luni/src/main/java/libcore/io/DiskLruCache.java b/luni/src/main/java/libcore/io/DiskLruCache.java
index ecc1302..4699766 100644
--- a/luni/src/main/java/libcore/io/DiskLruCache.java
+++ b/luni/src/main/java/libcore/io/DiskLruCache.java
@@ -69,7 +69,7 @@
  *     <li>When an entry is being <strong>created</strong> it is necessary to
  *         supply a full set of values; the empty value should be used as a
  *         placeholder if necessary.
- *     <li>When an entry is being <strong>created</strong>, it is not necessary
+ *     <li>When an entry is being <strong>edited</strong>, it is not necessary
  *         to supply data for every value; values default to their previous
  *         value.
  * </ul>
@@ -92,6 +92,7 @@
     static final String JOURNAL_FILE_TMP = "journal.tmp";
     static final String MAGIC = "libcore.io.DiskLruCache";
     static final String VERSION_1 = "1";
+    static final long ANY_SEQUENCE_NUMBER = -1;
     private static final String CLEAN = "CLEAN";
     private static final String DIRTY = "DIRTY";
     private static final String REMOVE = "REMOVE";
@@ -149,6 +150,13 @@
             = new LinkedHashMap<String, Entry>(0, 0.75f, true);
     private int redundantOpCount;
 
+    /**
+     * To differentiate between old and current snapshots, each entry is given
+     * a sequence number each time an edit is committed. A snapshot is stale if
+     * its sequence number is not equal to its entry's sequence number.
+     */
+    private long nextSequenceNumber = 0;
+
     /** This cache uses a single background thread to evict entries. */
     private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
             60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
@@ -382,22 +390,30 @@
             executorService.submit(cleanupCallable);
         }
 
-        return new Snapshot(ins);
+        return new Snapshot(key, entry.sequenceNumber, ins);
     }
 
     /**
-     * Returns an editor for the entry named {@code key}, or null if it cannot
-     * currently be edited.
+     * Returns an editor for the entry named {@code key}, or null if another
+     * edit is in progress.
      */
-    public synchronized Editor edit(String key) throws IOException {
+    public Editor edit(String key) throws IOException {
+        return edit(key, ANY_SEQUENCE_NUMBER);
+    }
+
+    private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
         checkNotClosed();
         validateKey(key);
         Entry entry = lruEntries.get(key);
+        if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
+                && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
+            return null; // snapshot is stale
+        }
         if (entry == null) {
             entry = new Entry(key);
             lruEntries.put(key, entry);
         } else if (entry.currentEditor != null) {
-            return null;
+            return null; // another edit is in progress
         }
 
         Editor editor = new Editor(entry);
@@ -470,6 +486,9 @@
         if (entry.readable | success) {
             entry.readable = true;
             journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+            if (success) {
+                entry.sequenceNumber = nextSequenceNumber++;
+            }
         } else {
             lruEntries.remove(entry.key);
             journalWriter.write(REMOVE + ' ' + entry.key + '\n');
@@ -594,14 +613,27 @@
     /**
      * A snapshot of the values for an entry.
      */
-    public static final class Snapshot implements Closeable {
+    public final class Snapshot implements Closeable {
+        private final String key;
+        private final long sequenceNumber;
         private final InputStream[] ins;
 
-        private Snapshot(InputStream[] ins) {
+        private Snapshot(String key, long sequenceNumber, InputStream[] ins) {
+            this.key = key;
+            this.sequenceNumber = sequenceNumber;
             this.ins = ins;
         }
 
         /**
+         * Returns an editor for this snapshot's entry, or null if either the
+         * entry has changed since this snapshot was created or if another edit
+         * is in progress.
+         */
+        public Editor edit() throws IOException {
+            return DiskLruCache.this.edit(key, sequenceNumber);
+        }
+
+        /**
          * Returns the unbuffered stream with the value for {@code index}.
          */
         public InputStream getInputStream(int index) {
@@ -759,6 +791,9 @@
         /** The ongoing edit or null if this entry is not being edited. */
         private Editor currentEditor;
 
+        /** The sequence number of the most recently committed edit to this entry. */
+        private long sequenceNumber;
+
         private Entry(String key) {
             this.key = key;
             this.lengths = new long[valueCount];
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index f95e8ec..2d60a86 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -128,32 +128,39 @@
             return true;
         }
 
-        // With a timeout, we set the socket to non-blocking, connect(2), and then loop
-        // using poll(2) to decide whether we're connected, whether we should keep waiting,
-        // or whether we've seen a permanent failure and should give up.
-        long finishTimeMs = System.currentTimeMillis() + timeoutMs;
+        // For connect with a timeout, we:
+        //   1. set the socket to non-blocking,
+        //   2. connect(2),
+        //   3. loop using poll(2) to decide whether we're connected, whether we should keep
+        //      waiting, or whether we've seen a permanent failure and should give up,
+        //   4. set the socket back to blocking.
+
+        // 1. set the socket to non-blocking.
         IoUtils.setBlocking(fd, false);
+
+        // 2. call connect(2) non-blocking.
+        long finishTimeMs = System.currentTimeMillis() + timeoutMs;
         try {
-            try {
-                Libcore.os.connect(fd, inetAddress, port);
-                return true; // We connected immediately.
-            } catch (ErrnoException errnoException) {
-                if (errnoException.errno != EINPROGRESS) {
-                    throw errnoException;
-                }
-                // EINPROGRESS means we should keep trying...
+            Libcore.os.connect(fd, inetAddress, port);
+            IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
+            return true; // We connected immediately.
+        } catch (ErrnoException errnoException) {
+            if (errnoException.errno != EINPROGRESS) {
+                throw errnoException;
             }
-            int remainingTimeoutMs;
-            do {
-                remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
-                if (remainingTimeoutMs <= 0) {
-                    throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
-                }
-            } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
-            return true; // Or we'd have thrown.
-        } finally {
-            IoUtils.setBlocking(fd, true);
+            // EINPROGRESS means we should keep trying...
         }
+
+        // 3. loop using poll(2).
+        int remainingTimeoutMs;
+        do {
+            remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
+            if (remainingTimeoutMs <= 0) {
+                throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
+            }
+        } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
+        IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
+        return true; // Or we'd have thrown.
     }
 
     private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
@@ -200,6 +207,9 @@
             }
             throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
         } catch (ErrnoException errnoException) {
+            if (!fd.valid()) {
+                throw new SocketException("Socket closed");
+            }
             if (errnoException.errno == EINTR) {
                 return false; // Punt and ask the caller to try again.
             } else {
@@ -209,8 +219,8 @@
         // TODO: is it really helpful/necessary to throw so many different exceptions?
         String detail = connectDetail(inetAddress, port, timeoutMs, cause);
         if (cause.errno == ECONNRESET || cause.errno == ECONNREFUSED ||
-        cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE ||
-        cause.errno == ENETUNREACH) {
+                cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE ||
+                cause.errno == ENETUNREACH) {
             throw new ConnectException(detail, cause);
         } else if (cause.errno == EACCES) {
             throw new SecurityException(detail, cause);
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 4681bbc..6ea0baf 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -179,6 +179,7 @@
         add("application/x-object", "o");
         add("application/x-oz-application", "oza");
         add("application/x-pkcs12", "p12");
+        add("application/x-pkcs12", "pfx");
         add("application/x-pkcs7-certreqresp", "p7r");
         add("application/x-pkcs7-crl", "crl");
         add("application/x-quicktimeplayer", "qtl");
diff --git a/luni/src/main/java/libcore/net/RawSocket.java b/luni/src/main/java/libcore/net/RawSocket.java
index bb29fb0..08a7d09 100644
--- a/luni/src/main/java/libcore/net/RawSocket.java
+++ b/luni/src/main/java/libcore/net/RawSocket.java
@@ -32,35 +32,50 @@
  * @hide
  */
 public class RawSocket implements Closeable {
-    private static native void create(FileDescriptor fd, String interfaceName)
+    /**
+     * Ethernet IP protocol type, part of the L2 header of IP packets.
+     */
+    public static final short ETH_P_IP = (short) 0x0800;
+
+    /**
+     * Ethernet ARP protocol type, part of the L2 header of ARP packets.
+     */
+    public static final short ETH_P_ARP = (short) 0x0806;
+
+    private static native void create(FileDescriptor fd, short
+            protocolType, String interfaceName)
             throws SocketException;
     private static native int sendPacket(FileDescriptor fd,
-        String interfaceName, byte[] destMac, byte[] packet, int offset,
-        int byteCount);
+        String interfaceName, short protocolType, byte[] destMac, byte[] packet,
+        int offset, int byteCount);
     private static native int recvPacket(FileDescriptor fd, byte[] packet,
         int offset, int byteCount, int destPort, int timeoutMillis);
 
     private final FileDescriptor fd;
     private final String mInterfaceName;
+    private final short mProtocolType;
     private final CloseGuard guard = CloseGuard.get();
 
     /**
      * Creates a socket on the specified interface.
      */
-    public RawSocket(String interfaceName) throws SocketException {
+    public RawSocket(String interfaceName, short protocolType)
+        throws SocketException {
         mInterfaceName = interfaceName;
+        mProtocolType = protocolType;
         fd = new FileDescriptor();
-        create(fd, mInterfaceName);
+        create(fd, mProtocolType, mInterfaceName);
         guard.open("close");
     }
 
     /**
      * Reads a raw packet into the specified buffer, with the
-     * specified timeout.  Packets not destined for the desired UDP
-     * port are discarded.  Returns the length actually read.  No
-     * indication of overflow is signaled.  The packet data will start
-     * at the IP header (EthernetII dest/source/type headers are
-     * removed).
+     * specified timeout.  If the destPort is -1, then the IP
+     * destination port is not verified, otherwise only packets
+     * destined for the specified UDP port are returned.  Returns the
+     * length actually read.  No indication of overflow is signaled.
+     * The packet data will start at the IP header (EthernetII
+     * dest/source/type headers are removed).
      */
     public int read(byte[] packet, int offset, int byteCount, int destPort,
         int timeoutMillis) {
@@ -70,7 +85,7 @@
 
         Arrays.checkOffsetAndCount(packet.length, offset, byteCount);
 
-        if (destPort < 0 || destPort > 65535) {
+        if (destPort > 65535) {
             throw new IllegalArgumentException("Port out of range: "
                 + destPort);
         }
@@ -82,8 +97,8 @@
     /**
      * Writes a raw packet to the desired interface.  A L2 header will
      * be added which includes the specified destination address, our
-     * source MAC, and the IP type.  The caller is responsible for
-     * computing correct IP-header and payload checksums.
+     * source MAC, and the specified protocol type.  The caller is responsible
+     * for computing correct IP-header and payload checksums.
      */
     public int write(byte[] destMac, byte[] packet, int offset, int byteCount) {
         if (destMac == null) {
@@ -101,8 +116,8 @@
                 + destMac.length);
         }
 
-        return sendPacket(fd, mInterfaceName, destMac, packet, offset,
-            byteCount);
+        return sendPacket(fd, mInterfaceName, mProtocolType, destMac, packet,
+            offset, byteCount);
     }
 
     /**
diff --git a/luni/src/main/java/libcore/net/UriCodec.java b/luni/src/main/java/libcore/net/UriCodec.java
index e11a014..bde922b 100644
--- a/luni/src/main/java/libcore/net/UriCodec.java
+++ b/luni/src/main/java/libcore/net/UriCodec.java
@@ -144,8 +144,12 @@
 
     /**
      * @param convertPlus true to convert '+' to ' '.
+     * @param throwOnFailure true to throw an IllegalArgumentException on
+     *     invalid escape sequences; false to replace them with the replacement
+     *     character (U+fffd).
      */
-    public static String decode(String s, boolean convertPlus, Charset charset) {
+    public static String decode(String s, boolean convertPlus, Charset charset,
+            boolean throwOnFailure) {
         if (s.indexOf('%') == -1 && (!convertPlus || s.indexOf('+') == -1)) {
             return s;
         }
@@ -156,16 +160,17 @@
             char c = s.charAt(i);
             if (c == '%') {
                 do {
-                    if (i + 2 >= s.length()) {
-                        throw new IllegalArgumentException("Incomplete % sequence at: " + i);
+                    int d1, d2;
+                    if (i + 2 < s.length()
+                            && (d1 = hexToInt(s.charAt(i + 1))) != -1
+                            && (d2 = hexToInt(s.charAt(i + 2))) != -1) {
+                        out.write((byte) ((d1 << 4) + d2));
+                    } else if (throwOnFailure) {
+                        throw new IllegalArgumentException("Invalid % sequence at " + i + ": " + s);
+                    } else {
+                        byte[] replacement = "\ufffd".getBytes(charset);
+                        out.write(replacement, 0, replacement.length);
                     }
-                    int d1 = hexToInt(s.charAt(i + 1));
-                    int d2 = hexToInt(s.charAt(i + 2));
-                    if (d1 == -1 || d2 == -1) {
-                        throw new IllegalArgumentException("Invalid % sequence " +
-                                s.substring(i, i + 3) + " at " + i);
-                    }
-                    out.write((byte) ((d1 << 4) + d2));
                     i += 3;
                 } while (i < s.length() && s.charAt(i) == '%');
                 result.append(new String(out.toByteArray(), charset));
@@ -198,7 +203,7 @@
     }
 
     public static String decode(String s) {
-        return decode(s, false, Charsets.UTF_8);
+        return decode(s, false, Charsets.UTF_8, true);
     }
 
     private static void appendHex(StringBuilder builder, String s, Charset charset) {
diff --git a/luni/src/main/java/libcore/net/http/Challenge.java b/luni/src/main/java/libcore/net/http/Challenge.java
new file mode 100644
index 0000000..0326c17
--- /dev/null
+++ b/luni/src/main/java/libcore/net/http/Challenge.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 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.net.http;
+
+/**
+ * An RFC 2617 challenge.
+ *
+ * @hide
+ */
+public final class Challenge {
+    final String scheme;
+    final String realm;
+
+    public Challenge(String scheme, String realm) {
+        this.scheme = scheme;
+        this.realm = realm;
+    }
+
+    @Override public boolean equals(Object o) {
+        return o instanceof Challenge
+                && ((Challenge) o).scheme.equals(scheme)
+                && ((Challenge) o).realm.equals(realm);
+    }
+
+    @Override public int hashCode() {
+        return scheme.hashCode() + 31 * realm.hashCode();
+    }
+
+    @Override public String toString() {
+        return "Challenge[" + scheme + " " + realm + "]";
+    }
+}
diff --git a/luni/src/main/java/libcore/net/http/HeaderParser.java b/luni/src/main/java/libcore/net/http/HeaderParser.java
index 31e09f9..8d5770e 100644
--- a/luni/src/main/java/libcore/net/http/HeaderParser.java
+++ b/luni/src/main/java/libcore/net/http/HeaderParser.java
@@ -16,7 +16,13 @@
 
 package libcore.net.http;
 
-final class HeaderParser {
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class HeaderParser {
 
     public interface CacheControlHandler {
         void handle(String directive, String parameter);
@@ -63,6 +69,55 @@
     }
 
     /**
+     * Parse RFC 2617 challenges. This API is only interested in the scheme
+     * name and realm.
+     */
+    public static List<Challenge> parseChallenges(
+            RawHeaders responseHeaders, String challengeHeader) {
+        /*
+         * auth-scheme = token
+         * auth-param  = token "=" ( token | quoted-string )
+         * challenge   = auth-scheme 1*SP 1#auth-param
+         * realm       = "realm" "=" realm-value
+         * realm-value = quoted-string
+         */
+        List<Challenge> result = new ArrayList<Challenge>();
+        for (int h = 0; h < responseHeaders.length(); h++) {
+            if (!challengeHeader.equalsIgnoreCase(responseHeaders.getFieldName(h))) {
+                continue;
+            }
+            String value = responseHeaders.getValue(h);
+            int pos = 0;
+            while (pos < value.length()) {
+                int tokenStart = pos;
+                pos = skipUntil(value, pos, " ");
+
+                String scheme = value.substring(tokenStart, pos).trim();
+                pos = skipWhitespace(value, pos);
+
+                // TODO: This currently only handles schemes with a 'realm' parameter;
+                //       It needs to be fixed to handle any scheme and any parameters
+                //       http://code.google.com/p/android/issues/detail?id=11140
+
+                if (!value.regionMatches(pos, "realm=\"", 0, "realm=\"".length())) {
+                    break; // unexpected challenge parameter; give up
+                }
+
+                pos += "realm=\"".length();
+                int realmStart = pos;
+                pos = skipUntil(value, pos, "\"");
+                String realm = value.substring(realmStart, pos);
+                pos++; // consume '"' close quote
+                pos = skipUntil(value, pos, ",");
+                pos++; // consume ',' comma
+                pos = skipWhitespace(value, pos);
+                result.add(new Challenge(scheme, realm));
+            }
+        }
+        return result;
+    }
+
+    /**
      * Returns the next index in {@code input} at or after {@code pos} that
      * contains a character from {@code characters}. Returns the input length if
      * none of the requested characters can be found.
diff --git a/luni/src/main/java/libcore/net/http/HttpConnection.java b/luni/src/main/java/libcore/net/http/HttpConnection.java
index 756edb8..66dec4d 100644
--- a/luni/src/main/java/libcore/net/http/HttpConnection.java
+++ b/luni/src/main/java/libcore/net/http/HttpConnection.java
@@ -28,8 +28,8 @@
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.SocketException;
-import java.net.SocketTimeoutException;
 import java.net.URI;
+import java.net.UnknownHostException;
 import java.util.List;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLSocket;
@@ -49,15 +49,14 @@
  */
 final class HttpConnection {
     private final Address address;
-
     private final Socket socket;
     private InputStream inputStream;
     private OutputStream outputStream;
-
     private SSLSocket unverifiedSocket;
     private SSLSocket sslSocket;
     private InputStream sslInputStream;
     private OutputStream sslOutputStream;
+    private boolean recycled = false;
 
     private HttpConnection(Address config, int connectTimeout) throws IOException {
         this.address = config;
@@ -87,15 +86,15 @@
         this.socket = socketCandidate;
     }
 
-    public static HttpConnection connect(URI uri, Proxy proxy, boolean requiresTunnel,
-            int connectTimeout) throws IOException {
+    public static HttpConnection connect(URI uri, SSLSocketFactory sslSocketFactory,
+            Proxy proxy, boolean requiresTunnel, int connectTimeout) throws IOException {
         /*
          * Try an explicitly-specified proxy.
          */
         if (proxy != null) {
             Address address = (proxy.type() == Proxy.Type.DIRECT)
-                    ? new Address(uri)
-                    : new Address(uri, proxy, requiresTunnel);
+                    ? new Address(uri, sslSocketFactory)
+                    : new Address(uri, sslSocketFactory, proxy, requiresTunnel);
             return HttpConnectionPool.INSTANCE.get(address, connectTimeout);
         }
 
@@ -113,7 +112,8 @@
                     continue;
                 }
                 try {
-                    Address address = new Address(uri, selectedProxy, requiresTunnel);
+                    Address address = new Address(uri, sslSocketFactory,
+                            selectedProxy, requiresTunnel);
                     return HttpConnectionPool.INSTANCE.get(address, connectTimeout);
                 } catch (IOException e) {
                     // failed to connect, tell it to the selector
@@ -125,7 +125,7 @@
         /*
          * Try a direct connection. If this fails, this method will throw.
          */
-        return HttpConnectionPool.INSTANCE.get(new Address(uri), connectTimeout);
+        return HttpConnectionPool.INSTANCE.get(new Address(uri, sslSocketFactory), connectTimeout);
     }
 
     public void closeSocketAndStreams() {
@@ -235,43 +235,20 @@
     }
 
     /**
-     * Returns true if the connection is functional. This uses a shameful hack
-     * to peek a byte from the socket.
+     * Returns true if this connection has been used to satisfy an earlier
+     * HTTP request/response pair.
      */
-    boolean isStale() throws IOException {
-        if (!isEligibleForRecycling()) {
-            return true;
-        }
+    public boolean isRecycled() {
+        return recycled;
+    }
 
-        InputStream in = getInputStream();
-        if (in.available() > 0) {
-            return false;
-        }
-
-        Socket socket = getSocket();
-        int soTimeout = socket.getSoTimeout();
-        try {
-            socket.setSoTimeout(1);
-            in.mark(1);
-            int byteRead = in.read();
-            if (byteRead != -1) {
-                in.reset();
-                return false;
-            }
-            return true; // the socket is reporting all data read; it's stale
-        } catch (SocketTimeoutException e) {
-            return false; // the connection is not stale; hooray
-        } catch (IOException e) {
-            return true; // the connection is stale, the read or soTimeout failed.
-        } finally {
-            socket.setSoTimeout(soTimeout);
-        }
+    public void setRecycled() {
+        this.recycled = true;
     }
 
     /**
-     * Returns true if this connection is eligible to be recycled. This
-     * is like {@link #isStale} except that it doesn't try to actually
-     * perform any I/O.
+     * Returns true if this connection is eligible to be reused for another
+     * request/response pair.
      */
     protected boolean isEligibleForRecycling() {
         return !socket.isClosed()
@@ -282,7 +259,8 @@
     /**
      * This address has two parts: the address we connect to directly and the
      * origin address of the resource. These are the same unless a proxy is
-     * being used.
+     * being used. It also includes the SSL socket factory so that a socket will
+     * not be reused if its SSL configuration is different.
      */
     public static final class Address {
         private final Proxy proxy;
@@ -291,14 +269,19 @@
         private final int uriPort;
         private final String socketHost;
         private final int socketPort;
+        private final SSLSocketFactory sslSocketFactory;
 
-        public Address(URI uri) {
+        public Address(URI uri, SSLSocketFactory sslSocketFactory) throws UnknownHostException {
             this.proxy = null;
             this.requiresTunnel = false;
             this.uriHost = uri.getHost();
             this.uriPort = uri.getEffectivePort();
+            this.sslSocketFactory = sslSocketFactory;
             this.socketHost = uriHost;
             this.socketPort = uriPort;
+            if (uriHost == null) {
+                throw new UnknownHostException(uri.toString());
+            }
         }
 
         /**
@@ -307,11 +290,13 @@
          *     proxy. When doing so, we must avoid buffering bytes intended for
          *     the higher-level protocol.
          */
-        public Address(URI uri, Proxy proxy, boolean requiresTunnel) {
+        public Address(URI uri, SSLSocketFactory sslSocketFactory,
+                Proxy proxy, boolean requiresTunnel) throws UnknownHostException {
             this.proxy = proxy;
             this.requiresTunnel = requiresTunnel;
             this.uriHost = uri.getHost();
             this.uriPort = uri.getEffectivePort();
+            this.sslSocketFactory = sslSocketFactory;
 
             SocketAddress proxyAddress = proxy.address();
             if (!(proxyAddress instanceof InetSocketAddress)) {
@@ -321,6 +306,9 @@
             InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
             this.socketHost = proxySocketAddress.getHostName();
             this.socketPort = proxySocketAddress.getPort();
+            if (uriHost == null) {
+                throw new UnknownHostException(uri.toString());
+            }
         }
 
         public Proxy getProxy() {
@@ -333,6 +321,7 @@
                 return Objects.equal(this.proxy, that.proxy)
                         && this.uriHost.equals(that.uriHost)
                         && this.uriPort == that.uriPort
+                        && Objects.equal(this.sslSocketFactory, that.sslSocketFactory)
                         && this.requiresTunnel == that.requiresTunnel;
             }
             return false;
@@ -342,6 +331,7 @@
             int result = 17;
             result = 31 * result + uriHost.hashCode();
             result = 31 * result + uriPort;
+            result = 31 * result + (sslSocketFactory != null ? sslSocketFactory.hashCode() : 0);
             result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
             result = 31 * result + (requiresTunnel ? 1 : 0);
             return result;
diff --git a/luni/src/main/java/libcore/net/http/HttpConnectionPool.java b/luni/src/main/java/libcore/net/http/HttpConnectionPool.java
index 21e94de..1f5f4d9 100644
--- a/luni/src/main/java/libcore/net/http/HttpConnectionPool.java
+++ b/luni/src/main/java/libcore/net/http/HttpConnectionPool.java
@@ -65,17 +65,18 @@
         // First try to reuse an existing HTTP connection.
         synchronized (connectionPool) {
             List<HttpConnection> connections = connectionPool.get(address);
-            if (connections != null) {
-                while (!connections.isEmpty()) {
-                    HttpConnection connection = connections.remove(connections.size() - 1);
-                    if (!connection.isStale()) { // TODO: this op does I/O!
-                        // Since Socket is recycled, re-tag before using
-                        final Socket socket = connection.getSocket();
-                        SocketTagger.get().tag(socket);
-                        return connection;
-                    }
+            while (connections != null) {
+                HttpConnection connection = connections.remove(connections.size() - 1);
+                if (connections.isEmpty()) {
+                    connectionPool.remove(address);
+                    connections = null;
                 }
-                connectionPool.remove(address);
+                if (connection.isEligibleForRecycling()) {
+                    // Since Socket is recycled, re-tag before using
+                    Socket socket = connection.getSocket();
+                    SocketTagger.get().tag(socket);
+                    return connection;
+                }
             }
         }
 
@@ -87,7 +88,7 @@
     }
 
     public void recycle(HttpConnection connection) {
-        final Socket socket = connection.getSocket();
+        Socket socket = connection.getSocket();
         try {
             SocketTagger.get().untag(socket);
         } catch (SocketException e) {
@@ -106,6 +107,7 @@
                     connectionPool.put(address, connections);
                 }
                 if (connections.size() < maxConnections) {
+                    connection.setRecycled();
                     connections.add(connection);
                     return; // keep the connection open
                 }
diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java
index 25f28f5..3e4b9d3 100644
--- a/luni/src/main/java/libcore/net/http/HttpEngine.java
+++ b/luni/src/main/java/libcore/net/http/HttpEngine.java
@@ -25,9 +25,11 @@
 import java.net.CacheRequest;
 import java.net.CacheResponse;
 import java.net.CookieHandler;
+import java.net.ExtendedResponseCache;
 import java.net.HttpURLConnection;
 import java.net.Proxy;
 import java.net.ResponseCache;
+import java.net.ResponseSource;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -38,6 +40,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.zip.GZIPInputStream;
+import javax.net.ssl.SSLSocketFactory;
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 import libcore.util.EmptyArray;
@@ -197,6 +200,10 @@
         this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
     }
 
+    public URI getUri() {
+        return uri;
+    }
+
     /**
      * Figures out what the response source will be, and opens a socket to that
      * source if necessary. Prepares the request headers and gets ready to start
@@ -209,8 +216,8 @@
 
         prepareRawRequestHeaders();
         initResponseSource();
-        if (responseCache instanceof HttpResponseCache) {
-            ((HttpResponseCache) responseCache).trackResponse(responseSource);
+        if (responseCache instanceof ExtendedResponseCache) {
+            ((ExtendedResponseCache) responseCache).trackResponse(responseSource);
         }
 
         /*
@@ -305,8 +312,8 @@
     }
 
     protected final HttpConnection openSocketConnection() throws IOException {
-        HttpConnection result = HttpConnection.connect(
-                uri, policy.getProxy(), requiresTunnel(), policy.getConnectTimeout());
+        HttpConnection result = HttpConnection.connect(uri, getSslSocketFactory(),
+                policy.getProxy(), requiresTunnel(), policy.getConnectTimeout());
         Proxy proxy = result.getAddress().getProxy();
         if (proxy != null) {
             policy.setProxy(proxy);
@@ -415,6 +422,10 @@
         return connection;
     }
 
+    public final boolean hasRecycledConnection() {
+        return connection != null && connection.isRecycled();
+    }
+
     /**
      * Returns true if {@code cacheResponse} is of the right type. This
      * condition is necessary but not sufficient for the cached response to
@@ -425,6 +436,11 @@
     }
 
     private void maybeCache() throws IOException {
+        // Never cache responses to proxy CONNECT requests.
+        if (method == CONNECT) {
+            return;
+        }
+
         // Are we caching at all?
         if (!policy.getUseCaches() || responseCache == null) {
             return;
@@ -553,8 +569,13 @@
      */
     public final boolean hasResponseBody() {
         int responseCode = responseHeaders.getHeaders().getResponseCode();
-        if (method != HEAD
-                && method != CONNECT
+
+        // HEAD requests never yield a body regardless of the response headers.
+        if (method == HEAD) {
+            return false;
+        }
+
+        if (method != CONNECT
                 && (responseCode < HTTP_CONTINUE || responseCode >= 200)
                 && responseCode != HttpURLConnectionImpl.HTTP_NO_CONTENT
                 && responseCode != HttpURLConnectionImpl.HTTP_NOT_MODIFIED) {
@@ -724,6 +745,14 @@
         return policy.usingProxy();
     }
 
+    /**
+     * Returns the SSL configuration for connections created by this engine.
+     * We cannot reuse HTTPS connections if the socket factory has changed.
+     */
+    protected SSLSocketFactory getSslSocketFactory() {
+        return null;
+    }
+
     protected final String getDefaultUserAgent() {
         String agent = System.getProperty("http.agent");
         return agent != null ? agent : ("Java" + System.getProperty("java.version"));
@@ -786,12 +815,14 @@
 
         if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
             if (cachedResponseHeaders.validate(responseHeaders)) {
-                if (responseCache instanceof HttpResponseCache) {
-                    ((HttpResponseCache) responseCache).trackConditionalCacheHit();
-                }
-                // Discard the network response body. Combine the headers.
                 release(true);
-                setResponse(cachedResponseHeaders.combine(responseHeaders), cachedResponseBody);
+                ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
+                setResponse(combinedHeaders, cachedResponseBody);
+                if (responseCache instanceof ExtendedResponseCache) {
+                    ExtendedResponseCache httpResponseCache = (ExtendedResponseCache) responseCache;
+                    httpResponseCache.trackConditionalCacheHit();
+                    httpResponseCache.update(cacheResponse, getHttpConnectionToCache());
+                }
                 return;
             } else {
                 IoUtils.closeQuietly(cachedResponseBody);
diff --git a/luni/src/main/java/libcore/net/http/HttpResponseCache.java b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
index 24aff87..910461e7 100644
--- a/luni/src/main/java/libcore/net/http/HttpResponseCache.java
+++ b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
@@ -29,8 +29,10 @@
 import java.io.Writer;
 import java.net.CacheRequest;
 import java.net.CacheResponse;
+import java.net.ExtendedResponseCache;
 import java.net.HttpURLConnection;
 import java.net.ResponseCache;
+import java.net.ResponseSource;
 import java.net.SecureCacheResponse;
 import java.net.URI;
 import java.net.URLConnection;
@@ -58,7 +60,7 @@
  * {@code android.net.HttpResponseCache}, the stable, documented front end for
  * this.
  */
-public final class HttpResponseCache extends ResponseCache {
+public final class HttpResponseCache extends ResponseCache implements ExtendedResponseCache {
     // TODO: add APIs to iterate the cache?
     private static final int VERSION = 201105;
     private static final int ENTRY_METADATA = 0;
@@ -109,23 +111,9 @@
             return null;
         }
 
-        InputStream body = newBodyInputStream(snapshot);
         return entry.isHttps()
-                ? entry.newSecureCacheResponse(body)
-                : entry.newCacheResponse(body);
-    }
-
-    /**
-     * Returns an input stream that reads the body of a snapshot, closing the
-     * snapshot when the stream is closed.
-     */
-    private InputStream newBodyInputStream(final DiskLruCache.Snapshot snapshot) {
-        return new FilterInputStream(snapshot.getInputStream(ENTRY_BODY)) {
-            @Override public void close() throws IOException {
-                snapshot.close();
-                super.close();
-            }
-        };
+                ? new EntrySecureCacheResponse(entry, snapshot)
+                : new EntryCacheResponse(entry, snapshot);
     }
 
     @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
@@ -178,17 +166,49 @@
             entry.writeTo(editor);
             return new CacheRequestImpl(editor);
         } catch (IOException e) {
-            // Give up because the cache cannot be written.
-            try {
-                if (editor != null) {
-                    editor.abort();
-                }
-            } catch (IOException ignored) {
-            }
+            abortQuietly(editor);
             return null;
         }
     }
 
+    /**
+     * Handles a conditional request hit by updating the stored cache response
+     * with the headers from {@code httpConnection}. The cached response body is
+     * not updated. If the stored response has changed since {@code
+     * conditionalCacheHit} was returned, this does nothing.
+     */
+    public void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection) {
+        HttpEngine httpEngine = getHttpEngine(httpConnection);
+        URI uri = httpEngine.getUri();
+        ResponseHeaders response = httpEngine.getResponseHeaders();
+        RawHeaders varyHeaders = httpEngine.getRequestHeaders().getHeaders()
+                .getAll(response.getVaryFields());
+        Entry entry = new Entry(uri, varyHeaders, httpConnection);
+        DiskLruCache.Snapshot snapshot = (conditionalCacheHit instanceof EntryCacheResponse)
+                ? ((EntryCacheResponse) conditionalCacheHit).snapshot
+                : ((EntrySecureCacheResponse) conditionalCacheHit).snapshot;
+        DiskLruCache.Editor editor = null;
+        try {
+            editor = snapshot.edit(); // returns null if snapshot is not current
+            if (editor != null) {
+                entry.writeTo(editor);
+                editor.commit();
+            }
+        } catch (IOException e) {
+            abortQuietly(editor);
+        }
+    }
+
+    private void abortQuietly(DiskLruCache.Editor editor) {
+        // Give up because the cache cannot be written.
+        try {
+            if (editor != null) {
+                editor.abort();
+            }
+        } catch (IOException ignored) {
+        }
+    }
+
     private HttpEngine getHttpEngine(HttpURLConnection httpConnection) {
         if (httpConnection instanceof HttpURLConnectionImpl) {
             return ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
@@ -211,7 +231,7 @@
         return writeSuccessCount;
     }
 
-    synchronized void trackResponse(ResponseSource source) {
+    public synchronized void trackResponse(ResponseSource source) {
         requestCount++;
 
         switch (source) {
@@ -225,7 +245,7 @@
         }
     }
 
-    synchronized void trackConditionalCacheHit() {
+    public synchronized void trackConditionalCacheHit() {
         hitCount++;
     }
 
@@ -490,62 +510,91 @@
                     && new ResponseHeaders(uri, responseHeaders)
                             .varyMatches(varyHeaders.toMultimap(), requestHeaders);
         }
+    }
 
-        public CacheResponse newCacheResponse(final InputStream in) {
-            return new CacheResponse() {
-                @Override public Map<String, List<String>> getHeaders() {
-                    return responseHeaders.toMultimap();
-                }
+    /**
+     * Returns an input stream that reads the body of a snapshot, closing the
+     * snapshot when the stream is closed.
+     */
+    private static InputStream newBodyInputStream(final DiskLruCache.Snapshot snapshot) {
+        return new FilterInputStream(snapshot.getInputStream(ENTRY_BODY)) {
+            @Override public void close() throws IOException {
+                snapshot.close();
+                super.close();
+            }
+        };
+    }
 
-                @Override public InputStream getBody() {
-                    return in;
-                }
-            };
+    static class EntryCacheResponse extends CacheResponse {
+        private final Entry entry;
+        private final DiskLruCache.Snapshot snapshot;
+        private final InputStream in;
+
+        public EntryCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
+            this.entry = entry;
+            this.snapshot = snapshot;
+            this.in = newBodyInputStream(snapshot);
         }
 
-        public SecureCacheResponse newSecureCacheResponse(final InputStream in) {
-            return new SecureCacheResponse() {
-                @Override public Map<String, List<String>> getHeaders() {
-                    return responseHeaders.toMultimap();
-                }
+        @Override public Map<String, List<String>> getHeaders() {
+            return entry.responseHeaders.toMultimap();
+        }
 
-                @Override public InputStream getBody() {
-                    return in;
-                }
+        @Override public InputStream getBody() {
+            return in;
+        }
+    }
 
-                @Override public String getCipherSuite() {
-                    return cipherSuite;
-                }
+    static class EntrySecureCacheResponse extends SecureCacheResponse {
+        private final Entry entry;
+        private final DiskLruCache.Snapshot snapshot;
+        private final InputStream in;
 
-                @Override public List<Certificate> getServerCertificateChain()
-                        throws SSLPeerUnverifiedException {
-                    if (peerCertificates == null || peerCertificates.length == 0) {
-                        throw new SSLPeerUnverifiedException(null);
-                    }
-                    return Arrays.asList(peerCertificates.clone());
-                }
+        public EntrySecureCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
+            this.entry = entry;
+            this.snapshot = snapshot;
+            this.in = newBodyInputStream(snapshot);
+        }
 
-                @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
-                    if (peerCertificates == null || peerCertificates.length == 0) {
-                        throw new SSLPeerUnverifiedException(null);
-                    }
-                    return ((X509Certificate) peerCertificates[0]).getSubjectX500Principal();
-                }
+        @Override public Map<String, List<String>> getHeaders() {
+            return entry.responseHeaders.toMultimap();
+        }
 
-                @Override public List<Certificate> getLocalCertificateChain() {
-                    if (localCertificates == null || localCertificates.length == 0) {
-                        return null;
-                    }
-                    return Arrays.asList(localCertificates.clone());
-                }
+        @Override public InputStream getBody() {
+            return in;
+        }
 
-                @Override public Principal getLocalPrincipal() {
-                    if (localCertificates == null || localCertificates.length == 0) {
-                        return null;
-                    }
-                    return ((X509Certificate) localCertificates[0]).getSubjectX500Principal();
-                }
-            };
+        @Override public String getCipherSuite() {
+            return entry.cipherSuite;
+        }
+
+        @Override public List<Certificate> getServerCertificateChain()
+                throws SSLPeerUnverifiedException {
+            if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
+                throw new SSLPeerUnverifiedException(null);
+            }
+            return Arrays.asList(entry.peerCertificates.clone());
+        }
+
+        @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+            if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
+                throw new SSLPeerUnverifiedException(null);
+            }
+            return ((X509Certificate) entry.peerCertificates[0]).getSubjectX500Principal();
+        }
+
+        @Override public List<Certificate> getLocalCertificateChain() {
+            if (entry.localCertificates == null || entry.localCertificates.length == 0) {
+                return null;
+            }
+            return Arrays.asList(entry.localCertificates.clone());
+        }
+
+        @Override public Principal getLocalPrincipal() {
+            if (entry.localCertificates == null || entry.localCertificates.length == 0) {
+                return null;
+            }
+            return ((X509Certificate) entry.localCertificates[0]).getSubjectX500Principal();
         }
     }
 }
diff --git a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
index 8dbaf8c..a59df55 100644
--- a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
@@ -268,53 +268,64 @@
             return httpEngine;
         }
 
-        try {
-            while (true) {
+        while (true) {
+            try {
                 httpEngine.sendRequest();
                 httpEngine.readResponse();
-
-                Retry retry = processResponseHeaders();
-                if (retry == Retry.NONE) {
-                    httpEngine.automaticallyReleaseConnectionToPool();
-                    break;
-                }
-
+            } catch (IOException e) {
                 /*
-                 * The first request was insufficient. Prepare for another...
+                 * If the connection was recycled, its staleness may have caused
+                 * the failure. Silently retry with a different connection.
                  */
-                String retryMethod = method;
                 OutputStream requestBody = httpEngine.getRequestBody();
-
-                /*
-                 * Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM
-                 * redirect should keep the same method, Chrome, Firefox and the
-                 * RI all issue GETs when following any redirect.
-                 */
-                int responseCode = getResponseCode();
-                if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM
-                        || responseCode == HTTP_MOVED_TEMP || responseCode == HTTP_SEE_OTHER) {
-                    retryMethod = HttpEngine.GET;
-                    requestBody = null;
+                if (httpEngine.hasRecycledConnection()
+                        && (requestBody == null || requestBody instanceof RetryableOutputStream)) {
+                    httpEngine.release(false);
+                    httpEngine = newHttpEngine(method, rawRequestHeaders, null,
+                            (RetryableOutputStream) requestBody);
+                    continue;
                 }
-
-                if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) {
-                    throw new HttpRetryException("Cannot retry streamed HTTP body",
-                            httpEngine.getResponseCode());
-                }
-
-                if (retry == Retry.DIFFERENT_CONNECTION) {
-                    httpEngine.automaticallyReleaseConnectionToPool();
-                }
-
-                httpEngine.release(true);
-
-                httpEngine = newHttpEngine(retryMethod, rawRequestHeaders,
-                        httpEngine.getConnection(), (RetryableOutputStream) requestBody);
+                httpEngineFailure = e;
+                throw e;
             }
-            return httpEngine;
-        } catch (IOException e) {
-            httpEngineFailure = e;
-            throw e;
+
+            Retry retry = processResponseHeaders();
+            if (retry == Retry.NONE) {
+                httpEngine.automaticallyReleaseConnectionToPool();
+                return httpEngine;
+            }
+
+            /*
+             * The first request was insufficient. Prepare for another...
+             */
+            String retryMethod = method;
+            OutputStream requestBody = httpEngine.getRequestBody();
+
+            /*
+             * Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM
+             * redirect should keep the same method, Chrome, Firefox and the
+             * RI all issue GETs when following any redirect.
+             */
+            int responseCode = getResponseCode();
+            if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM
+                    || responseCode == HTTP_MOVED_TEMP || responseCode == HTTP_SEE_OTHER) {
+                retryMethod = HttpEngine.GET;
+                requestBody = null;
+            }
+
+            if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) {
+                throw new HttpRetryException("Cannot retry streamed HTTP body",
+                        httpEngine.getResponseCode());
+            }
+
+            if (retry == Retry.DIFFERENT_CONNECTION) {
+                httpEngine.automaticallyReleaseConnectionToPool();
+            }
+
+            httpEngine.release(true);
+
+            httpEngine = newHttpEngine(retryMethod, rawRequestHeaders,
+                    httpEngine.getConnection(), (RetryableOutputStream) requestBody);
         }
     }
 
@@ -390,13 +401,10 @@
         }
 
         // keep asking for username/password until authorized
-        String challenge = responseCode == HTTP_PROXY_AUTH
-                ? response.getProxyAuthenticate()
-                : response.getWwwAuthenticate();
-        if (challenge == null) {
-            throw new IOException("Received authentication challenge is null");
-        }
-        String credentials = getAuthorizationCredentials(challenge);
+        String challengeHeader = responseCode == HTTP_PROXY_AUTH
+                ? "Proxy-Authenticate"
+                : "WWW-Authenticate";
+        String credentials = getAuthorizationCredentials(response.getHeaders(), challengeHeader);
         if (credentials == null) {
             return false; // could not find credentials, end request cycle
         }
@@ -412,31 +420,30 @@
     /**
      * Returns the authorization credentials on the base of provided challenge.
      */
-    private String getAuthorizationCredentials(String challenge) throws IOException {
-        int idx = challenge.indexOf(" ");
-        if (idx == -1) {
-            return null;
+    private String getAuthorizationCredentials(RawHeaders responseHeaders, String challengeHeader)
+            throws IOException {
+        List<Challenge> challenges = HeaderParser.parseChallenges(responseHeaders, challengeHeader);
+        if (challenges.isEmpty()) {
+            throw new IOException("No authentication challenges found");
         }
-        String scheme = challenge.substring(0, idx);
-        int realm = challenge.indexOf("realm=\"") + 7;
-        String prompt = null;
-        if (realm != -1) {
-            int end = challenge.indexOf('"', realm);
-            if (end != -1) {
-                prompt = challenge.substring(realm, end);
+
+        for (Challenge challenge : challenges) {
+            // use the global authenticator to get the password
+            PasswordAuthentication auth = Authenticator.requestPasswordAuthentication(
+                    getConnectToInetAddress(), getConnectToPort(), url.getProtocol(),
+                    challenge.realm, challenge.scheme);
+            if (auth == null) {
+                continue;
             }
+
+            // base64 encode the username and password
+            String usernameAndPassword = auth.getUserName() + ":" + new String(auth.getPassword());
+            byte[] bytes = usernameAndPassword.getBytes(Charsets.ISO_8859_1);
+            String encoded = Base64.encode(bytes);
+            return challenge.scheme + " " + encoded;
         }
-        // use the global authenticator to get the password
-        PasswordAuthentication pa = Authenticator.requestPasswordAuthentication(
-                getConnectToInetAddress(), getConnectToPort(), url.getProtocol(), prompt, scheme);
-        if (pa == null) {
-            return null;
-        }
-        // base64 encode the username and password
-        String usernameAndPassword = pa.getUserName() + ":" + new String(pa.getPassword());
-        byte[] bytes = usernameAndPassword.getBytes(Charsets.ISO_8859_1);
-        String encoded = Base64.encode(bytes);
-        return scheme + " " + encoded;
+
+        return null;
     }
 
     private InetAddress getConnectToInetAddress() throws IOException {
diff --git a/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
index ae5e7c1..9e3e4ef 100644
--- a/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
@@ -35,6 +35,7 @@
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
 
 final class HttpsURLConnectionImpl extends HttpsURLConnection {
 
@@ -336,8 +337,8 @@
     }
 
     @Override
-    public void setConnectTimeout(int timeout) {
-        delegate.setConnectTimeout(timeout);
+    public void setConnectTimeout(int timeoutMillis) {
+        delegate.setConnectTimeout(timeoutMillis);
     }
 
     @Override
@@ -346,8 +347,8 @@
     }
 
     @Override
-    public void setReadTimeout(int timeout) {
-        delegate.setReadTimeout(timeout);
+    public void setReadTimeout(int timeoutMillis) {
+        delegate.setReadTimeout(timeoutMillis);
     }
 
     @Override
@@ -519,6 +520,10 @@
             return false;
         }
 
+        @Override protected SSLSocketFactory getSslSocketFactory() {
+            return enclosing.getSSLSocketFactory();
+        }
+
         @Override protected HttpURLConnection getHttpConnectionToCache() {
             return enclosing;
         }
diff --git a/luni/src/main/java/libcore/net/http/ResponseHeaders.java b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
index 0d8cc40..003b445 100644
--- a/luni/src/main/java/libcore/net/http/ResponseHeaders.java
+++ b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
@@ -17,6 +17,7 @@
 package libcore.net.http;
 
 import java.net.HttpURLConnection;
+import java.net.ResponseSource;
 import java.net.URI;
 import java.util.Collections;
 import java.util.Date;
@@ -108,8 +109,6 @@
     private String transferEncoding;
     private int contentLength = -1;
     private String connection;
-    private String proxyAuthenticate;
-    private String wwwAuthenticate;
 
     public ResponseHeaders(URI uri, RawHeaders headers) {
         this.uri = uri;
@@ -171,10 +170,6 @@
                 }
             } else if ("Connection".equalsIgnoreCase(fieldName)) {
                 connection = value;
-            } else if ("Proxy-Authenticate".equalsIgnoreCase(fieldName)) {
-                proxyAuthenticate = value;
-            } else if ("WWW-Authenticate".equalsIgnoreCase(fieldName)) {
-                wwwAuthenticate = value;
             } else if (SENT_MILLIS.equalsIgnoreCase(fieldName)) {
                 sentRequestMillis = Long.parseLong(value);
             } else if (RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
@@ -264,14 +259,6 @@
         return connection;
     }
 
-    public String getProxyAuthenticate() {
-        return proxyAuthenticate;
-    }
-
-    public String getWwwAuthenticate() {
-        return wwwAuthenticate;
-    }
-
     public void setLocalTimestamps(long sentRequestMillis, long receivedResponseMillis) {
         this.sentRequestMillis = sentRequestMillis;
         headers.add(SENT_MILLIS, Long.toString(sentRequestMillis));
@@ -476,6 +463,7 @@
      */
     public ResponseHeaders combine(ResponseHeaders network) {
         RawHeaders result = new RawHeaders();
+        result.setStatusLine(headers.getStatusLine());
 
         for (int i = 0; i < headers.length(); i++) {
             String fieldName = headers.getFieldName(i);
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
index e6adc44..b15e8ac 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
@@ -38,7 +38,6 @@
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -49,7 +48,7 @@
 import org.apache.harmony.security.x509.Extension;
 import org.apache.harmony.security.x509.Extensions;
 import org.apache.harmony.security.x509.TBSCertificate;
-import org.apache.harmony.xnet.provider.jsse.OpenSSLSignature;
+import org.apache.harmony.xnet.provider.jsse.OpenSSLProvider;
 
 /**
  * This class is an implementation of X509Certificate. It wraps
@@ -372,7 +371,7 @@
 
         Signature signature;
         try {
-            signature = OpenSSLSignature.getInstance(getSigAlgName());
+            signature = Signature.getInstance(getSigAlgName(), OpenSSLProvider.PROVIDER_NAME);
         } catch (NoSuchAlgorithmException ignored) {
             signature = Signature.getInstance(getSigAlgName());
         }
@@ -393,7 +392,7 @@
         Signature signature;
         try {
             if (sigProvider == null) {
-                signature = OpenSSLSignature.getInstance(getSigAlgName());
+                signature = Signature.getInstance(getSigAlgName(), OpenSSLProvider.PROVIDER_NAME);
             } else {
                 signature = Signature.getInstance(getSigAlgName(), sigProvider);
             }
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
index 70a13f7..f6efb8a 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
@@ -33,7 +33,6 @@
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import javax.security.auth.x500.X500Principal;
@@ -43,7 +42,7 @@
 import org.apache.harmony.security.pkcs7.SignerInfo;
 import org.apache.harmony.security.provider.cert.X509CertImpl;
 import org.apache.harmony.security.x501.AttributeTypeAndValue;
-import org.apache.harmony.xnet.provider.jsse.OpenSSLSignature;
+import org.apache.harmony.xnet.provider.jsse.OpenSSLProvider;
 
 public class JarUtils {
 
@@ -122,7 +121,7 @@
         if (da != null && dea != null) {
             alg = da + "with" +  dea;
             try {
-                sig = OpenSSLSignature.getInstance(alg);
+                sig = Signature.getInstance(alg, OpenSSLProvider.PROVIDER_NAME);
             } catch (NoSuchAlgorithmException e) {}
         }
         if (sig == null) {
@@ -131,7 +130,7 @@
                 return null;
             }
             try {
-                sig = OpenSSLSignature.getInstance(alg);
+                sig = Signature.getInstance(alg, OpenSSLProvider.PROVIDER_NAME);
             } catch (NoSuchAlgorithmException e) {
                 return null;
             }
diff --git a/luni/src/main/java/org/apache/harmony/security/x509/GeneralName.java b/luni/src/main/java/org/apache/harmony/security/x509/GeneralName.java
index d8188be..e216029 100644
--- a/luni/src/main/java/org/apache/harmony/security/x509/GeneralName.java
+++ b/luni/src/main/java/org/apache/harmony/security/x509/GeneralName.java
@@ -539,9 +539,12 @@
     }
 
     /**
-     * Checks the correctness of the string representation of DNS name.
-     * The correctness is checked as specified in RFC 1034 p. 10, and modified
-     * by RFC 1123 (section 2.1).
+     * Checks the correctness of the string representation of DNS name as
+     * specified in RFC 1034 p. 10 and RFC 1123 section 2.1.
+     *
+     * <p>This permits a wildcard character '*' anywhere in the name; it is up
+     * to the application to check which wildcards are permitted. See RFC 6125
+     * for recommended wildcard matching rules.
      */
     public static void checkDNS(String dns) throws IOException {
         String string = dns.toLowerCase(Locale.US);
@@ -551,18 +554,14 @@
         for (int i = 0; i < length; i++) {
             char ch = string.charAt(i);
             if (first_letter) {
-                if ((length > 2) && (ch == '*') && (string.charAt(1) == '.')) {
-                    first_letter = false;
-                    continue;
-                }
-                if ((ch > 'z' || ch < 'a') && (ch < '0' || ch > '9')) {
+                if ((ch > 'z' || ch < 'a') && (ch < '0' || ch > '9') && (ch != '*')) {
                     throw new IOException("DNS name must start with a letter: " + dns);
                 }
                 first_letter = false;
                 continue;
             }
             if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
-                    || (ch == '-') || (ch == '.'))) {
+                    || (ch == '-') || (ch == '.') || (ch == '*'))) {
                 throw new IOException("Incorrect DNS name: " + dns);
             }
             if (ch == '.') {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
index d21aa2c..4b29363 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
@@ -36,6 +36,7 @@
 import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.DHParameterSpec;
 import javax.crypto.spec.DHPublicKeySpec;
+import javax.crypto.spec.SecretKeySpec;
 import javax.net.ssl.X509ExtendedKeyManager;
 import javax.net.ssl.X509KeyManager;
 import javax.security.auth.x500.X500Principal;
@@ -416,10 +417,10 @@
             try {
                 c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                 if (serverKeyExchange != null) {
-                    c.init(Cipher.ENCRYPT_MODE, serverKeyExchange
+                    c.init(Cipher.WRAP_MODE, serverKeyExchange
                             .getRSAPublicKey());
                 } else {
-                    c.init(Cipher.ENCRYPT_MODE, serverCert.certs[0]);
+                    c.init(Cipher.WRAP_MODE, serverCert.certs[0]);
                 }
             } catch (Exception e) {
                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
@@ -431,7 +432,7 @@
             System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0, 2);
             try {
                 clientKeyExchange = new ClientKeyExchange(c
-                        .doFinal(preMasterSecret),
+                        .wrap(new SecretKeySpec(preMasterSecret, "preMasterSecret")),
                         serverHello.server_version[1] == 1);
             } catch (Exception e) {
                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 5e759c1..759fc85 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -26,7 +26,6 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -48,15 +47,69 @@
 
     private native static void clinit();
 
+    // --- ENGINE functions ----------------------------------------------------
+    public static native void ENGINE_load_dynamic();
+
+    public static native int ENGINE_by_id(String id);
+
+    public static native int ENGINE_init(int e);
+
+    public static native int ENGINE_finish(int e);
+
+    public static native int ENGINE_free(int e);
+
+    public static native int ENGINE_load_private_key(int e, String key_id);
+
     // --- DSA/RSA public/private key handling functions -----------------------
 
     public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g,
-                                              byte[] priv_key, byte[] pub_key);
+                                              byte[] pub_key, byte[] priv_key);
 
-    public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+    public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q,
+            byte[] dmp1, byte[] dmq1, byte[] iqmp);
+
+    public static native int EVP_PKEY_size(int pkey);
+
+    public static native int EVP_PKEY_type(int pkey);
 
     public static native void EVP_PKEY_free(int pkey);
 
+    public static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int pkey);
+
+    public static native int d2i_PKCS8_PRIV_KEY_INFO(byte[] data);
+
+    public static native byte[] i2d_PUBKEY(int pkey);
+
+    public static native int d2i_PUBKEY(byte[] data);
+
+    public static native int RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
+
+    /**
+     * @return array of {n, e}
+     */
+    public static native byte[][] get_RSA_public_params(int rsa);
+
+    /**
+     * @return array of {n, e, d, p, q, dmp1, dmq1, iqmp}
+     */
+    public static native byte[][] get_RSA_private_params(int rsa);
+
+    public static native int DSA_generate_key(int primeBits, byte[] seed, byte[] g, byte[] p,
+            byte[] q);
+
+    /**
+     * @return array of {g, p, q, y(pub), x(priv)}
+     */
+    public static native byte[][] get_DSA_params(int dsa);
+
+    public static native byte[] i2d_RSAPublicKey(int rsa);
+
+    public static native byte[] i2d_RSAPrivateKey(int rsa);
+
+    public static native byte[] i2d_DSAPublicKey(int dsa);
+
+    public static native byte[] i2d_DSAPrivateKey(int dsa);
+
     // --- Message digest functions --------------
 
     public static native int EVP_get_digestbyname(String name);
@@ -81,6 +134,13 @@
 
     // --- Signature handling functions ----------------------------------------
 
+    public static native int EVP_SignInit(String algorithm);
+
+    public static native void EVP_SignUpdate(int ctx, byte[] buffer,
+                                               int offset, int length);
+
+    public static native int EVP_SignFinal(int ctx, byte[] signature, int offset, int key);
+
     public static native int EVP_VerifyInit(String algorithm);
 
     public static native void EVP_VerifyUpdate(int ctx, byte[] buffer,
@@ -89,6 +149,21 @@
     public static native int EVP_VerifyFinal(int ctx, byte[] signature,
                                              int offset, int length, int key);
 
+
+    // --- Block ciphers -------------------------------------------------------
+
+    public static native int EVP_get_cipherbyname(String string);
+
+    public static native int EVP_CipherInit_ex(int cipherNid, byte[] key, byte[] iv,
+            boolean encrypting);
+
+    public static native int EVP_CipherUpdate(int ctx, byte[] out, int outOffset, byte[] in,
+            int inOffset);
+
+    public static native int EVP_CipherFinal_ex(int ctx, byte[] out, int outOffset);
+
+    public static native void EVP_CIPHER_CTX_cleanup(int ctx);
+
     // --- RAND seeding --------------------------------------------------------
 
     public static final int RAND_SEED_LENGTH_IN_BYTES = 1024;
@@ -118,6 +193,8 @@
 
     private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3";
     private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1";
+    private static final String SUPPORTED_PROTOCOL_TLSV1_1 = "TLSv1.1";
+    private static final String SUPPORTED_PROTOCOL_TLSV1_2 = "TLSv1.2";
 
     public static final Map<String, String> OPENSSL_TO_STANDARD_CIPHER_SUITES
             = new HashMap<String, String>();
@@ -250,14 +327,23 @@
         SUPPORTED_CIPHER_SUITES[size] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
     }
 
+    // EVP_PKEY types from evp.h and objects.h
+    public static final int EVP_PKEY_RSA = 6;   // NID_rsaEcnryption
+    public static final int EVP_PKEY_DSA = 116; // NID_dsa
+    public static final int EVP_PKEY_DH  = 28;  // NID_dhKeyAgreement
+    public static final int EVP_PKEY_EC  = 408; // NID_X9_62_id_ecPublicKey
+
     // SSL mode from ssl.h
     public static final long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
 
     // SSL options from ssl.h
-    public static final long SSL_OP_NO_TICKET      = 0x00004000L;
-    public static final long SSL_OP_NO_COMPRESSION = 0x00020000L;
-    public static final long SSL_OP_NO_SSLv3       = 0x02000000L;
-    public static final long SSL_OP_NO_TLSv1       = 0x04000000L;
+    public static final long SSL_OP_NO_TICKET                              = 0x00004000L;
+    public static final long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L;
+    public static final long SSL_OP_NO_COMPRESSION                         = 0x00020000L;
+    public static final long SSL_OP_NO_SSLv3                               = 0x02000000L;
+    public static final long SSL_OP_NO_TLSv1                               = 0x04000000L;
+    public static final long SSL_OP_NO_TLSv1_1                             = 0x10000000L;
+    public static final long SSL_OP_NO_TLSv1_2                             = 0x08000000L;
 
     public static native int SSL_CTX_new();
 
@@ -307,6 +393,8 @@
 
     public static native void SSL_CTX_free(int ssl_ctx);
 
+    public static native void SSL_CTX_set_session_id_context(int ssl_ctx, byte[] sid_ctx);
+
     public static native int SSL_new(int ssl_ctx) throws SSLException;
 
     public static byte[][] encodeCertificates(Certificate[] certificates)
@@ -320,6 +408,8 @@
 
     public static native void SSL_use_certificate(int ssl, byte[][] asn1DerEncodedCertificateChain);
 
+    public static native void SSL_use_OpenSSL_PrivateKey(int ssl, int pkey);
+
     public static native void SSL_use_PrivateKey(int ssl, byte[] pkcs8EncodedPrivateKey);
 
     public static native void SSL_check_private_key(int ssl) throws SSLException;
@@ -347,8 +437,18 @@
 
     public static native long SSL_clear_options(int ssl, long options);
 
+    public static String[] getDefaultProtocols() {
+        return new String[] { SUPPORTED_PROTOCOL_SSLV3,
+                              SUPPORTED_PROTOCOL_TLSV1,
+        };
+    }
+
     public static String[] getSupportedProtocols() {
-        return new String[] { SUPPORTED_PROTOCOL_SSLV3, SUPPORTED_PROTOCOL_TLSV1 };
+        return new String[] { SUPPORTED_PROTOCOL_SSLV3,
+                              SUPPORTED_PROTOCOL_TLSV1,
+                              SUPPORTED_PROTOCOL_TLSV1_1,
+                              SUPPORTED_PROTOCOL_TLSV1_2,
+        };
     }
 
     public static void setEnabledProtocols(int ssl, String[] protocols) {
@@ -356,7 +456,7 @@
         // openssl uses negative logic letting you disable protocols.
         // so first, assume we need to set all (disable all) and clear none (enable none).
         // in the loop, selectively move bits from set to clear (from disable to enable)
-        long optionsToSet = (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);
+        long optionsToSet = (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
         long optionsToClear = 0;
         for (int i = 0; i < protocols.length; i++) {
             String protocol = protocols[i];
@@ -366,6 +466,12 @@
             } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) {
                 optionsToSet &= ~SSL_OP_NO_TLSv1;
                 optionsToClear |= SSL_OP_NO_TLSv1;
+            } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_1)) {
+                optionsToSet &= ~SSL_OP_NO_TLSv1_1;
+                optionsToClear |= SSL_OP_NO_TLSv1_1;
+            } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_2)) {
+                optionsToSet &= ~SSL_OP_NO_TLSv1_2;
+                optionsToClear |= SSL_OP_NO_TLSv1_2;
             } else {
                 // error checked by checkEnabledProtocols
                 throw new IllegalStateException();
@@ -386,7 +492,9 @@
                 throw new IllegalArgumentException("protocols[" + i + "] == null");
             }
             if ((!protocol.equals(SUPPORTED_PROTOCOL_SSLV3))
-                    && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1))) {
+                    && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1))
+                    && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1_1))
+                    && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1_2))) {
                 throw new IllegalArgumentException("protocol " + protocol
                                                    + " is not supported");
             }
@@ -436,8 +544,8 @@
         return cipherSuites;
     }
 
-    private static final String SUPPORTED_COMPRESSION_METHOD_ZLIB = "ZLIB";
-    private static final String SUPPORTED_COMPRESSION_METHOD_NULL = "NULL";
+    public static final String SUPPORTED_COMPRESSION_METHOD_ZLIB = "ZLIB";
+    public static final String SUPPORTED_COMPRESSION_METHOD_NULL = "NULL";
 
     private static final String[] SUPPORTED_COMPRESSION_METHODS
             = { SUPPORTED_COMPRESSION_METHOD_ZLIB, SUPPORTED_COMPRESSION_METHOD_NULL };
@@ -516,15 +624,39 @@
     public static native String SSL_get_servername(int sslNativePointer);
 
     /**
+     * Enables NPN for all SSL connections in the context.
+     *
+     * <p>For clients this causes the NPN extension to be included in the
+     * ClientHello message.
+     *
+     * <p>For servers this causes the NPN extension to be included in the
+     * ServerHello message. The NPN extension will not be included in the
+     * ServerHello response if the client didn't include it in the ClientHello
+     * request.
+     *
+     * <p>In either case the caller should pass a non-null byte array of NPN
+     * protocols to {@link #SSL_do_handshake}.
+     */
+    public static native void SSL_CTX_enable_npn(int sslCtxNativePointer);
+
+    /**
+     * Disables NPN for all SSL connections in the context.
+     */
+    public static native void SSL_CTX_disable_npn(int sslCtxNativePointer);
+
+    /**
      * Returns the sslSessionNativePointer of the negotiated session
      */
     public static native int SSL_do_handshake(int sslNativePointer,
                                               FileDescriptor fd,
                                               SSLHandshakeCallbacks shc,
-                                              int timeout,
-                                              boolean client_mode)
+                                              int timeoutMillis,
+                                              boolean client_mode,
+                                              byte[] npnProtocols)
         throws SSLException, SocketTimeoutException, CertificateException;
 
+    public static native byte[] SSL_get_npn_negotiated_protocol(int sslNativePointer);
+
     /**
      * Currently only intended for forcing renegotiation for testing.
      * Not used within OpenSSLSocketImpl.
@@ -548,7 +680,7 @@
     public static native int SSL_read(int sslNativePointer,
                                       FileDescriptor fd,
                                       SSLHandshakeCallbacks shc,
-                                      byte[] b, int off, int len, int timeout)
+                                      byte[] b, int off, int len, int timeoutMillis)
         throws IOException;
 
     /**
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java
new file mode 100644
index 0000000..e3f6ed5
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+public class OpenSSLDSAKeyFactory extends KeyFactorySpi {
+
+    @Override
+    protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec instanceof DSAPublicKeySpec) {
+            DSAPublicKeySpec dsaKeySpec = (DSAPublicKeySpec) keySpec;
+
+            return new OpenSSLDSAPublicKey(dsaKeySpec);
+        } else if (keySpec instanceof X509EncodedKeySpec) {
+            X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec;
+
+            try {
+                final OpenSSLKey key = new OpenSSLKey(
+                        NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
+                return new OpenSSLDSAPublicKey(key);
+            } catch (Exception e) {
+                throw new InvalidKeySpecException(e);
+            }
+        }
+        throw new InvalidKeySpecException("Must use DSAPublicKeySpec or X509EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec instanceof DSAPrivateKeySpec) {
+            DSAPrivateKeySpec dsaKeySpec = (DSAPrivateKeySpec) keySpec;
+
+            return new OpenSSLDSAPrivateKey(dsaKeySpec);
+        } else if (keySpec instanceof PKCS8EncodedKeySpec) {
+            PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec;
+
+            try {
+                final OpenSSLKey key = new OpenSSLKey(
+                        NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
+                return new OpenSSLDSAPrivateKey(key);
+            } catch (Exception e) {
+                throw new InvalidKeySpecException(e);
+            }
+        }
+        throw new InvalidKeySpecException("Must use DSAPublicKeySpec or PKCS8EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+            throws InvalidKeySpecException {
+        if (key == null) {
+            throw new InvalidKeySpecException("key == null");
+        }
+
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (key instanceof DSAPublicKey) {
+            DSAPublicKey dsaKey = (DSAPublicKey) key;
+
+            if (DSAPublicKeySpec.class.equals(keySpec)) {
+                BigInteger y = dsaKey.getY();
+
+                DSAParams params = dsaKey.getParams();
+                BigInteger p = params.getP();
+                BigInteger q = params.getQ();
+                BigInteger g = params.getG();
+
+                return (T) new DSAPublicKeySpec(y, p, q, g);
+            } else if (PKCS8EncodedKeySpec.class.equals(keySpec)) {
+                return (T) new PKCS8EncodedKeySpec(key.getEncoded());
+            } else {
+                throw new InvalidKeySpecException("Must be DSAPublicKeySpec or PKCS8EncodedKeySpec");
+            }
+        } else if (key instanceof DSAPrivateKey) {
+            DSAPrivateKey dsaKey = (DSAPrivateKey) key;
+
+            if (DSAPrivateKeySpec.class.equals(keySpec)) {
+                BigInteger x = dsaKey.getX();
+
+                DSAParams params = dsaKey.getParams();
+                BigInteger p = params.getP();
+                BigInteger q = params.getQ();
+                BigInteger g = params.getG();
+
+                return (T) new DSAPrivateKeySpec(x, p, q, g);
+            } else if (X509EncodedKeySpec.class.equals(keySpec)) {
+                return (T) new X509EncodedKeySpec(dsaKey.getEncoded());
+            } else {
+                throw new InvalidKeySpecException("Must be DSAPrivateKeySpec or X509EncodedKeySpec");
+            }
+        } else {
+            throw new InvalidKeySpecException("Must be DSAPublicKey or DSAPrivateKey");
+        }
+    }
+
+    @Override
+    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        }
+
+        if (key instanceof DSAPublicKey) {
+            DSAPublicKey dsaKey = (DSAPublicKey) key;
+
+            BigInteger y = dsaKey.getY();
+
+            DSAParams params = dsaKey.getParams();
+            BigInteger p = params.getP();
+            BigInteger q = params.getQ();
+            BigInteger g = params.getG();
+
+            try {
+                return engineGeneratePublic(new DSAPublicKeySpec(y, p, q, g));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if (key instanceof DSAPrivateKey) {
+            DSAPrivateKey dsaKey = (DSAPrivateKey) key;
+
+            BigInteger x = dsaKey.getX();
+
+            DSAParams params = dsaKey.getParams();
+            BigInteger p = params.getP();
+            BigInteger q = params.getQ();
+            BigInteger g = params.getG();
+
+            try {
+                return engineGeneratePublic(new DSAPrivateKeySpec(x, p, q, g));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else {
+            throw new InvalidKeyException("Key is not DSAPublicKey or DSAPrivateKey");
+        }
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyPairGenerator.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyPairGenerator.java
new file mode 100644
index 0000000..aa0b317
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyPairGenerator.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAParameterSpec;
+
+public class OpenSSLDSAKeyPairGenerator extends KeyPairGeneratorSpi {
+
+    private int primeBits = 1024;
+
+    private SecureRandom random = null;
+
+    private byte[] g;
+
+    private byte[] p;
+
+    private byte[] q;
+
+    @Override
+    public KeyPair generateKeyPair() {
+        final byte[] seed;
+        if (random == null) {
+            seed = null;
+        } else {
+            seed = new byte[20];
+            random.nextBytes(seed);
+        }
+
+        final OpenSSLKey key = new OpenSSLKey(NativeCrypto.DSA_generate_key(primeBits, seed, g, p,
+                q));
+
+        final OpenSSLDSAPrivateKey privKey = new OpenSSLDSAPrivateKey(key);
+        final OpenSSLDSAPublicKey pubKey = new OpenSSLDSAPublicKey(key);
+
+        return new KeyPair(pubKey, privKey);
+    }
+
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        primeBits = keysize;
+        this.random = random;
+    }
+
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        this.random = random;
+
+        if (params instanceof DSAParameterSpec) {
+            DSAParameterSpec dsaParams = (DSAParameterSpec) params;
+
+            BigInteger gInt = dsaParams.getG();
+            if (gInt != null) {
+                g = gInt.toByteArray();
+            }
+
+            BigInteger pInt = dsaParams.getP();
+            if (pInt != null) {
+                p = pInt.toByteArray();
+            }
+
+            BigInteger qInt = dsaParams.getQ();
+            if (qInt != null) {
+                q = qInt.toByteArray();
+            }
+        } else if (params != null) {
+            throw new InvalidAlgorithmParameterException("Params must be DSAParameterSpec");
+        }
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAParams.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAParams.java
new file mode 100644
index 0000000..08aebf0
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAParams.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.interfaces.DSAParams;
+import java.security.spec.AlgorithmParameterSpec;
+
+public class OpenSSLDSAParams implements DSAParams, AlgorithmParameterSpec {
+
+    private OpenSSLKey key;
+
+    private boolean fetchedParams;
+
+    private BigInteger g;
+
+    private BigInteger p;
+
+    private BigInteger q;
+
+    private BigInteger y;
+
+    private BigInteger x;
+
+    OpenSSLDSAParams(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    private synchronized final void ensureReadParams() {
+        if (fetchedParams) {
+            return;
+        }
+
+        byte[][] params = NativeCrypto.get_DSA_params(key.getPkeyContext());
+        g = new BigInteger(params[0]);
+        p = new BigInteger(params[1]);
+        q = new BigInteger(params[2]);
+        if (params[3] != null) {
+            y = new BigInteger(params[3]);
+        }
+        if (params[4] != null) {
+            x = new BigInteger(params[4]);
+        }
+
+        fetchedParams = true;
+    }
+
+    @Override
+    public BigInteger getG() {
+        ensureReadParams();
+        return g;
+    }
+
+    @Override
+    public BigInteger getP() {
+        ensureReadParams();
+        return p;
+    }
+
+    @Override
+    public BigInteger getQ() {
+        ensureReadParams();
+        return q;
+    }
+
+    BigInteger getY() {
+        ensureReadParams();
+        return y;
+    }
+
+    BigInteger getX() {
+        ensureReadParams();
+        return x;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLDSAParams) {
+            OpenSSLDSAParams other = (OpenSSLDSAParams) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (key == other.getOpenSSLKey()) {
+                return true;
+            }
+        }
+
+        if (!(o instanceof DSAParams)) {
+            return false;
+        }
+
+        ensureReadParams();
+
+        DSAParams other = (DSAParams) o;
+        return g.equals(other.getG()) && p.equals(other.getP()) && q.equals(other.getQ());
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+
+        return g.hashCode() ^ p.hashCode() ^ q.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        ensureReadParams();
+
+        final StringBuilder sb = new StringBuilder("OpenSSLDSAParams{");
+        sb.append("G=");
+        sb.append(g.toString(16));
+        sb.append(",P=");
+        sb.append(p.toString(16));
+        sb.append(",Q=");
+        sb.append(q.toString(16));
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
new file mode 100644
index 0000000..7cd16f7
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+public class OpenSSLDSAPrivateKey implements DSAPrivateKey {
+    private static final long serialVersionUID = 6524734576187424628L;
+
+    private final OpenSSLKey key;
+
+    private OpenSSLDSAParams params;
+
+    OpenSSLDSAPrivateKey(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    OpenSSLDSAPrivateKey(DSAPrivateKeySpec dsaKeySpec) throws InvalidKeySpecException {
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
+                    dsaKeySpec.getP().toByteArray(),
+                    dsaKeySpec.getQ().toByteArray(),
+                    dsaKeySpec.getG().toByteArray(),
+                    null,
+                    dsaKeySpec.getX().toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    private void ensureReadParams() {
+        if (params == null) {
+            params = new OpenSSLDSAParams(key);
+        }
+    }
+
+    static OpenSSLKey getInstance(DSAPrivateKey dsaPrivateKey) throws InvalidKeyException {
+        try {
+            DSAParams dsaParams = dsaPrivateKey.getParams();
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
+                    dsaParams.getP().toByteArray(),
+                    dsaParams.getQ().toByteArray(),
+                    dsaParams.getG().toByteArray(),
+                    null,
+                    dsaPrivateKey.getX().toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    public DSAParams getParams() {
+        ensureReadParams();
+        return params;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "DSA";
+    }
+
+    @Override
+    public String getFormat() {
+        /*
+         * If we're using an OpenSSL ENGINE, there's no guarantee we can export
+         * the key. Returning {@code null} tells the caller that there's no
+         * encoded format.
+         */
+        if (key.isEngineBased()) {
+            return null;
+        }
+
+        return "PKCS#8";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        /*
+         * If we're using an OpenSSL ENGINE, there's no guarantee we can export
+         * the key. Returning {@code null} tells the caller that there's no
+         * encoded format.
+         */
+        if (key.isEngineBased()) {
+            return null;
+        }
+
+        return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
+    }
+
+    @Override
+    public BigInteger getX() {
+        ensureReadParams();
+        return params.getX();
+    }
+
+    public int getPkeyContext() {
+        return key.getPkeyContext();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLDSAPrivateKey) {
+            OpenSSLDSAPrivateKey other = (OpenSSLDSAPrivateKey) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (key.equals(other.getOpenSSLKey())) {
+                return true;
+            }
+        }
+
+        if (!(o instanceof DSAPrivateKey)) {
+            return false;
+        }
+
+        ensureReadParams();
+
+        final BigInteger x = params.getX();
+        if (x == null) {
+            /*
+             * If our X is null, we can't tell if these two private keys are
+             * equivalent. This usually happens if this key is ENGINE-based. If
+             * the other key was ENGINE-based, we should have caught it in the
+             * OpenSSLDSAPrivateKey case.
+             */
+            return false;
+        }
+
+        final DSAPrivateKey other = (DSAPrivateKey) o;
+        return x.equals(other.getX()) && params.equals(other.getParams());
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+
+        int hash = 1;
+
+        final BigInteger x = getX();
+        if (x != null) {
+            hash = hash * 3 + x.hashCode();
+        }
+
+        hash = hash * 7 + params.hashCode();
+
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("OpenSSLDSAPrivateKey{");
+
+        if (key.isEngineBased()) {
+            sb.append("key=");
+            sb.append(key);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        ensureReadParams();
+        sb.append("X=");
+        sb.append(params.getX().toString(16));
+        sb.append(',');
+        sb.append("params=");
+        sb.append(params.toString());
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPublicKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPublicKey.java
new file mode 100644
index 0000000..25869bb
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPublicKey.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+public class OpenSSLDSAPublicKey implements DSAPublicKey {
+    private static final long serialVersionUID = 5238609500353792232L;
+
+    private final OpenSSLKey key;
+
+    private OpenSSLDSAParams params;
+
+    OpenSSLDSAPublicKey(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    OpenSSLDSAPublicKey(DSAPublicKeySpec dsaKeySpec) throws InvalidKeySpecException {
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
+                    dsaKeySpec.getP().toByteArray(),
+                    dsaKeySpec.getQ().toByteArray(),
+                    dsaKeySpec.getG().toByteArray(),
+                    dsaKeySpec.getY().toByteArray(),
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    private void ensureReadParams() {
+        if (params == null) {
+            params = new OpenSSLDSAParams(key);
+        }
+    }
+
+    static OpenSSLKey getInstance(DSAPublicKey dsaPublicKey) throws InvalidKeyException {
+        try {
+            final DSAParams dsaParams = dsaPublicKey.getParams();
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
+                    dsaParams.getP().toByteArray(),
+                    dsaParams.getQ().toByteArray(),
+                    dsaParams.getG().toByteArray(),
+                    dsaPublicKey.getY().toByteArray(),
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    public DSAParams getParams() {
+        ensureReadParams();
+        return params;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "DSA";
+    }
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return NativeCrypto.i2d_PUBKEY(key.getPkeyContext());
+    }
+
+    @Override
+    public BigInteger getY() {
+        ensureReadParams();
+        return params.getY();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLDSAPublicKey) {
+            OpenSSLDSAPublicKey other = (OpenSSLDSAPublicKey) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (key.equals(other.getOpenSSLKey())) {
+                return true;
+            }
+        }
+
+        if (!(o instanceof DSAPublicKey)) {
+            return false;
+        }
+
+        ensureReadParams();
+
+        DSAPublicKey other = (DSAPublicKey) o;
+        return params.getY().equals(other.getY()) && params.equals(other.getParams());
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+
+        return params.getY().hashCode() ^ params.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        ensureReadParams();
+
+        final StringBuilder sb = new StringBuilder("OpenSSLDSAPublicKey{");
+        sb.append("Y=");
+        sb.append(params.getY().toString(16));
+        sb.append(',');
+        sb.append("params=");
+        sb.append(params.toString());
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
new file mode 100644
index 0000000..e91e6d8
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+
+public class OpenSSLEngine {
+    static {
+        NativeCrypto.ENGINE_load_dynamic();
+    }
+
+    /** The ENGINE's native handle. */
+    private final int ctx;
+
+    public static OpenSSLEngine getInstance(String engine) throws IllegalArgumentException {
+        if (engine == null) {
+            throw new NullPointerException("engine == null");
+        }
+
+        final int engineCtx = NativeCrypto.ENGINE_by_id(engine);
+
+        if (engineCtx == 0) {
+            throw new IllegalArgumentException("Unknown ENGINE id: " + engine);
+        }
+
+        return new OpenSSLEngine(engineCtx);
+    }
+
+    private OpenSSLEngine(int engineCtx) {
+        ctx = engineCtx;
+
+        if (NativeCrypto.ENGINE_init(engineCtx) == 0) {
+            throw new IllegalArgumentException("Could not initialize engine");
+        }
+    }
+
+    public PrivateKey getPrivateKeyById(String id) throws InvalidKeyException {
+        if (id == null) {
+            throw new NullPointerException("id == null");
+        }
+
+        final int keyRef = NativeCrypto.ENGINE_load_private_key(ctx, id);
+        if (keyRef == 0) {
+            return null;
+        }
+
+        final int keyType = NativeCrypto.EVP_PKEY_type(keyRef);
+        switch (keyType) {
+            case NativeCrypto.EVP_PKEY_RSA:
+                return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this));
+            case NativeCrypto.EVP_PKEY_DSA:
+                return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this));
+            default:
+                throw new InvalidKeyException("Unknown key type: " + keyType);
+        }
+    }
+
+    int getEngineContext() {
+        return ctx;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            NativeCrypto.ENGINE_finish(ctx);
+            NativeCrypto.ENGINE_free(ctx);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof OpenSSLEngine)) {
+            return false;
+        }
+
+        OpenSSLEngine other = (OpenSSLEngine) o;
+
+        return other.getEngineContext() == ctx;
+    }
+
+    @Override
+    public int hashCode() {
+        return ctx;
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
new file mode 100644
index 0000000..90eb0e2
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+class OpenSSLKey {
+    private final int ctx;
+
+    private final OpenSSLEngine engine;
+
+    OpenSSLKey(int ctx) {
+        this.ctx = ctx;
+        engine = null;
+    }
+
+    OpenSSLKey(int ctx, OpenSSLEngine engine) {
+        this.ctx = ctx;
+        this.engine = engine;
+    }
+
+    int getPkeyContext() {
+        return ctx;
+    }
+
+    OpenSSLEngine getEngine() {
+        return engine;
+    }
+
+    boolean isEngineBased() {
+        return engine != null;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (ctx != 0) {
+                NativeCrypto.EVP_PKEY_free(ctx);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof OpenSSLKey)) {
+            return false;
+        }
+
+        OpenSSLKey other = (OpenSSLKey) o;
+        if (ctx != other.getPkeyContext()) {
+            return false;
+        }
+
+        if (engine == null) {
+            return other.getEngine() == null;
+        } else {
+            return engine.equals(other.getEngine());
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 1;
+        hash = hash * 17 + ctx;
+        hash = hash * 31 + (engine == null ? 0 : engine.getEngineContext());
+        return hash;
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
index 4c8ad3a..97753cf 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
@@ -19,16 +19,21 @@
 import java.security.Provider;
 
 public final class OpenSSLProvider extends Provider {
+    public static final String PROVIDER_NAME = "AndroidOpenSSL";
 
     public OpenSSLProvider() {
-        super("AndroidOpenSSL", 1.0, "Android's OpenSSL-backed security provider");
+        super(PROVIDER_NAME, 1.0, "Android's OpenSSL-backed security provider");
 
+        // SSL Contexts
         put("SSLContext.SSL", OpenSSLContextImpl.class.getName());
         put("SSLContext.SSLv3", OpenSSLContextImpl.class.getName());
         put("SSLContext.TLS", OpenSSLContextImpl.class.getName());
         put("SSLContext.TLSv1", OpenSSLContextImpl.class.getName());
+        put("SSLContext.TLSv1.1", OpenSSLContextImpl.class.getName());
+        put("SSLContext.TLSv1.2", OpenSSLContextImpl.class.getName());
         put("SSLContext.Default", DefaultSSLContextImpl.class.getName());
 
+        // Message Digests
         put("MessageDigest.SHA-1",
             "org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK$SHA1");
         put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
@@ -54,6 +59,54 @@
             "org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK$MD5");
         put("Alg.Alias.MessageDigest.1.2.840.113549.2.5", "MD5");
 
-        // TODO Flush out implementation of OpenSSLSignature so it can be registered here
+        // KeyPairGenerators
+        put("KeyPairGenerator.RSA", OpenSSLRSAKeyPairGenerator.class.getName());
+        put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.1", "RSA");
+
+        put("KeyPairGenerator.DSA", OpenSSLDSAKeyPairGenerator.class.getName());
+
+        // KeyFactory
+
+        put("KeyFactory.RSA", OpenSSLRSAKeyFactory.class.getName());
+        put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA");
+
+        // put("KeyFactory.DSA", OpenSSLDSAKeyFactory.class.getName());
+
+        // Signatures
+        put("Signature.MD5WithRSAEncryption", OpenSSLSignature.MD5RSA.class.getName());
+        put("Alg.Alias.Signature.MD5WithRSA", "MD5WithRSAEncryption");
+        put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSAEncryption");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSAEncryption");
+        put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1",
+                "MD5WithRSAEncryption");
+
+        put("Signature.SHA1WithRSAEncryption", OpenSSLSignature.SHA1RSA.class.getName());
+        put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSAEncryption");
+        put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSAEncryption");
+        put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSAEncryption");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSAEncryption");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSAEncryption");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSAEncryption");
+        put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSAEncryption");
+
+        put("Signature.SHA256WithRSAEncryption", OpenSSLSignature.SHA256RSA.class.getName());
+        put("Alg.Alias.Signature.SHA256WithRSA", "SHA256WithRSAEncryption");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSAEncryption");
+
+        put("Signature.SHA384WithRSAEncryption", OpenSSLSignature.SHA384RSA.class.getName());
+        put("Alg.Alias.Signature.SHA384WithRSA", "SHA384WithRSAEncryption");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSAEncryption");
+
+        put("Signature.SHA512WithRSAEncryption", OpenSSLSignature.SHA512RSA.class.getName());
+        put("Alg.Alias.Signature.SHA512WithRSA", "SHA512WithRSAEncryption");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSAEncryption");
+
+        put("Signature.SHA1withDSA", OpenSSLSignature.SHA1DSA.class.getName());
+        put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA");
+        put("Alg.Alias.Signature.DSA", "SHA1withDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.1", "SHA1withDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "SHA1withDSA");
+        put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
+        put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA");
     }
 }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java
new file mode 100644
index 0000000..49d31d3
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+public class OpenSSLRSAKeyFactory<T, S> extends KeyFactorySpi {
+
+    @Override
+    protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec instanceof RSAPublicKeySpec) {
+            RSAPublicKeySpec rsaKeySpec = (RSAPublicKeySpec) keySpec;
+
+            return new OpenSSLRSAPublicKey(rsaKeySpec);
+        } else if (keySpec instanceof X509EncodedKeySpec) {
+            X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec;
+
+            try {
+                final OpenSSLKey key = new OpenSSLKey(
+                        NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
+                return new OpenSSLRSAPublicKey(key);
+            } catch (Exception e) {
+                throw new InvalidKeySpecException(e);
+            }
+        }
+        throw new InvalidKeySpecException("Must use RSAPublicKeySpec or X509EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec instanceof RSAPrivateCrtKeySpec) {
+            RSAPrivateCrtKeySpec rsaKeySpec = (RSAPrivateCrtKeySpec) keySpec;
+
+            return new OpenSSLRSAPrivateCrtKey(rsaKeySpec);
+        } else if (keySpec instanceof RSAPrivateKeySpec) {
+            RSAPrivateKeySpec rsaKeySpec = (RSAPrivateKeySpec) keySpec;
+
+            return new OpenSSLRSAPrivateKey(rsaKeySpec);
+        } else if (keySpec instanceof PKCS8EncodedKeySpec) {
+            PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec;
+
+            try {
+                final OpenSSLKey key = new OpenSSLKey(
+                        NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
+                return OpenSSLRSAPrivateKey.getInstance(key);
+            } catch (Exception e) {
+                throw new InvalidKeySpecException(e);
+            }
+        }
+        throw new InvalidKeySpecException("Must use RSAPublicKeySpec or PKCS8EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+            throws InvalidKeySpecException {
+        if (key == null) {
+            throw new InvalidKeySpecException("key == null");
+        }
+
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (key instanceof RSAPublicKey) {
+            RSAPublicKey rsaKey = (RSAPublicKey) key;
+
+            if (RSAPublicKeySpec.class.equals(keySpec)) {
+                BigInteger modulus = rsaKey.getModulus();
+                BigInteger publicExponent = rsaKey.getPublicExponent();
+                return (T) new RSAPublicKeySpec(modulus, publicExponent);
+            } else if (X509EncodedKeySpec.class.equals(keySpec)) {
+                return (T) new X509EncodedKeySpec(key.getEncoded());
+            } else {
+                throw new InvalidKeySpecException("Must be RSAPublicKeySpec or X509EncodedKeySpec");
+            }
+        } else if (key instanceof RSAPrivateCrtKey) {
+            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
+
+            if (RSAPrivateKeySpec.class.equals(keySpec)) {
+                BigInteger modulus = rsaKey.getModulus();
+                BigInteger privateExponent = rsaKey.getPrivateExponent();
+                return (T) new RSAPrivateKeySpec(modulus, privateExponent);
+            } else if (RSAPrivateCrtKeySpec.class.equals(keySpec)) {
+                BigInteger modulus = rsaKey.getModulus();
+                BigInteger publicExponent = rsaKey.getPublicExponent();
+                BigInteger privateExponent = rsaKey.getPrivateExponent();
+                BigInteger primeP = rsaKey.getPrimeP();
+                BigInteger primeQ = rsaKey.getPrimeQ();
+                BigInteger primeExponentP = rsaKey.getPrimeExponentP();
+                BigInteger primeExponentQ = rsaKey.getPrimeExponentQ();
+                BigInteger crtCoefficient = rsaKey.getCrtCoefficient();
+                return (T) new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent,
+                        primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);
+            } else if (PKCS8EncodedKeySpec.class.equals(keySpec)) {
+                return (T) new PKCS8EncodedKeySpec(rsaKey.getEncoded());
+            } else {
+                throw new InvalidKeySpecException(
+                        "Must be RSAPrivateKeySpec or or RSAPrivateCrtKeySpec or PKCS8EncodedKeySpec");
+            }
+        } else if (key instanceof RSAPrivateKey) {
+            RSAPrivateKey rsaKey = (RSAPrivateKey) key;
+
+            if (RSAPrivateKeySpec.class.equals(keySpec)) {
+                BigInteger modulus = rsaKey.getModulus();
+                BigInteger privateExponent = rsaKey.getPrivateExponent();
+                return (T) new RSAPrivateKeySpec(modulus, privateExponent);
+            } else if (RSAPrivateCrtKeySpec.class.equals(keySpec)) {
+                BigInteger modulus = rsaKey.getModulus();
+                BigInteger privateExponent = rsaKey.getPrivateExponent();
+                return (T) new RSAPrivateCrtKeySpec(modulus, null, privateExponent, null, null,
+                        null, null, null);
+            } else if (PKCS8EncodedKeySpec.class.equals(keySpec)) {
+                return (T) new PKCS8EncodedKeySpec(rsaKey.getEncoded());
+            } else {
+                throw new InvalidKeySpecException(
+                        "Must be RSAPrivateKeySpec or PKCS8EncodedKeySpec");
+            }
+        } else {
+            throw new InvalidKeySpecException("Must be RSAPublicKey or RSAPrivateKey");
+        }
+    }
+
+    @Override
+    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        }
+
+        if (key instanceof RSAPublicKey) {
+            RSAPublicKey rsaKey = (RSAPublicKey) key;
+
+            try {
+                return engineGeneratePublic(new RSAPublicKeySpec(rsaKey.getModulus(),
+                        rsaKey.getPublicExponent()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if (key instanceof RSAPrivateCrtKey) {
+            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
+            BigInteger modulus = rsaKey.getModulus();
+            BigInteger publicExponent = rsaKey.getPublicExponent();
+            BigInteger privateExponent = rsaKey.getPrivateExponent();
+            BigInteger primeP = rsaKey.getPrimeP();
+            BigInteger primeQ = rsaKey.getPrimeQ();
+            BigInteger primeExponentP = rsaKey.getPrimeExponentP();
+            BigInteger primeExponentQ = rsaKey.getPrimeExponentQ();
+            BigInteger crtCoefficient = rsaKey.getCrtCoefficient();
+
+            try {
+                return engineGeneratePrivate(new RSAPrivateCrtKeySpec(modulus, publicExponent,
+                        privateExponent, primeP, primeQ, primeExponentP, primeExponentQ,
+                        crtCoefficient));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if (key instanceof RSAPrivateKey) {
+            RSAPrivateKey rsaKey = (RSAPrivateKey) key;
+            BigInteger modulus = rsaKey.getModulus();
+            BigInteger privateExponent = rsaKey.getPrivateExponent();
+
+            try {
+                return engineGeneratePrivate(new RSAPrivateKeySpec(modulus, privateExponent));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else {
+            throw new InvalidKeyException(
+                    "Key must be RSAPublicKey or RSAPrivateCrtKey or RSAPrivateKey");
+        }
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyPairGenerator.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyPairGenerator.java
new file mode 100644
index 0000000..2d19d4e
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyPairGenerator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+
+public class OpenSSLRSAKeyPairGenerator extends KeyPairGeneratorSpi {
+    /**
+     * Default modulus size is 0x10001 (65537)
+     */
+    private byte[] publicExponent = new byte[] {
+            0x01, 0x00, 0x01
+    };
+
+    /**
+     * Default RSA key size 2048 bits.
+     */
+    private int modulusBits = 2048;
+
+    @Override
+    public KeyPair generateKeyPair() {
+        final OpenSSLKey key = new OpenSSLKey(NativeCrypto.RSA_generate_key_ex(modulusBits,
+                publicExponent));
+
+        PrivateKey privKey = OpenSSLRSAPrivateKey.getInstance(key);
+        PublicKey pubKey = new OpenSSLRSAPublicKey(key);
+
+        return new KeyPair(pubKey, privKey);
+    }
+
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        this.modulusBits = keysize;
+    }
+
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        if (!(params instanceof RSAKeyGenParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only RSAKeyGenParameterSpec supported");
+        }
+
+        RSAKeyGenParameterSpec spec = (RSAKeyGenParameterSpec) params;
+
+        final BigInteger publicExponent = spec.getPublicExponent();
+        if (publicExponent != null) {
+            this.publicExponent = publicExponent.toByteArray();
+        }
+
+        this.modulusBits = spec.getKeysize();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
new file mode 100644
index 0000000..8376515
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+
+public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSAPrivateCrtKey {
+    private BigInteger publicExponent;
+
+    private BigInteger primeP;
+
+    private BigInteger primeQ;
+
+    private BigInteger primeExponentP;
+
+    private BigInteger primeExponentQ;
+
+    private BigInteger crtCoefficient;
+
+    OpenSSLRSAPrivateCrtKey(OpenSSLKey key) {
+        super(key);
+    }
+
+    OpenSSLRSAPrivateCrtKey(OpenSSLKey key, byte[][] params) {
+        super(key, params);
+    }
+
+    public OpenSSLRSAPrivateCrtKey(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        super(init(rsaKeySpec));
+    }
+
+    private static OpenSSLKey init(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        BigInteger modulus = rsaKeySpec.getModulus();
+        BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeySpecException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeySpecException("privateExponent == null");
+        }
+
+        try {
+            /*
+             * OpenSSL uses the public modulus to do RSA blinding. If
+             * the public modulus is not available, the call to
+             * EVP_PKEY_new_RSA will turn off blinding for this key
+             * instance.
+             */
+            final BigInteger publicExponent = rsaKeySpec.getPublicExponent();
+            final BigInteger primeP = rsaKeySpec.getPrimeP();
+            final BigInteger primeQ = rsaKeySpec.getPrimeQ();
+            final BigInteger primeExponentP = rsaKeySpec.getPrimeExponentP();
+            final BigInteger primeExponentQ = rsaKeySpec.getPrimeExponentQ();
+            final BigInteger crtCoefficient = rsaKeySpec.getCrtCoefficient();
+
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    publicExponent == null ? null : publicExponent.toByteArray(),
+                    privateExponent.toByteArray(),
+                    primeP == null ? null : primeP.toByteArray(),
+                    primeQ == null ? null : primeQ.toByteArray(),
+                    primeExponentP == null ? null : primeExponentP.toByteArray(),
+                    primeExponentQ == null ? null : primeExponentQ.toByteArray(),
+                    crtCoefficient == null ? null : crtCoefficient.toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLKey getInstance(RSAPrivateCrtKey rsaPrivateKey) throws InvalidKeyException {
+        BigInteger modulus = rsaPrivateKey.getModulus();
+        BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeyException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeyException("privateExponent == null");
+        }
+
+        try {
+            /*
+             * OpenSSL uses the public modulus to do RSA blinding. If
+             * the public modulus is not available, the call to
+             * EVP_PKEY_new_RSA will turn off blinding for this key
+             * instance.
+             */
+            final BigInteger publicExponent = rsaPrivateKey.getPublicExponent();
+            final BigInteger primeP = rsaPrivateKey.getPrimeP();
+            final BigInteger primeQ = rsaPrivateKey.getPrimeQ();
+            final BigInteger primeExponentP = rsaPrivateKey.getPrimeExponentP();
+            final BigInteger primeExponentQ = rsaPrivateKey.getPrimeExponentQ();
+            final BigInteger crtCoefficient = rsaPrivateKey.getCrtCoefficient();
+
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    publicExponent == null ? null : publicExponent.toByteArray(),
+                    privateExponent.toByteArray(),
+                    primeP == null ? null : primeP.toByteArray(),
+                    primeQ == null ? null : primeQ.toByteArray(),
+                    primeExponentP == null ? null : primeExponentP.toByteArray(),
+                    primeExponentQ == null ? null : primeExponentQ.toByteArray(),
+                    crtCoefficient == null ? null : crtCoefficient.toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    synchronized void readParams(byte[][] params) {
+        super.readParams(params);
+        // params[0] read in super.readParams
+        if (params[1] != null) {
+            publicExponent = new BigInteger(params[1]);
+        }
+        // params[2] read in super.readParams
+        if (params[3] != null) {
+            primeP = new BigInteger(params[3]);
+        }
+        if (params[4] != null) {
+            primeQ = new BigInteger(params[4]);
+        }
+        if (params[5] != null) {
+            primeExponentP = new BigInteger(params[5]);
+        }
+        if (params[6] != null) {
+            primeExponentQ = new BigInteger(params[6]);
+        }
+        if (params[7] != null) {
+            crtCoefficient = new BigInteger(params[7]);
+        }
+    }
+
+    @Override
+    public BigInteger getPublicExponent() {
+        ensureReadParams();
+        return publicExponent;
+    }
+
+    @Override
+    public BigInteger getPrimeP() {
+        ensureReadParams();
+        return primeP;
+    }
+
+    @Override
+    public BigInteger getPrimeQ() {
+        ensureReadParams();
+        return primeQ;
+    }
+
+    @Override
+    public BigInteger getPrimeExponentP() {
+        ensureReadParams();
+        return primeExponentP;
+    }
+
+    @Override
+    public BigInteger getPrimeExponentQ() {
+        ensureReadParams();
+        return primeExponentQ;
+    }
+
+    @Override
+    public BigInteger getCrtCoefficient() {
+        ensureReadParams();
+        return crtCoefficient;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLRSAPrivateCrtKey) {
+            OpenSSLRSAPrivateCrtKey other = (OpenSSLRSAPrivateCrtKey) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (getOpenSSLKey().equals(other.getOpenSSLKey())) {
+                return true;
+            }
+        }
+
+        if (o instanceof RSAPrivateCrtKey) {
+            ensureReadParams();
+            RSAPrivateCrtKey other = (RSAPrivateCrtKey) o;
+
+            return getModulus().equals(other.getModulus())
+                    && publicExponent.equals(other.getPublicExponent())
+                    && getPrivateExponent().equals(other.getPrivateExponent())
+                    && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ())
+                    && primeExponentP.equals(other.getPrimeExponentP())
+                    && primeExponentQ.equals(other.getPrimeExponentQ())
+                    && crtCoefficient.equals(other.getCrtCoefficient());
+        }
+
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        int hashCode = super.hashCode();
+        if (publicExponent != null) {
+            hashCode ^= publicExponent.hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{");
+
+        if (getOpenSSLKey().isEngineBased()) {
+            sb.append("key=");
+            sb.append(getOpenSSLKey());
+            sb.append('}');
+            return sb.toString();
+        }
+
+        ensureReadParams();
+        sb.append("modulus=");
+        sb.append(getModulus().toString(16));
+        sb.append(',');
+
+        if (publicExponent != null) {
+            sb.append("publicExponent=");
+            sb.append(publicExponent.toString(16));
+            sb.append(',');
+        }
+
+        sb.append("privateExponent=");
+        sb.append(getPrivateExponent().toString(16));
+        sb.append(',');
+
+        if (primeP != null) {
+            sb.append("primeP=");
+            sb.append(primeP.toString(16));
+            sb.append(',');
+        }
+
+        if (primeQ != null) {
+            sb.append("primeQ=");
+            sb.append(primeQ.toString(16));
+            sb.append(',');
+        }
+
+        if (primeExponentP != null) {
+            sb.append("primeExponentP=");
+            sb.append(primeExponentP.toString(16));
+            sb.append(',');
+        }
+
+        if (primeExponentQ != null) {
+            sb.append("primeExponentQ=");
+            sb.append(primeExponentQ.toString(16));
+            sb.append(',');
+        }
+
+        if (crtCoefficient != null) {
+            sb.append("crtCoefficient=");
+            sb.append(crtCoefficient.toString(16));
+            sb.append(',');
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
new file mode 100644
index 0000000..c9fa178
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateKeySpec;
+
+public class OpenSSLRSAPrivateKey implements RSAPrivateKey {
+    private static final long serialVersionUID = 4872170254439578735L;
+
+    private final OpenSSLKey key;
+
+    private boolean fetchedParams;
+
+    private BigInteger modulus;
+
+    private BigInteger privateExponent;
+
+    OpenSSLRSAPrivateKey(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    OpenSSLRSAPrivateKey(OpenSSLKey key, byte[][] params) {
+        this(key);
+        readParams(params);
+        fetchedParams = true;
+    }
+
+    final OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    public OpenSSLRSAPrivateKey(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        this(init(rsaKeySpec));
+    }
+
+    private static OpenSSLKey init(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        final BigInteger modulus = rsaKeySpec.getModulus();
+        final BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeySpecException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeySpecException("privateExponent == null");
+        }
+
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    null,
+                    privateExponent.toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLRSAPrivateKey getInstance(OpenSSLKey key) {
+      byte[][] params = NativeCrypto.get_RSA_private_params(key.getPkeyContext());
+      if (params[1] != null) {
+          return new OpenSSLRSAPrivateCrtKey(key, params);
+      }
+      return new OpenSSLRSAPrivateKey(key, params);
+    }
+
+    static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException {
+        final BigInteger modulus = rsaPrivateKey.getModulus();
+        final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeyException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeyException("privateExponent == null");
+        }
+
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    null,
+                    privateExponent.toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    synchronized final void ensureReadParams() {
+        if (fetchedParams) {
+            return;
+        }
+        readParams(NativeCrypto.get_RSA_private_params(key.getPkeyContext()));
+        fetchedParams = true;
+    }
+
+    void readParams(byte[][] params) {
+        if (params[0] == null) {
+            throw new NullPointerException("modulus == null");
+        } else if (params[2] == null && !key.isEngineBased()) {
+            throw new NullPointerException("privateExponent == null");
+        }
+
+        modulus = new BigInteger(params[0]);
+
+        // ENGINE-based keys are not guaranteed to have a private exponent.
+        if (params[2] != null) {
+            privateExponent = new BigInteger(params[2]);
+        }
+    }
+
+    @Override
+    public final BigInteger getPrivateExponent() {
+        ensureReadParams();
+        return privateExponent;
+    }
+
+    @Override
+    public final BigInteger getModulus() {
+        ensureReadParams();
+        return modulus;
+    }
+
+    @Override
+    public final byte[] getEncoded() {
+        /*
+         * If we're using an OpenSSL ENGINE, there's no guarantee we can export
+         * the key. Returning {@code null} tells the caller that there's no
+         * encoded format.
+         */
+        if (key.isEngineBased()) {
+            return null;
+        }
+
+        return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
+    }
+
+    public final String getFormat() {
+        /*
+         * If we're using an OpenSSL ENGINE, there's no guarantee we can export
+         * the key. Returning {@code null} tells the caller that there's no
+         * encoded format.
+         */
+        if (key.isEngineBased()) {
+            return null;
+        }
+
+        return "PKCS#8";
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return "RSA";
+    }
+
+    public int getPkeyContext() {
+        return key.getPkeyContext();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLRSAPrivateKey) {
+            OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (key.equals(other.getOpenSSLKey())) {
+                return true;
+            }
+        }
+
+        if (o instanceof RSAPrivateKey) {
+            ensureReadParams();
+            RSAPrivateKey other = (RSAPrivateKey) o;
+
+            return modulus.equals(other.getModulus())
+                    && privateExponent.equals(other.getPrivateExponent());
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+        int hash = 1;
+
+        hash = hash * 3 + modulus.hashCode();
+        if (privateExponent != null) {
+            hash = hash * 7 + privateExponent.hashCode();
+        }
+
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
+
+        if (key.isEngineBased()) {
+            sb.append("key=");
+            sb.append(key);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        ensureReadParams();
+        sb.append("modulus=");
+        sb.append(modulus.toString(16));
+        sb.append(',');
+
+        sb.append("privateExponent=");
+        sb.append(privateExponent.toString(16));
+        sb.append(',');
+
+        return sb.toString();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPublicKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPublicKey.java
new file mode 100644
index 0000000..1613cf0
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPublicKey.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2012 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.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+
+public class OpenSSLRSAPublicKey implements RSAPublicKey {
+    private static final long serialVersionUID = 123125005824688292L;
+
+    private final OpenSSLKey key;
+
+    private BigInteger publicExponent;
+
+    private BigInteger modulus;
+
+    private boolean fetchedParams;
+
+    OpenSSLRSAPublicKey(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    OpenSSLRSAPublicKey(RSAPublicKeySpec spec) throws InvalidKeySpecException {
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    spec.getModulus().toByteArray(),
+                    spec.getPublicExponent().toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLKey getInstance(RSAPublicKey rsaPublicKey) throws InvalidKeyException {
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    rsaPublicKey.getModulus().toByteArray(),
+                    rsaPublicKey.getPublicExponent().toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "RSA";
+    }
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return NativeCrypto.i2d_PUBKEY(key.getPkeyContext());
+    }
+
+    private void ensureReadParams() {
+        if (fetchedParams) {
+            return;
+        }
+
+        byte[][] params = NativeCrypto.get_RSA_public_params(key.getPkeyContext());
+        modulus = new BigInteger(params[0]);
+        publicExponent = new BigInteger(params[1]);
+
+        fetchedParams = true;
+    }
+
+    @Override
+    public BigInteger getModulus() {
+        ensureReadParams();
+        return modulus;
+    }
+
+    @Override
+    public BigInteger getPublicExponent() {
+        ensureReadParams();
+        return publicExponent;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLRSAPublicKey) {
+            OpenSSLRSAPublicKey other = (OpenSSLRSAPublicKey) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (key.equals(other.getOpenSSLKey())) {
+                return true;
+            }
+        }
+
+        if (!(o instanceof RSAPublicKey)) {
+            return false;
+        }
+
+        ensureReadParams();
+
+        RSAPublicKey other = (RSAPublicKey) o;
+        return modulus.equals(other.getModulus())
+                && publicExponent.equals(other.getPublicExponent());
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+
+        return modulus.hashCode() ^ publicExponent.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        ensureReadParams();
+
+        final StringBuilder sb = new StringBuilder("OpenSSLRSAPublicKey{");
+        sb.append("modulus=");
+        sb.append(modulus.toString(16));
+        sb.append(',');
+        sb.append("publicExponent=");
+        sb.append(publicExponent.toString(16));
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
index 20219e0..841c31c 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
@@ -27,9 +27,6 @@
 
 /**
  * OpenSSL-based implementation of server sockets.
- *
- * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
- * later, for example in the package.html or a separate reference document.
  */
 public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
     private final SSLParametersImpl sslParameters;
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java
index bcccda7..370e7a4 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java
@@ -23,66 +23,20 @@
 import java.security.PublicKey;
 import java.security.Signature;
 import java.security.SignatureException;
-import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
 
 /**
  * Implements the subset of the JDK Signature interface needed for
  * signature verification using OpenSSL.
  */
 public class OpenSSLSignature extends Signature {
-
-    private static Map<String,Class<? extends OpenSSLSignature>> jdkToOpenSsl
-            = new HashMap<String,Class<? extends OpenSSLSignature>>();
-    private static void register(String algorithm, Class implementation) {
-        jdkToOpenSsl.put(algorithm.toUpperCase(Locale.US), implementation);
-    }
-    private static Class lookup(String algorithm) {
-        return jdkToOpenSsl.get(algorithm.toUpperCase(Locale.US));
-    }
-
-    static {
-        // TODO Finish OpenSSLSignature implementation and move
-        // registration information to the OpenSSLProvider
-        register("MD5WithRSAEncryption", MD5RSA.class);
-        register("MD5WithRSA", MD5RSA.class);
-        register("MD5/RSA", MD5RSA.class);
-        register("1.2.840.113549.1.1.4", MD5RSA.class);
-        register("1.2.840.113549.2.5with1.2.840.113549.1.1.1", MD5RSA.class);
-
-        register("SHA1WithRSAEncryption", SHA1RSA.class);
-        register("SHA1WithRSA", SHA1RSA.class);
-        register("SHA1/RSA", SHA1RSA.class);
-        register("SHA-1/RSA", SHA1RSA.class);
-        register("1.2.840.113549.1.1.5", SHA1RSA.class);
-        register("1.3.14.3.2.26with1.2.840.113549.1.1.1", SHA1RSA.class);
-        register("1.3.14.3.2.26with1.2.840.113549.1.1.5", SHA1RSA.class);
-        register("1.3.14.3.2.29", SHA1RSA.class);
-
-        register("SHA256WithRSAEncryption", SHA256RSA.class);
-        register("SHA256WithRSA", SHA256RSA.class);
-        register("1.2.840.113549.1.1.11", SHA256RSA.class);
-
-        register("SHA384WithRSAEncryption", SHA384RSA.class);
-        register("SHA384WithRSA", SHA384RSA.class);
-        register("1.2.840.113549.1.1.12", SHA384RSA.class);
-
-        register("SHA512WithRSAEncryption", SHA512RSA.class);
-        register("SHA512WithRSA", SHA512RSA.class);
-        register("1.2.840.113549.1.1.13", SHA512RSA.class);
-
-        register("SHA1withDSA", SHA1DSA.class);
-        register("SHA/DSA", SHA1DSA.class);
-        register("DSA", SHA1DSA.class);
-        register("1.3.14.3.2.26with1.2.840.10040.4.1", SHA1DSA.class);
-        register("1.3.14.3.2.26with1.2.840.10040.4.3", SHA1DSA.class);
-        register("DSAWithSHA1", SHA1DSA.class);
-        register("1.2.840.10040.4.3", SHA1DSA.class);
-    }
+    private static enum EngineType {
+        RSA, DSA,
+    };
 
     /**
      * Holds a pointer to the native message digest context.
@@ -90,14 +44,14 @@
     private int ctx;
 
     /**
-     * Holds a pointer to the native DSA key.
+     * The current OpenSSL key we're operating on.
      */
-    private int dsa;
+    private OpenSSLKey key;
 
     /**
-     * Holds a pointer to the native RSA key.
+     * Holds the type of the Java algorithm.
      */
-    private int rsa;
+    private final EngineType engineType;
 
     /**
      * Holds the OpenSSL name of the algorithm (lower case, no dashes).
@@ -112,34 +66,10 @@
     /**
      * Creates a new OpenSSLSignature instance for the given algorithm name.
      *
-     * @param algorithm The name of the algorithm, e.g. "SHA1WithRSA".
-     *
-     * @return The new OpenSSLSignature instance.
-     *
-     * @throws RuntimeException In case of problems.
-     */
-    public static OpenSSLSignature getInstance(String algorithm) throws NoSuchAlgorithmException {
-        // System.out.println("getInstance() invoked with " + algorithm);
-
-        Class <? extends OpenSSLSignature> clazz = lookup(algorithm);
-        if (clazz == null) {
-            throw new NoSuchAlgorithmException(algorithm);
-        }
-        try {
-            return clazz.newInstance();
-        } catch (InstantiationException e) {
-            throw new NoSuchAlgorithmException(algorithm, e);
-        } catch (IllegalAccessException e) {
-            throw new NoSuchAlgorithmException(algorithm, e);
-        }
-    }
-
-    /**
-     * Creates a new OpenSSLSignature instance for the given algorithm name.
-     *
      * @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1".
      */
-    private OpenSSLSignature(String algorithm) throws NoSuchAlgorithmException {
+    private OpenSSLSignature(String algorithm, EngineType engineType)
+            throws NoSuchAlgorithmException {
         super(algorithm);
 
         // We don't support MD2
@@ -147,6 +77,7 @@
             throw new NoSuchAlgorithmException(algorithm);
         }
 
+        this.engineType = engineType;
         this.evpAlgorithm = algorithm;
     }
 
@@ -159,8 +90,24 @@
     @Override
     protected void engineUpdate(byte[] input, int offset, int len) {
         if (state == SIGN) {
-            throw new UnsupportedOperationException();
+            if (ctx == 0) {
+                try {
+                    ctx = NativeCrypto.EVP_SignInit(evpAlgorithm);
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+
+            NativeCrypto.EVP_SignUpdate(ctx, input, offset, len);
         } else {
+            if (ctx == 0) {
+                try {
+                    ctx = NativeCrypto.EVP_VerifyInit(evpAlgorithm);
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+
             NativeCrypto.EVP_VerifyUpdate(ctx, input, offset, len);
         }
     }
@@ -172,43 +119,84 @@
 
     @Override
     protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
-        throw new UnsupportedOperationException();
+        destroyContextIfExists();
+
+        if (privateKey instanceof OpenSSLDSAPrivateKey) {
+            if (engineType != EngineType.DSA) {
+                throw new InvalidKeyException("Signature not initialized as DSA");
+            }
+
+            OpenSSLDSAPrivateKey dsaPrivateKey = (OpenSSLDSAPrivateKey) privateKey;
+            key = dsaPrivateKey.getOpenSSLKey();
+        } else if (privateKey instanceof DSAPrivateKey) {
+            if (engineType != EngineType.DSA) {
+                throw new InvalidKeyException("Signature not initialized as DSA");
+            }
+
+            DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privateKey;
+            key = OpenSSLDSAPrivateKey.getInstance(dsaPrivateKey);
+        } else if (privateKey instanceof OpenSSLRSAPrivateKey) {
+            if (engineType != EngineType.RSA) {
+                throw new InvalidKeyException("Signature not initialized as RSA");
+            }
+
+            OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
+            key = rsaPrivateKey.getOpenSSLKey();
+        } else if (privateKey instanceof RSAPrivateCrtKey) {
+            if (engineType != EngineType.RSA) {
+                throw new InvalidKeyException("Signature not initialized as RSA");
+            }
+
+            RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
+            key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+        } else if (privateKey instanceof RSAPrivateKey) {
+            if (engineType != EngineType.RSA) {
+                throw new InvalidKeyException("Signature not initialized as RSA");
+            }
+
+            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
+            key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+        } else {
+            throw new InvalidKeyException("Need DSA or RSA private key");
+        }
     }
 
     @Override
     protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
-        // System.out.println("engineInitVerify() invoked with "
-        //                    + publicKey.getClass().getCanonicalName());
+        // If we had an existing context, destroy it first.
+        destroyContextIfExists();
 
-        if (publicKey instanceof DSAPublicKey) {
-            try {
-                DSAPublicKey dsaPublicKey = (DSAPublicKey)publicKey;
-                DSAParams dsaParams = dsaPublicKey.getParams();
-                dsa = NativeCrypto.EVP_PKEY_new_DSA(dsaParams.getP().toByteArray(),
-                        dsaParams.getQ().toByteArray(), dsaParams.getG().toByteArray(),
-                        dsaPublicKey.getY().toByteArray(), null);
-
-            } catch (Exception e) {
-                throw new InvalidKeyException(e);
+        if (publicKey instanceof OpenSSLDSAPublicKey) {
+            if (engineType != EngineType.DSA) {
+                throw new InvalidKeyException("Signature not initialized as DSA");
             }
+
+            OpenSSLDSAPublicKey dsaPublicKey = (OpenSSLDSAPublicKey) publicKey;
+            key = dsaPublicKey.getOpenSSLKey();
+        } else if (publicKey instanceof DSAPublicKey) {
+            if (engineType != EngineType.DSA) {
+                throw new InvalidKeyException("Signature not initialized as DSA");
+            }
+
+            DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
+            key = OpenSSLDSAPublicKey.getInstance(dsaPublicKey);
+        } else if (publicKey instanceof OpenSSLRSAPublicKey) {
+            if (engineType != EngineType.RSA) {
+                throw new InvalidKeyException("Signature not initialized as RSA");
+            }
+
+            OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
+            key = rsaPublicKey.getOpenSSLKey();
         } else if (publicKey instanceof RSAPublicKey) {
-            try {
-                RSAPublicKey rsaPublicKey = (RSAPublicKey)publicKey;
-                rsa = NativeCrypto.EVP_PKEY_new_RSA(rsaPublicKey.getModulus().toByteArray(),
-                        rsaPublicKey.getPublicExponent().toByteArray(), null, null, null);
-
-            } catch (Exception e) {
-                throw new InvalidKeyException(e);
+            if (engineType != EngineType.RSA) {
+                throw new InvalidKeyException("Signature not initialized as RSA");
             }
+
+            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+            key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
         } else {
             throw new InvalidKeyException("Need DSA or RSA public key");
         }
-
-        try {
-            ctx = NativeCrypto.EVP_VerifyInit(evpAlgorithm);
-        } catch (Exception ex) {
-            throw new RuntimeException(ex);
-        }
     }
 
     @Override
@@ -217,37 +205,62 @@
 
     @Override
     protected byte[] engineSign() throws SignatureException {
-        throw new UnsupportedOperationException();
+        if (key == null) {
+            // This can't actually happen, but you never know...
+            throw new SignatureException("Need DSA or RSA private key");
+        }
+
+        try {
+            byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getPkeyContext())];
+            int bytesWritten = NativeCrypto.EVP_SignFinal(ctx, buffer, 0, key.getPkeyContext());
+
+            byte[] signature = new byte[bytesWritten];
+            System.arraycopy(buffer, 0, signature, 0, bytesWritten);
+
+            return signature;
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            /*
+             * Java expects the digest context to be reset completely after sign
+             * calls.
+             */
+            destroyContextIfExists();
+        }
     }
 
     @Override
     protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
-        int handle = (rsa != 0) ? rsa : dsa;
-
-        if (handle == 0) {
+        if (key == null) {
             // This can't actually happen, but you never know...
             throw new SignatureException("Need DSA or RSA public key");
         }
 
         try {
-            int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length, handle);
+            int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length,
+                    key.getPkeyContext());
             return result == 1;
         } catch (Exception ex) {
             throw new SignatureException(ex);
+        } finally {
+            /*
+             * Java expects the digest context to be reset completely after
+             * verify calls.
+             */
+            destroyContextIfExists();
         }
-
     }
 
-    @Override protected void finalize() throws Throwable {
+    private void destroyContextIfExists() {
+        if (ctx != 0) {
+            NativeCrypto.EVP_MD_CTX_destroy(ctx);
+            ctx = 0;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
         try {
-            if (dsa != 0) {
-                NativeCrypto.EVP_PKEY_free(dsa);
-            }
-
-            if (rsa != 0) {
-                NativeCrypto.EVP_PKEY_free(rsa);
-            }
-
             if (ctx != 0) {
                 NativeCrypto.EVP_MD_CTX_destroy(ctx);
             }
@@ -258,32 +271,32 @@
 
     public static final class MD5RSA extends OpenSSLSignature {
         public MD5RSA() throws NoSuchAlgorithmException {
-            super("RSA-MD5");
+            super("RSA-MD5", EngineType.RSA);
         }
     }
     public static final class SHA1RSA extends OpenSSLSignature {
         public SHA1RSA() throws NoSuchAlgorithmException {
-            super("RSA-SHA1");
+            super("RSA-SHA1", EngineType.RSA);
         }
     }
     public static final class SHA256RSA extends OpenSSLSignature {
         public SHA256RSA() throws NoSuchAlgorithmException {
-            super("RSA-SHA256");
+            super("RSA-SHA256", EngineType.RSA);
         }
     }
     public static final class SHA384RSA extends OpenSSLSignature {
         public SHA384RSA() throws NoSuchAlgorithmException {
-            super("RSA-SHA384");
+            super("RSA-SHA384", EngineType.RSA);
         }
     }
     public static final class SHA512RSA extends OpenSSLSignature {
         public SHA512RSA() throws NoSuchAlgorithmException {
-            super("RSA-SHA512");
+            super("RSA-SHA512", EngineType.RSA);
         }
     }
     public static final class SHA1DSA extends OpenSSLSignature {
         public SHA1DSA() throws NoSuchAlgorithmException {
-            super("DSA-SHA1");
+            super("DSA-SHA1", EngineType.DSA);
         }
     }
 }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index d4681ae..4c92952 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -18,7 +18,6 @@
 
 import dalvik.system.BlockGuard;
 import dalvik.system.CloseGuard;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
@@ -49,9 +48,6 @@
 /**
  * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
  * <p>
- * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
- * later, for example in the package.html or a separate reference document.
- * <p>
  * Extensions to SSLSocket include:
  * <ul>
  * <li>handshake timeout
@@ -71,6 +67,7 @@
     private final Object readLock = new Object();
     private final Object writeLock = new Object();
     private SSLParametersImpl sslParameters;
+    private byte[] npnProtocols;
     private String[] enabledProtocols;
     private String[] enabledCipherSuites;
     private String[] enabledCompressionMethods;
@@ -171,7 +168,7 @@
      */
     private void init(SSLParametersImpl sslParameters) throws IOException {
         init(sslParameters,
-             NativeCrypto.getSupportedProtocols(),
+             NativeCrypto.getDefaultProtocols(),
              NativeCrypto.getDefaultCipherSuites(),
              NativeCrypto.getDefaultCompressionMethods());
     }
@@ -194,14 +191,12 @@
      * Gets the suitable session reference from the session cache container.
      */
     private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
-        if (super.getInetAddress() == null ||
-                super.getInetAddress().getHostAddress() == null ||
-                super.getInetAddress().getHostName() == null) {
+        String hostName = getPeerHostName();
+        int port = getPeerPort();
+        if (hostName == null) {
             return null;
         }
-        OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession(
-                super.getInetAddress().getHostName(),
-                super.getPort());
+        OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession(hostName, port);
         if (session == null) {
             return null;
         }
@@ -231,32 +226,22 @@
         }
 
         String compressionMethod = session.getCompressionMethod();
-        boolean compressionMethodFound = false;
-        for (String enabledCompressionMethod : enabledCompressionMethods) {
-            if (compressionMethod.equals(enabledCompressionMethod)) {
-                compressionMethodFound = true;
-                break;
+        if (!compressionMethod.equals(NativeCrypto.SUPPORTED_COMPRESSION_METHOD_NULL)) {
+            boolean compressionMethodFound = false;
+            for (String enabledCompressionMethod : enabledCompressionMethods) {
+                if (compressionMethod.equals(enabledCompressionMethod)) {
+                    compressionMethodFound = true;
+                    break;
+                }
             }
-        }
-        if (!compressionMethodFound) {
-            return null;
+            if (!compressionMethodFound) {
+                return null;
+            }
         }
 
         return session;
     }
 
-    /**
-     * Starts a TLS/SSL handshake on this connection using some native methods
-     * from the OpenSSL library. It can negotiate new encryption keys, change
-     * cipher suites, or initiate a new session. The certificate chain is
-     * verified if the correspondent property in java.Security is set. All
-     * listeners are notified at the end of the TLS/SSL handshake.
-     */
-    @Override
-    public void startHandshake() throws IOException {
-        startHandshake(true);
-    }
-
     private void checkOpen() throws SocketException {
         if (isClosed()) {
             throw new SocketException("Socket is closed");
@@ -264,11 +249,13 @@
     }
 
     /**
-     * Perform the handshake
-     *
-     * @param full If true, disable handshake cutthrough for a fully synchronous handshake
+     * Starts a TLS/SSL handshake on this connection using some native methods
+     * from the OpenSSL library. It can negotiate new encryption keys, change
+     * cipher suites, or initiate a new session. The certificate chain is
+     * verified if the correspondent property in java.Security is set. All
+     * listeners are notified at the end of the TLS/SSL handshake.
      */
-    public synchronized void startHandshake(boolean full) throws IOException {
+    @Override public synchronized void startHandshake() throws IOException {
         synchronized (handshakeLock) {
             checkOpen();
             if (!handshakeStarted) {
@@ -299,6 +286,10 @@
             sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
             guard.open("close");
 
+            if (npnProtocols != null) {
+                NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer);
+            }
+
             // setup server certificates and private keys.
             // clients will receive a call back to request certificates.
             if (!client) {
@@ -393,12 +384,6 @@
                 }
             }
 
-            if (client && full) {
-                // we want to do a full synchronous handshake, so turn off cutthrough
-                NativeCrypto.SSL_clear_mode(sslNativePointer,
-                                            NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
-            }
-
             // Temporarily use a different timeout for the handshake process
             int savedTimeoutMilliseconds = getSoTimeout();
             if (handshakeTimeoutMilliseconds >= 0) {
@@ -408,7 +393,7 @@
             int sslSessionNativePointer;
             try {
                 sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer,
-                        socket.getFileDescriptor$(), this, getSoTimeout(), client);
+                        socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols);
             } catch (CertificateException e) {
                 SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
                 wrapper.initCause(e);
@@ -428,17 +413,8 @@
                         = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer));
                 X509Certificate[] peerCertificates
                         = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
-                if (wrappedHost == null) {
-                    sslSession = new OpenSSLSessionImpl(sslSessionNativePointer,
-                                                        localCertificates, peerCertificates,
-                                                        super.getInetAddress().getHostName(),
-                                                        super.getPort(), sessionContext);
-                } else  {
-                    sslSession = new OpenSSLSessionImpl(sslSessionNativePointer,
-                                                        localCertificates, peerCertificates,
-                                                        wrappedHost, wrappedPort,
-                                                        sessionContext);
-                }
+                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
+                        peerCertificates, getPeerHostName(), getPeerPort(), sessionContext);
                 // if not, putSession later in handshakeCompleted() callback
                 if (handshakeCompleted) {
                     sessionContext.putSession(sslSession);
@@ -466,6 +442,21 @@
         }
     }
 
+    private String getPeerHostName() {
+        if (wrappedHost != null) {
+            return wrappedHost;
+        }
+        InetAddress inetAddress = super.getInetAddress();
+        if (inetAddress != null) {
+            return inetAddress.getHostName();
+        }
+        return null;
+    }
+
+    private int getPeerPort() {
+        return wrappedHost == null ? super.getPort() : wrappedPort;
+    }
+
     /**
      * Return a possibly null array of X509Certificates given the
      * possibly null array of DER encoded bytes.
@@ -498,9 +489,22 @@
             return;
         }
 
-        byte[] privateKeyBytes = privateKey.getEncoded();
+        if (privateKey instanceof OpenSSLRSAPrivateKey) {
+            OpenSSLRSAPrivateKey rsaKey = (OpenSSLRSAPrivateKey) privateKey;
+            OpenSSLKey key = rsaKey.getOpenSSLKey();
+            NativeCrypto.SSL_use_OpenSSL_PrivateKey(sslNativePointer, key.getPkeyContext());
+        } else if (privateKey instanceof OpenSSLDSAPrivateKey) {
+            OpenSSLDSAPrivateKey dsaKey = (OpenSSLDSAPrivateKey) privateKey;
+            OpenSSLKey key = dsaKey.getOpenSSLKey();
+            NativeCrypto.SSL_use_OpenSSL_PrivateKey(sslNativePointer, key.getPkeyContext());
+        } else if ("PKCS#8".equals(privateKey.getFormat())) {
+            byte[] privateKeyBytes = privateKey.getEncoded();
+            NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes);
+        } else {
+            throw new SSLException("Unsupported PrivateKey format: " + privateKey.getFormat());
+        }
+
         byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates);
-        NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes);
         NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes);
 
         // checks the last installed private key and certificate,
@@ -636,11 +640,11 @@
      */
     private class SSLInputStream extends InputStream {
         SSLInputStream() throws IOException {
-            /**
-            /* Note: When startHandshake() throws an exception, no
+            /*
+             * Note: When startHandshake() throws an exception, no
              * SSLInputStream object will be created.
              */
-            OpenSSLSocketImpl.this.startHandshake(false);
+            OpenSSLSocketImpl.this.startHandshake();
         }
 
         /**
@@ -681,11 +685,11 @@
      */
     private class SSLOutputStream extends OutputStream {
         SSLOutputStream() throws IOException {
-            /**
-            /* Note: When startHandshake() throws an exception, no
+            /*
+             * Note: When startHandshake() throws an exception, no
              * SSLOutputStream object will be created.
              */
-            OpenSSLSocketImpl.this.startHandshake(false);
+            OpenSSLSocketImpl.this.startHandshake();
         }
 
         /**
@@ -720,7 +724,7 @@
     @Override public SSLSession getSession() {
         if (sslSession == null) {
             try {
-                startHandshake(true);
+                startHandshake();
             } catch (IOException e) {
                 // return an invalid session with
                 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
@@ -997,4 +1001,28 @@
             return socket.getFileDescriptor$();
         }
     }
+
+    /**
+     * Returns the protocol agreed upon by client and server, or null if no
+     * protocol was agreed upon.
+     */
+    public byte[] getNpnSelectedProtocol() {
+        return NativeCrypto.SSL_get_npn_negotiated_protocol(sslNativePointer);
+    }
+
+    /**
+     * Sets the list of protocols this peer is interested in. If null no
+     * protocols will be used.
+     *
+     * @param npnProtocols a non-empty array of protocol names. From
+     *     SSL_select_next_proto, "vector of 8-bit, length prefixed byte
+     *     strings. The length byte itself is not included in the length. A byte
+     *     string of length 0 is invalid. No byte string may be truncated.".
+     */
+    public void setNpnProtocols(byte[] npnProtocols) {
+        if (npnProtocols != null && npnProtocols.length == 0) {
+            throw new IllegalArgumentException("npnProtocols.length == 0");
+        }
+        this.npnProtocols = npnProtocols;
+    }
 }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParametersImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParametersImpl.java
index a17492f..287f9f3 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParametersImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParametersImpl.java
@@ -24,6 +24,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
+import java.util.Arrays;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.TrustManager;
@@ -112,9 +113,6 @@
         } else {
             keyManager = findX509KeyManager(kms);
         }
-        if (keyManager == null) {
-            throw new KeyManagementException("No X509KeyManager found");
-        }
 
         // initialize trustManager
         if ((tms == null) || (tms.length == 0)) {
@@ -122,9 +120,6 @@
         } else {
             trustManager = findX509TrustManager(tms);
         }
-        if (trustManager == null) {
-            throw new KeyManagementException("No X509TrustManager found");
-        }
         // initialize secure random
         // BEGIN android-removed
         // if (sr == null) {
@@ -354,7 +349,7 @@
         }
     }
 
-    private static X509KeyManager getDefaultKeyManager() {
+    private static X509KeyManager getDefaultKeyManager() throws KeyManagementException {
         X509KeyManager result = defaultKeyManager;
         if (result == null) {
             // single-check idiom
@@ -362,7 +357,7 @@
         }
         return result;
     }
-    private static X509KeyManager createDefaultKeyManager() {
+    private static X509KeyManager createDefaultKeyManager() throws KeyManagementException {
         try {
             String algorithm = KeyManagerFactory.getDefaultAlgorithm();
             KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
@@ -370,20 +365,20 @@
             KeyManager[] kms = kmf.getKeyManagers();
             return findX509KeyManager(kms);
         } catch (NoSuchAlgorithmException e) {
-            return null;
+            throw new KeyManagementException(e);
         } catch (KeyStoreException e) {
-            return null;
+            throw new KeyManagementException(e);
         } catch (UnrecoverableKeyException e) {
-            return null;
+            throw new KeyManagementException(e);
         }
     }
-    private static X509KeyManager findX509KeyManager(KeyManager[] kms) {
+    private static X509KeyManager findX509KeyManager(KeyManager[] kms) throws KeyManagementException {
         for (KeyManager km : kms) {
             if (km instanceof X509KeyManager) {
                 return (X509KeyManager)km;
             }
         }
-        return null;
+        throw new KeyManagementException("Failed to find an X509KeyManager in " + Arrays.toString(kms));
     }
 
     /**
@@ -391,7 +386,7 @@
      *
      * TODO: Move this to a published API under dalvik.system.
      */
-    public static X509TrustManager getDefaultTrustManager() {
+    public static X509TrustManager getDefaultTrustManager() throws KeyManagementException {
         X509TrustManager result = defaultTrustManager;
         if (result == null) {
             // single-check idiom
@@ -399,7 +394,7 @@
         }
         return result;
     }
-    private static X509TrustManager createDefaultTrustManager() {
+    private static X509TrustManager createDefaultTrustManager() throws KeyManagementException {
         try {
             String algorithm = TrustManagerFactory.getDefaultAlgorithm();
             TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
@@ -408,17 +403,17 @@
             X509TrustManager trustManager = findX509TrustManager(tms);
             return trustManager;
         } catch (NoSuchAlgorithmException e) {
-            return null;
+            throw new KeyManagementException(e);
         } catch (KeyStoreException e) {
-            return null;
+            throw new KeyManagementException(e);
         }
     }
-    private static X509TrustManager findX509TrustManager(TrustManager[] tms) {
+    private static X509TrustManager findX509TrustManager(TrustManager[] tms) throws KeyManagementException {
         for (TrustManager tm : tms) {
             if (tm instanceof X509TrustManager) {
                 return (X509TrustManager)tm;
             }
         }
-        return null;
+        throw new KeyManagementException("Failed to find an X509TrustManager in " +  Arrays.toString(tms));
     }
 }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java
index abec7d1..e9f77f72 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java
@@ -442,7 +442,7 @@
     /**
      * Sets up the SSL version used in this connection.
      * This method is calling from the handshake protocol after
-     * it becomes known witch protocol version will be used.
+     * it becomes known which protocol version will be used.
      * @param   ver:    byte[]
      * @return
      */
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
index b6a65b4..c5e1838 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
@@ -205,8 +205,10 @@
                         Cipher c = null;
                         try {
                             c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
-                            c.init(Cipher.DECRYPT_MODE, privKey);
-                            preMasterSecret = c.doFinal(clientKeyExchange.exchange_keys);
+                            c.init(Cipher.UNWRAP_MODE, privKey);
+                            preMasterSecret = c.unwrap(clientKeyExchange.exchange_keys,
+                                                       "preMasterSecret",
+                                                       Cipher.SECRET_KEY).getEncoded();
                             // check preMasterSecret:
                             if (preMasterSecret.length != 48
                                     || preMasterSecret[0] != clientHello.client_version[0]
@@ -330,11 +332,17 @@
                        "HANDSHAKE FAILURE. Incorrect client hello message");
         }
 
+        byte[] server_version = clientHello.client_version;
         if (!ProtocolVersion.isSupported(clientHello.client_version)) {
-            fatalAlert(AlertProtocol.PROTOCOL_VERSION,
-                       "PROTOCOL VERSION. Unsupported client version "
-                       + clientHello.client_version[0]
-                       + clientHello.client_version[1]);
+            if (clientHello.client_version[0] >= 3) {
+                // Protocol from the future, admit that the newest thing we know is TLSv1
+                server_version = ProtocolVersion.TLSv1.version;
+            } else {
+                fatalAlert(AlertProtocol.PROTOCOL_VERSION,
+                           "PROTOCOL VERSION. Unsupported client version "
+                           + clientHello.client_version[0]
+                           + clientHello.client_version[1]);
+            }
         }
 
         isResuming = false;
@@ -404,13 +412,13 @@
             }
         }
 
-        recordProtocol.setVersion(clientHello.client_version);
-        session.protocol = ProtocolVersion.getByVersion(clientHello.client_version);
+        recordProtocol.setVersion(server_version);
+        session.protocol = ProtocolVersion.getByVersion(server_version);
         session.clientRandom = clientHello.random;
 
         // create server hello message
         serverHello = new ServerHello(parameters.getSecureRandom(),
-                clientHello.client_version,
+                server_version,
                 session.getId(), cipher_suite, (byte) 0); //CompressionMethod.null
         session.serverRandom = serverHello.random;
         send(serverHello);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
index cb21be4..f5716c1 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
@@ -37,6 +37,12 @@
 
         // TODO override trimToSize and removeEldestEntry to use
         // SSL_CTX_sessions to remove from native cache
+
+        // Set a trivial session id context. OpenSSL uses this to make
+        // sure you don't reuse sessions externalized with i2d_SSL_SESSION
+        // between apps. However our sessions are either in memory or
+        // exported to a app's SSLServerSessionCache.
+        NativeCrypto.SSL_CTX_set_session_id_context(sslCtxNativePointer, new byte[] { ' ' });
     }
 
     public void setPersistentCache(SSLServerSessionCache persistentCache) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
index 653dca4..3f362c5 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
@@ -163,6 +163,14 @@
         checkTrusted(chain, authType);
     }
 
+    public void handleTrustStorageUpdate() {
+        if (acceptedIssuers == null) {
+            trustedCertificateIndex.reset();
+        } else {
+            trustedCertificateIndex.reset(trustAnchors(acceptedIssuers));
+        }
+    }
+
     private void checkTrusted(X509Certificate[] chain, String authType)
             throws CertificateException {
         if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateIndex.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateIndex.java
index 9138b19..0b1f098 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateIndex.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateIndex.java
@@ -74,6 +74,19 @@
         }
     }
 
+    public void reset() {
+        synchronized (subjectToTrustAnchors) {
+            subjectToTrustAnchors.clear();
+        }
+    }
+
+    public void reset(Set<TrustAnchor> anchors) {
+        synchronized (subjectToTrustAnchors) {
+            reset();
+            index(anchors);
+        }
+    }
+
     public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
         X500Principal issuer = cert.getIssuerX500Principal();
         synchronized (subjectToTrustAnchors) {
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp b/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
index 0cfb8bb..7b0f602 100644
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
+++ b/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
@@ -53,7 +53,7 @@
     sa.sa_flags = 0;
     int rc = sigaction(BLOCKED_THREAD_SIGNAL, &sa, NULL);
     if (rc == -1) {
-        LOGE("setting blocked thread signal handler failed: %s", strerror(errno));
+        ALOGE("setting blocked thread signal handler failed: %s", strerror(errno));
     }
 }
 
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
index c09f559..6815734 100644
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -65,7 +65,7 @@
     ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
     jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
     if (result == NULL) {
-        LOGE("failed to find class '%s'", name);
+        ALOGE("failed to find class '%s'", name);
         abort();
     }
     return result;
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
index 3b5b840..9f4d770 100644
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -35,7 +35,7 @@
     const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(ss);
     if (ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
         // Copy the IPv6 address into the temporary sockaddr_storage.
-        memcpy(&tmp, ss, sizeof(tmp));
+        memcpy(&tmp, ss, sizeof(sockaddr_in6));
         // Unmap it into an IPv4 address.
         sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&tmp);
         sin->sin_family = AF_INET;
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index a637a73..36abd7b 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -21,93 +21,56 @@
 
 #include <stdlib.h>
 
-extern int register_java_io_Console(JNIEnv* env);
-extern int register_java_io_File(JNIEnv* env);
-extern int register_java_io_ObjectStreamClass(JNIEnv* env);
-extern int register_java_lang_Character(JNIEnv* env);
-extern int register_java_lang_Math(JNIEnv* env);
-extern int register_java_lang_ProcessManager(JNIEnv* env);
-extern int register_java_lang_RealToString(JNIEnv* env);
-extern int register_java_lang_StrictMath(JNIEnv* env);
-extern int register_java_lang_StringToReal(JNIEnv* env);
-extern int register_java_lang_System(JNIEnv* env);
-extern int register_java_math_NativeBN(JNIEnv* env);
-extern int register_java_nio_ByteOrder(JNIEnv* env);
-extern int register_java_nio_charset_Charsets(JNIEnv* env);
-extern int register_java_text_Bidi(JNIEnv* env);
-extern int register_java_util_regex_Matcher(JNIEnv* env);
-extern int register_java_util_regex_Pattern(JNIEnv* env);
-extern int register_java_util_zip_Adler32(JNIEnv* env);
-extern int register_java_util_zip_CRC32(JNIEnv* env);
-extern int register_java_util_zip_Deflater(JNIEnv* env);
-extern int register_java_util_zip_Inflater(JNIEnv* env);
-extern int register_libcore_icu_ICU(JNIEnv* env);
-extern int register_libcore_icu_NativeBreakIterator(JNIEnv* env);
-extern int register_libcore_icu_NativeCollation(JNIEnv* env);
-extern int register_libcore_icu_NativeConverter(JNIEnv* env);
-extern int register_libcore_icu_NativeDecimalFormat(JNIEnv* env);
-extern int register_libcore_icu_NativeIDN(JNIEnv* env);
-extern int register_libcore_icu_NativeNormalizer(JNIEnv* env);
-extern int register_libcore_icu_NativePluralRules(JNIEnv* env);
-extern int register_libcore_icu_TimeZones(JNIEnv* env);
-extern int register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env);
-extern int register_libcore_io_Memory(JNIEnv* env);
-extern int register_libcore_io_OsConstants(JNIEnv* env);
-extern int register_libcore_io_Posix(JNIEnv* env);
-extern int register_libcore_net_RawSocket(JNIEnv* env);
-extern int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env);
-extern int register_org_apache_harmony_xml_ExpatParser(JNIEnv* env);
-extern int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env);
-
 // DalvikVM calls this on startup, so we can statically register all our native methods.
-int registerCoreLibrariesJni(JNIEnv* env) {
+int JNI_OnLoad(JavaVM* vm, void*) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        ALOGE("JavaVM::GetEnv() failed");
+        abort();
+    }
+
     ScopedLocalFrame localFrame(env);
 
     JniConstants::init(env);
 
-    bool result =
-            register_java_io_Console(env) != -1 &&
-            register_java_io_File(env) != -1 &&
-            register_java_io_ObjectStreamClass(env) != -1 &&
-            register_java_lang_Character(env) != -1 &&
-            register_java_lang_Math(env) != -1 &&
-            register_java_lang_ProcessManager(env) != -1 &&
-            register_java_lang_RealToString(env) != -1 &&
-            register_java_lang_StrictMath(env) != -1 &&
-            register_java_lang_StringToReal(env) != -1 &&
-            register_java_lang_System(env) != -1 &&
-            register_java_math_NativeBN(env) != -1 &&
-            register_java_nio_ByteOrder(env) != -1 &&
-            register_java_nio_charset_Charsets(env) != -1 &&
-            register_java_text_Bidi(env) != -1 &&
-            register_java_util_regex_Matcher(env) != -1 &&
-            register_java_util_regex_Pattern(env) != -1 &&
-            register_java_util_zip_Adler32(env) != -1 &&
-            register_java_util_zip_CRC32(env) != -1 &&
-            register_java_util_zip_Deflater(env) != -1 &&
-            register_java_util_zip_Inflater(env) != -1 &&
-            register_libcore_icu_ICU(env) != -1 &&
-            register_libcore_icu_NativeBreakIterator(env) != -1 &&
-            register_libcore_icu_NativeCollation(env) != -1 &&
-            register_libcore_icu_NativeConverter(env) != -1 &&
-            register_libcore_icu_NativeDecimalFormat(env) != -1 &&
-            register_libcore_icu_NativeIDN(env) != -1 &&
-            register_libcore_icu_NativeNormalizer(env) != -1 &&
-            register_libcore_icu_NativePluralRules(env) != -1 &&
-            register_libcore_icu_TimeZones(env) != -1 &&
-            register_libcore_io_AsynchronousCloseMonitor(env) != -1 &&
-            register_libcore_io_Memory(env) != -1 &&
-            register_libcore_io_OsConstants(env) != -1 &&
-            register_libcore_io_Posix(env) != -1 &&
-            register_libcore_net_RawSocket(env) != -1 &&
-            register_org_apache_harmony_dalvik_NativeTestTarget(env) != -1 &&
-            register_org_apache_harmony_xml_ExpatParser(env) != -1 &&
-            register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(env) != -1 &&
-            true;
-
-    if (!result) {
-        LOGE("Failed to initialize the core libraries; aborting...");
-        abort();
-    }
-    return 0;
+#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
+    REGISTER(register_java_io_Console);
+    REGISTER(register_java_io_File);
+    REGISTER(register_java_io_ObjectStreamClass);
+    REGISTER(register_java_lang_Character);
+    REGISTER(register_java_lang_Math);
+    REGISTER(register_java_lang_ProcessManager);
+    REGISTER(register_java_lang_RealToString);
+    REGISTER(register_java_lang_StrictMath);
+    REGISTER(register_java_lang_StringToReal);
+    REGISTER(register_java_lang_System);
+    REGISTER(register_java_math_NativeBN);
+    REGISTER(register_java_nio_ByteOrder);
+    REGISTER(register_java_nio_charset_Charsets);
+    REGISTER(register_java_text_Bidi);
+    REGISTER(register_java_util_regex_Matcher);
+    REGISTER(register_java_util_regex_Pattern);
+    REGISTER(register_java_util_zip_Adler32);
+    REGISTER(register_java_util_zip_CRC32);
+    REGISTER(register_java_util_zip_Deflater);
+    REGISTER(register_java_util_zip_Inflater);
+    REGISTER(register_libcore_icu_ICU);
+    REGISTER(register_libcore_icu_NativeBreakIterator);
+    REGISTER(register_libcore_icu_NativeCollation);
+    REGISTER(register_libcore_icu_NativeConverter);
+    REGISTER(register_libcore_icu_NativeDecimalFormat);
+    REGISTER(register_libcore_icu_NativeIDN);
+    REGISTER(register_libcore_icu_NativeNormalizer);
+    REGISTER(register_libcore_icu_NativePluralRules);
+    REGISTER(register_libcore_icu_TimeZones);
+    REGISTER(register_libcore_io_AsynchronousCloseMonitor);
+    REGISTER(register_libcore_io_Memory);
+    REGISTER(register_libcore_io_OsConstants);
+    REGISTER(register_libcore_io_Posix);
+    REGISTER(register_libcore_net_RawSocket);
+    REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
+    REGISTER(register_org_apache_harmony_xml_ExpatParser);
+    REGISTER(register_org_apache_harmony_xnet_provider_jsse_NativeCrypto);
+#undef REGISTER
+    return JNI_VERSION_1_6;
 }
diff --git a/luni/src/main/native/java_io_Console.cpp b/luni/src/main/native/java_io_Console.cpp
index 9150fb7..512bc72 100644
--- a/luni/src/main/native/java_io_Console.cpp
+++ b/luni/src/main/native/java_io_Console.cpp
@@ -46,6 +46,6 @@
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(Console, setEchoImpl, "(ZI)I"),
 };
-int register_java_io_Console(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/io/Console", gMethods, NELEM(gMethods));
+void register_java_io_Console(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/io/Console", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_io_File.cpp b/luni/src/main/native/java_io_File.cpp
index db88a11..ba1f55a 100644
--- a/luni/src/main/native/java_io_File.cpp
+++ b/luni/src/main/native/java_io_File.cpp
@@ -104,6 +104,9 @@
 
     // Returns the next filename, or NULL.
     const char* next() {
+        if (mIsBad) {
+            return NULL;
+        }
         dirent* result = NULL;
         int rc = readdir_r(mDirStream, &mEntry, &result);
         if (rc != 0) {
@@ -139,9 +142,6 @@
     }
 
     ScopedReaddir dir(path.c_str());
-    if (dir.isBad()) {
-        return false;
-    }
     const char* filename;
     while ((filename = dir.next()) != NULL) {
         if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) {
@@ -149,7 +149,7 @@
             entries.push_back(filename);
         }
     }
-    return true;
+    return !dir.isBad();
 }
 
 static jobjectArray File_listImpl(JNIEnv* env, jclass, jstring javaPath) {
@@ -168,6 +168,6 @@
     NATIVE_METHOD(File, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"),
 };
-int register_java_io_File(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
+void register_java_io_File(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_io_ObjectStreamClass.cpp b/luni/src/main/native/java_io_ObjectStreamClass.cpp
index 721136e..f6bd560 100644
--- a/luni/src/main/native/java_io_ObjectStreamClass.cpp
+++ b/luni/src/main/native/java_io_ObjectStreamClass.cpp
@@ -63,6 +63,6 @@
     NATIVE_METHOD(ObjectStreamClass, hasClinit, "(Ljava/lang/Class;)Z"),
     NATIVE_METHOD(ObjectStreamClass, newInstance, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
 };
-int register_java_io_ObjectStreamClass(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/io/ObjectStreamClass", gMethods, NELEM(gMethods));
+void register_java_io_ObjectStreamClass(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/io/ObjectStreamClass", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_lang_Character.cpp b/luni/src/main/native/java_lang_Character.cpp
index 2ec029e..7dbef90 100644
--- a/luni/src/main/native/java_lang_Character.cpp
+++ b/luni/src/main/native/java_lang_Character.cpp
@@ -160,6 +160,6 @@
     NATIVE_METHOD(Character, toTitleCaseImpl, "!(I)I"),
     NATIVE_METHOD(Character, toUpperCaseImpl, "!(I)I"),
 };
-int register_java_lang_Character(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/Character", gMethods, NELEM(gMethods));
+void register_java_lang_Character(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/Character", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_lang_Math.cpp b/luni/src/main/native/java_lang_Math.cpp
index 4a41af2..273820e 100644
--- a/luni/src/main/native/java_lang_Math.cpp
+++ b/luni/src/main/native/java_lang_Math.cpp
@@ -131,6 +131,6 @@
     NATIVE_METHOD(Math, tanh, "!(D)D"),
 };
 
-int register_java_lang_Math(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/Math", gMethods, NELEM(gMethods));
+void register_java_lang_Math(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/Math", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_lang_ProcessManager.cpp b/luni/src/main/native/java_lang_ProcessManager.cpp
index 5d59f27..6302d13 100644
--- a/luni/src/main/native/java_lang_ProcessManager.cpp
+++ b/luni/src/main/native/java_lang_ProcessManager.cpp
@@ -255,7 +255,7 @@
     // Re-throw exception if present.
     if (exception != NULL) {
         if (env->Throw(exception) < 0) {
-            LOGE("Error rethrowing exception!");
+            ALOGE("Error rethrowing exception!");
         }
     }
 
@@ -265,6 +265,6 @@
 static JNINativeMethod methods[] = {
     NATIVE_METHOD(ProcessManager, exec, "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Z)I"),
 };
-int register_java_lang_ProcessManager(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods));
+void register_java_lang_ProcessManager(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/ProcessManager", methods, NELEM(methods));
 }
diff --git a/luni/src/main/native/java_lang_RealToString.cpp b/luni/src/main/native/java_lang_RealToString.cpp
index 835151e..7036fe8 100644
--- a/luni/src/main/native/java_lang_RealToString.cpp
+++ b/luni/src/main/native/java_lang_RealToString.cpp
@@ -237,6 +237,6 @@
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(RealToString, bigIntDigitGenerator, "(JIZI)V"),
 };
-int register_java_lang_RealToString(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/RealToString", gMethods, NELEM(gMethods));
+void register_java_lang_RealToString(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/RealToString", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_lang_StrictMath.cpp b/luni/src/main/native/java_lang_StrictMath.cpp
index 6df3f01..959ddc6 100644
--- a/luni/src/main/native/java_lang_StrictMath.cpp
+++ b/luni/src/main/native/java_lang_StrictMath.cpp
@@ -145,6 +145,6 @@
     NATIVE_METHOD(StrictMath, tanh, "!(D)D"),
 };
 
-int register_java_lang_StrictMath(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/StrictMath", gMethods, NELEM(gMethods));
+void register_java_lang_StrictMath(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/StrictMath", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_lang_StringToReal.cpp b/luni/src/main/native/java_lang_StringToReal.cpp
index 5dc096e..d401e65 100644
--- a/luni/src/main/native/java_lang_StringToReal.cpp
+++ b/luni/src/main/native/java_lang_StringToReal.cpp
@@ -1008,6 +1008,6 @@
     NATIVE_METHOD(StringToReal, parseFltImpl, "(Ljava/lang/String;I)F"),
     NATIVE_METHOD(StringToReal, parseDblImpl, "(Ljava/lang/String;I)D"),
 };
-int register_java_lang_StringToReal(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/StringToReal", gMethods, NELEM(gMethods));
+void register_java_lang_StringToReal(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/StringToReal", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_lang_System.cpp b/luni/src/main/native/java_lang_System.cpp
index 9ebfc32..8cfd070 100644
--- a/luni/src/main/native/java_lang_System.cpp
+++ b/luni/src/main/native/java_lang_System.cpp
@@ -36,7 +36,7 @@
     ScopedUtfChars message(env, javaMessage);
     if (message.c_str() == NULL) {
         // Since this function is used for last-gasp debugging output, be noisy on failure.
-        LOGE("message.c_str() == NULL");
+        ALOGE("message.c_str() == NULL");
         return;
     }
     int priority;
@@ -88,6 +88,6 @@
     NATIVE_METHOD(System, setFieldImpl, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"),
     NATIVE_METHOD(System, specialProperties, "()[Ljava/lang/String;"),
 };
-int register_java_lang_System(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/lang/System", gMethods, NELEM(gMethods));
+void register_java_lang_System(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/lang/System", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
index 6359e3e..5e111d7 100644
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ b/luni/src/main/native/java_math_NativeBN.cpp
@@ -405,7 +405,7 @@
 }
 
 static jboolean NativeBN_modifyBit(JNIEnv* env, jclass, BIGNUM* a, int n, int op) {
-// LOGD("NativeBN_BN_modifyBit");
+// ALOGD("NativeBN_BN_modifyBit");
     if (!oneValidHandle(env, a)) return JNI_FALSE;
     switch (op) {
     case 1: return BN_set_bit(a, n);
@@ -553,6 +553,6 @@
    NATIVE_METHOD(NativeBN, sign, "(I)I"),
    NATIVE_METHOD(NativeBN, twosComp2bn, "([BII)Z"),
 };
-int register_java_math_NativeBN(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/math/NativeBN", gMethods, NELEM(gMethods));
+void register_java_math_NativeBN(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/math/NativeBN", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_nio_ByteOrder.cpp b/luni/src/main/native/java_nio_ByteOrder.cpp
index b1695a9..3269bc2 100644
--- a/luni/src/main/native/java_nio_ByteOrder.cpp
+++ b/luni/src/main/native/java_nio_ByteOrder.cpp
@@ -27,6 +27,6 @@
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(ByteOrder, isLittleEndian, "!()Z"),
 };
-int register_java_nio_ByteOrder(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/nio/ByteOrder", gMethods, NELEM(gMethods));
+void register_java_nio_ByteOrder(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/nio/ByteOrder", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_nio_charset_Charsets.cpp b/luni/src/main/native/java_nio_charset_Charsets.cpp
index 22422fe..a49ba22 100644
--- a/luni/src/main/native/java_nio_charset_Charsets.cpp
+++ b/luni/src/main/native/java_nio_charset_Charsets.cpp
@@ -245,6 +245,6 @@
     NATIVE_METHOD(Charsets, toIsoLatin1Bytes, "([CII)[B"),
     NATIVE_METHOD(Charsets, toUtf8Bytes, "([CII)[B"),
 };
-int register_java_nio_charset_Charsets(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/nio/charset/Charsets", gMethods, NELEM(gMethods));
+void register_java_nio_charset_Charsets(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/nio/charset/Charsets", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_text_Bidi.cpp b/luni/src/main/native/java_text_Bidi.cpp
index 27d3ddb..93c02bb 100644
--- a/luni/src/main/native/java_text_Bidi.cpp
+++ b/luni/src/main/native/java_text_Bidi.cpp
@@ -187,6 +187,6 @@
     NATIVE_METHOD(Bidi, ubidi_setLine, "(JII)J"),
     NATIVE_METHOD(Bidi, ubidi_setPara, "(J[CII[B)V"),
 };
-int register_java_text_Bidi(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
+void register_java_text_Bidi(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index 024a6f0..0f91bd5 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -211,6 +211,6 @@
     NATIVE_METHOD(Matcher, useAnchoringBoundsImpl, "(IZ)V"),
     NATIVE_METHOD(Matcher, useTransparentBoundsImpl, "(IZ)V"),
 };
-int register_java_util_regex_Matcher(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/util/regex/Matcher", gMethods, NELEM(gMethods));
+void register_java_util_regex_Matcher(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/util/regex/Matcher", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_util_regex_Pattern.cpp b/luni/src/main/native/java_util_regex_Pattern.cpp
index 1e0e1e3..cad154f 100644
--- a/luni/src/main/native/java_util_regex_Pattern.cpp
+++ b/luni/src/main/native/java_util_regex_Pattern.cpp
@@ -95,6 +95,6 @@
     NATIVE_METHOD(Pattern, closeImpl, "(I)V"),
     NATIVE_METHOD(Pattern, compileImpl, "(Ljava/lang/String;I)I"),
 };
-int register_java_util_regex_Pattern(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/util/regex/Pattern", gMethods, NELEM(gMethods));
+void register_java_util_regex_Pattern(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/util/regex/Pattern", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_util_zip_Adler32.cpp b/luni/src/main/native/java_util_zip_Adler32.cpp
index d0c96ea..9358f26 100644
--- a/luni/src/main/native/java_util_zip_Adler32.cpp
+++ b/luni/src/main/native/java_util_zip_Adler32.cpp
@@ -40,6 +40,6 @@
     NATIVE_METHOD(Adler32, updateImpl, "([BIIJ)J"),
     NATIVE_METHOD(Adler32, updateByteImpl, "(IJ)J"),
 };
-int register_java_util_zip_Adler32(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/util/zip/Adler32", gMethods, NELEM(gMethods));
+void register_java_util_zip_Adler32(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/util/zip/Adler32", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_util_zip_CRC32.cpp b/luni/src/main/native/java_util_zip_CRC32.cpp
index ecfde7f..502a4ecf 100644
--- a/luni/src/main/native/java_util_zip_CRC32.cpp
+++ b/luni/src/main/native/java_util_zip_CRC32.cpp
@@ -40,6 +40,6 @@
     NATIVE_METHOD(CRC32, updateImpl, "([BIIJ)J"),
     NATIVE_METHOD(CRC32, updateByteImpl, "(BJ)J"),
 };
-int register_java_util_zip_CRC32(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/util/zip/CRC32", gMethods, NELEM(gMethods));
+void register_java_util_zip_CRC32(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/util/zip/CRC32", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_util_zip_Deflater.cpp b/luni/src/main/native/java_util_zip_Deflater.cpp
index 82d081b..e129134 100644
--- a/luni/src/main/native/java_util_zip_Deflater.cpp
+++ b/luni/src/main/native/java_util_zip_Deflater.cpp
@@ -146,6 +146,6 @@
     NATIVE_METHOD(Deflater, setInputImpl, "([BIIJ)V"),
     NATIVE_METHOD(Deflater, setLevelsImpl, "(IIJ)V"),
 };
-int register_java_util_zip_Deflater(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/util/zip/Deflater", gMethods, NELEM(gMethods));
+void register_java_util_zip_Deflater(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/util/zip/Deflater", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/java_util_zip_Inflater.cpp b/luni/src/main/native/java_util_zip_Inflater.cpp
index 192d1b5..890c6dc 100644
--- a/luni/src/main/native/java_util_zip_Inflater.cpp
+++ b/luni/src/main/native/java_util_zip_Inflater.cpp
@@ -168,6 +168,6 @@
     NATIVE_METHOD(Inflater, setFileInputImpl, "(Ljava/io/FileDescriptor;JIJ)I"),
     NATIVE_METHOD(Inflater, setInputImpl, "([BIIJ)V"),
 };
-int register_java_util_zip_Inflater(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
+void register_java_util_zip_Inflater(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 9f96f42..5a07694 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -135,7 +135,7 @@
 
     ScopedResourceBundle currencyElem(ures_getByIndex(currency.get(), 0, NULL, &status));
     if (U_FAILURE(status)) {
-        return env->NewStringUTF("None");
+        return env->NewStringUTF("XXX");
     }
 
     // Check if there's a 'to' date. If there is, the currency isn't used anymore.
@@ -149,12 +149,12 @@
     ScopedResourceBundle currencyId(ures_getByKey(currencyElem.get(), "id", NULL, &status));
     if (U_FAILURE(status)) {
         // No id defined for this country
-        return env->NewStringUTF("None");
+        return env->NewStringUTF("XXX");
     }
 
     int32_t charCount;
     const jchar* chars = ures_getString(currencyId.get(), &charCount, &status);
-    return (charCount == 0) ? env->NewStringUTF("None") : env->NewString(chars, charCount);
+    return (charCount == 0) ? env->NewStringUTF("XXX") : env->NewString(chars, charCount);
 }
 
 static jstring ICU_getCurrencyDisplayName(JNIEnv* env, jclass, jstring javaLocaleName, jstring javaCurrencyCode) {
@@ -269,97 +269,6 @@
     return toStringArray(env, unum_countAvailable, unum_getAvailable);
 }
 
-static bool getDayIntVector(JNIEnv*, UResourceBundle* gregorian, int* values) {
-    // get the First day of week and the minimal days in first week numbers
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle gregorianElems(ures_getByKey(gregorian, "DateTimeElements", NULL, &status));
-    if (U_FAILURE(status)) {
-        return false;
-    }
-
-    int intVectSize;
-    const int* result = ures_getIntVector(gregorianElems.get(), &intVectSize, &status);
-    if (U_FAILURE(status) || intVectSize != 2) {
-        return false;
-    }
-
-    values[0] = result[0];
-    values[1] = result[1];
-    return true;
-}
-
-// This allows you to leave extra space at the beginning or end of the array to support the
-// month names and day names arrays.
-static jobjectArray toStringArray(JNIEnv* env, UResourceBundle* rb, size_t size, int capacity, size_t offset) {
-    if (capacity == -1) {
-        capacity = size;
-    }
-    jobjectArray result = env->NewObjectArray(capacity, JniConstants::stringClass, NULL);
-    if (result == NULL) {
-        return NULL;
-    }
-    UErrorCode status = U_ZERO_ERROR;
-    for (size_t i = 0; i < size; ++i) {
-        int charCount;
-        const jchar* chars = ures_getStringByIndex(rb, i, &charCount, &status);
-        if (U_FAILURE(status)) {
-            return NULL;
-        }
-        ScopedLocalRef<jstring> s(env, env->NewString(chars, charCount));
-        if (env->ExceptionCheck()) {
-            return NULL;
-        }
-        env->SetObjectArrayElement(result, offset + i, s.get());
-        if (env->ExceptionCheck()) {
-            return NULL;
-        }
-    }
-    return result;
-}
-
-static jobjectArray getAmPmMarkers(JNIEnv* env, UResourceBundle* gregorian) {
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle amPmMarkers(ures_getByKey(gregorian, "AmPmMarkers", NULL, &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    return toStringArray(env, amPmMarkers.get(), ures_getSize(amPmMarkers.get()), -1, 0);
-}
-
-static jobjectArray getEras(JNIEnv* env, UResourceBundle* gregorian) {
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle eras(ures_getByKey(gregorian, "eras", NULL, &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    ScopedResourceBundle abbreviatedEras(ures_getByKey(eras.get(), "abbreviated", NULL, &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-    return toStringArray(env, abbreviatedEras.get(), ures_getSize(abbreviatedEras.get()), -1, 0);
-}
-
-enum NameType { REGULAR, STAND_ALONE };
-enum NameWidth { LONG, SHORT };
-static jobjectArray getNames(JNIEnv* env, UResourceBundle* namesBundle, bool months, NameType type, NameWidth width) {
-    const char* typeKey = (type == REGULAR) ? "format" : "stand-alone";
-    const char* widthKey = (width == LONG) ? "wide" : "abbreviated";
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle formatBundle(ures_getByKey(namesBundle, typeKey, NULL, &status));
-    ScopedResourceBundle valuesBundle(ures_getByKey(formatBundle.get(), widthKey, NULL, &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-
-    // The months array has a trailing empty string. The days array has a leading empty string.
-    int count = ures_getSize(valuesBundle.get());
-    int offset = months ? 0 : 1;
-    jobjectArray result = toStringArray(env, valuesBundle.get(), count, count + 1, offset);
-    ScopedLocalRef<jstring> emptyString(env, env->NewStringUTF(""));
-    env->SetObjectArrayElement(result, months ? count : 0, emptyString.get());
-    return result;
-}
-
 static void setIntegerField(JNIEnv* env, jobject obj, const char* fieldName, int value) {
     ScopedLocalRef<jobject> integerValue(env, integerValueOf(env, value));
     jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "Ljava/lang/Integer;");
@@ -377,6 +286,21 @@
     env->SetObjectField(obj, fid, value);
 }
 
+static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString* valueArray, int32_t size) {
+    ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::stringClass, NULL));
+    for (int32_t i = 0; i < size ; i++) {
+        ScopedLocalRef<jstring> s(env, env->NewString(valueArray[i].getBuffer(),valueArray[i].length()));
+        if (env->ExceptionCheck()) {
+            return;
+        }
+        env->SetObjectArrayElement(result.get(), i, s.get());
+        if (env->ExceptionCheck()) {
+            return;
+        }
+    }
+    setStringArrayField(env, obj, fieldName, result.get());
+}
+
 static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
     UErrorCode status = U_ZERO_ERROR;
     int charCount;
@@ -384,168 +308,57 @@
     if (U_SUCCESS(status)) {
         setStringField(env, obj, fieldName, env->NewString(chars, charCount));
     } else {
-        LOGE("Error setting String field %s from ICU resource: %s", fieldName, u_errorName(status));
+        ALOGE("Error setting String field %s from ICU resource: %s", fieldName, u_errorName(status));
     }
 }
 
-static bool setStringField(JNIEnv* env, jobject obj, const char* key, const char* fieldName, UResourceBundle* bundle) {
-    if (bundle == NULL) {
-        return false;
-    }
-    UErrorCode status = U_ZERO_ERROR;
-    int charCount;
-    const UChar* chars = ures_getStringByKey(bundle, key, &charCount, &status);
-    if (U_SUCCESS(status)) {
-        setStringField(env, obj, fieldName, env->NewString(chars, charCount));
-        return true;
-    } else {
-        // Missing item in current resource bundle but not an error.
-        return false;
-    }
-}
-
-static void setStringField(JNIEnv* env, jobject obj, const char* key, const char* fieldName,
-    UResourceBundle* bundle, UResourceBundle* fallbackBundle) {
-    if (!setStringField(env, obj, key, fieldName, bundle) && fallbackBundle != NULL) {
-        setStringField(env, obj, key, fieldName, fallbackBundle);
-    }
-}
-
-static bool setCharField(JNIEnv* env, jobject obj, const char* key, const char* fieldName,
-    UResourceBundle* bundle) {
-    if (bundle == NULL) {
-        return false;
-    }
-    UErrorCode status = U_ZERO_ERROR;
-    int charCount;
-    const UChar* chars = ures_getStringByKey(bundle, key, &charCount, &status);
-    if (U_SUCCESS(status)) {
-        jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "C");
-        env->SetCharField(obj, fid, chars[0]);
-        return true;
-    } else {
-        // Missing item in current resource bundle but not an error.
-        return false;
-    }
-}
-
-static void setCharField(JNIEnv* env, jobject obj, const char* key, const char* fieldName,
-    UResourceBundle* bundle, UResourceBundle* fallbackBundle) {
-    if (!setCharField(env, obj, key, fieldName, bundle) && fallbackBundle != NULL) {
-        setCharField(env, obj, key, fieldName, fallbackBundle);
-    }
-}
-
-static void setNumberSymbols(JNIEnv* env, jobject obj, UResourceBundle* numberSymbols, UResourceBundle* fallbackNumberSymbols) {
-    setCharField(env, obj, "decimal", "decimalSeparator", numberSymbols, fallbackNumberSymbols);
-    setCharField(env, obj, "group", "groupingSeparator", numberSymbols, fallbackNumberSymbols);
-    setCharField(env, obj, "list", "patternSeparator", numberSymbols, fallbackNumberSymbols);
-    setCharField(env, obj, "percentSign", "percent", numberSymbols, fallbackNumberSymbols);
-    setCharField(env, obj, "perMille", "perMill", numberSymbols, fallbackNumberSymbols);
-    setCharField(env, obj, "decimal", "monetarySeparator", numberSymbols, fallbackNumberSymbols);
-    setCharField(env, obj, "minusSign", "minusSign", numberSymbols, fallbackNumberSymbols);
-    setStringField(env, obj, "exponential", "exponentSeparator", numberSymbols, fallbackNumberSymbols);
-    setStringField(env, obj, "infinity", "infinity", numberSymbols, fallbackNumberSymbols);
-    setStringField(env, obj, "nan", "NaN", numberSymbols, fallbackNumberSymbols);
-}
-
-static void setZeroDigitToDefault(JNIEnv* env, jobject obj) {
-    static jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, "zeroDigit", "C");
-    env->SetCharField(obj, fid, '0');
-}
-
-static void setZeroDigit(JNIEnv* env, jobject obj, bool isLatn, char* buffer) {
-    if (isLatn || buffer == NULL || buffer[0] == '\0') {
-        return setZeroDigitToDefault(env, obj);
-    }
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle numSystemRoot(ures_openDirect(NULL, "numberingSystems", &status));
-    if (U_FAILURE(status)) {
-        return setZeroDigitToDefault(env, obj);
-    }
-    ScopedResourceBundle numSystem(ures_getByKey(numSystemRoot.get(), "numberingSystems", NULL, &status));
-    if (U_FAILURE(status)) {
-        return setZeroDigitToDefault(env, obj);
-    }
-    ScopedResourceBundle nonLatnSystem(ures_getByKey(numSystem.get(), buffer, NULL, &status));
-    if (U_FAILURE(status)) {
-        return setZeroDigitToDefault(env, obj);
-    }
-    int32_t charCount = 0;
-    const UChar* chars = ures_getStringByKey(nonLatnSystem.get(), "desc", &charCount, &status);
-    if (charCount == 0) {
-        setZeroDigitToDefault(env, obj);
-    } else {
-        static jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, "zeroDigit", "C");
-        env->SetCharField(obj, fid, chars[0]);
-    }
-}
-
-static void setNumberElements(JNIEnv* env, jobject obj, UResourceBundle* numberElements) {
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle latnNumberRB(ures_getByKey(numberElements, "latn", NULL, &status));
-    if (U_FAILURE(status)) {
-        LOGW("Error getting ICU latn number elements system value: %s", u_errorName(status));
+static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
+    if (value.length() == 0) {
         return;
     }
-    ScopedResourceBundle patternsRB(ures_getByKey(latnNumberRB.get(), "patterns", NULL, &status));
-    if (U_FAILURE(status)) {
-        LOGW("Error getting ICU latn number patterns value: %s", u_errorName(status));
-        return;
-    }
-    // Get the patterns from the 'latn' numberElements
-    // This is a temporary workaround for ICU ticket#8611.
-    UResourceBundle* bundle = patternsRB.get();
-    setStringField(env, obj, "currencyFormat", "currencyPattern", bundle);
-    setStringField(env, obj, "decimalFormat", "numberPattern", bundle);
-    setStringField(env, obj, "percentFormat", "percentPattern", bundle);
+    jfieldID fid = env->GetFieldID(JniConstants::localeDataClass, fieldName, "C");
+    env->SetCharField(obj, fid, value.charAt(0));
+}
 
-    status = U_ZERO_ERROR;
-    bool isLatn = false;
-    char buffer[256];
-    buffer[0] = '\0';
-    ScopedResourceBundle defaultNumberElem(ures_getByKey(numberElements, "default", NULL, &status));
-    if (U_SUCCESS(status)) {
-        int32_t charCount = 256;
-        ures_getUTF8String(defaultNumberElem.get(), buffer, &charCount, true, &status);
-        buffer[charCount] = '\0';
-        if (U_FAILURE(status)) {
-            LOGW("Error getting ICU default number element system value: %s", u_errorName(status));
-            // Use latn number symbols instead.
-            isLatn = true;
-        } else {
-            isLatn = (strcmp(buffer, "latn") == 0);
-        }
-    } else {
-        // Not default data, fallback to latn number elements.
-        isLatn = true;
-    }
+static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
+    const UChar* chars = value.getBuffer();
+    setStringField(env, obj, fieldName, env->NewString(chars, value.length()));
+}
 
-    status = U_ZERO_ERROR;
-    setZeroDigit(env, obj, isLatn, buffer);
-    if (isLatn) {
-        ScopedResourceBundle symbolsRB(ures_getByKey(latnNumberRB.get(), "symbols", NULL, &status));
-        if (U_SUCCESS(status)) {
-            setNumberSymbols(env, obj, symbolsRB.get(), NULL);
-        } else {
-            LOGW("Missing ICU latn symbols system value: %s", u_errorName(status));
-        }
-    } else {
-        // Get every symbol item from default numbering system first. If it does not
-        // exist, get the symbol from latn numbering system.
-        ScopedResourceBundle defaultNumberRB(ures_getByKey(numberElements, (const char*)buffer, NULL, &status));
-        ScopedResourceBundle defaultSymbolsRB(ures_getByKey(defaultNumberRB.get(), "symbols", NULL, &status));
-        if (U_FAILURE(status)) {
-            LOGW("Missing ICU %s symbols system value: %s", buffer, u_errorName(status));
-            isLatn = true;  // Fallback to latn symbols.
-            status = U_ZERO_ERROR;
-        }
-        ScopedResourceBundle latnSymbolsRB(ures_getByKey(latnNumberRB.get(), "symbols", NULL, &status));
-        if (isLatn && U_FAILURE(status)) {
-            return;
-        }
-        setNumberSymbols(env, obj, defaultSymbolsRB.get(), latnSymbolsRB.get());
-    }
+static void setNumberPatterns(JNIEnv* env, jobject obj, jstring locale) {
+    UErrorCode status = U_ZERO_ERROR;
+    Locale localeObj = getLocale(env, locale);
+
+    UnicodeString pattern;
+    UniquePtr<DecimalFormat> fmt(static_cast<DecimalFormat*>(NumberFormat::createInstance(localeObj, UNUM_CURRENCY, status)));
+    pattern = fmt->toPattern(pattern.remove());
+    setStringField(env, obj, "currencyPattern", pattern);
+
+    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(localeObj, UNUM_DECIMAL, status)));
+    pattern = fmt->toPattern(pattern.remove());
+    setStringField(env, obj, "numberPattern", pattern);
+
+    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(localeObj, UNUM_PERCENT, status)));
+    pattern = fmt->toPattern(pattern.remove());
+    setStringField(env, obj, "percentPattern", pattern);
+}
+
+static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, jstring locale) {
+    UErrorCode status = U_ZERO_ERROR;
+    Locale localeObj = getLocale(env, locale);
+    DecimalFormatSymbols dfs(localeObj, status);
+
+    setCharField(env, obj, "decimalSeparator", dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol));
+    setCharField(env, obj, "groupingSeparator", dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
+    setCharField(env, obj, "patternSeparator", dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol));
+    setCharField(env, obj, "percent", dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol));
+    setCharField(env, obj, "perMill", dfs.getSymbol(DecimalFormatSymbols::kPerMillSymbol));
+    setCharField(env, obj, "monetarySeparator", dfs.getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
+    setCharField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
+    setStringField(env, obj, "exponentSeparator", dfs.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
+    setStringField(env, obj, "infinity", dfs.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
+    setStringField(env, obj, "NaN", dfs.getSymbol(DecimalFormatSymbols::kNaNSymbol));
+    setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
 }
 
 static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobject localeData) {
@@ -554,96 +367,110 @@
         return JNI_FALSE;
     }
 
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle root(ures_open(NULL, localeName.c_str(), &status));
+    // Get DateTimePatterns
+    UErrorCode status;
+    char currentLocale[ULOC_FULLNAME_CAPACITY];
+    int32_t localeNameLen = 0;
+    if (localeName.size() >= ULOC_FULLNAME_CAPACITY) {
+        return JNI_FALSE;  // Exceed ICU defined limit of the whole locale ID.
+    }
+    strcpy(currentLocale, localeName.c_str());
+    do {
+        status = U_ZERO_ERROR;
+        ScopedResourceBundle root(ures_open(NULL, currentLocale, &status));
+        if (U_FAILURE(status)) {
+            if (localeNameLen == 0) {
+                break;  // No parent locale, report this error outside the loop.
+            } else {
+                status = U_ZERO_ERROR;
+                continue;  // get parent locale.
+            }
+        }
+        ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
+        if (U_FAILURE(status)) {
+            status = U_ZERO_ERROR;
+            continue;  // get parent locale.
+        }
+
+        ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
+        if (U_FAILURE(status)) {
+            status = U_ZERO_ERROR;
+            continue;  // get parent locale.
+        }
+        ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
+        if (U_SUCCESS(status)) {
+            setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
+            setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
+            setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
+            setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
+            setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
+            setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
+            setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
+            setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
+            break;
+        } else {
+            status = U_ZERO_ERROR;  // get parent locale.
+        }
+    } while((localeNameLen = uloc_getParent(currentLocale, currentLocale, sizeof(currentLocale), &status)) >= 0);
     if (U_FAILURE(status)) {
-        LOGE("Error getting ICU resource bundle: %s", u_errorName(status));
+        ALOGE("Error getting ICU resource bundle: %s", u_errorName(status));
         return JNI_FALSE;
     }
 
-    ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
+    status = U_ZERO_ERROR;
+    Locale localeObj = getLocale(env, locale);
+
+    UniquePtr<Calendar> cal(Calendar::createInstance(localeObj, status));
     if (U_FAILURE(status)) {
-        LOGE("Error getting ICU calendar resource bundle: %s", u_errorName(status));
         return JNI_FALSE;
     }
+    setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek());
+    setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek());
 
-    ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
+    // Get DateFormatSymbols
+    status = U_ZERO_ERROR;
+    DateFormatSymbols dateFormatSym(localeObj, status);
     if (U_FAILURE(status)) {
-        LOGE("Error getting ICU gregorian resource bundle: %s", u_errorName(status));
         return JNI_FALSE;
     }
+    int32_t count = 0;
+    // Get AM/PM marker
+    const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
+    setStringArrayField(env, localeData, "amPm", amPmStrs, count);
+    const UnicodeString* erasStrs = dateFormatSym.getEras(count);
+    setStringArrayField(env, localeData, "eras", erasStrs, count);
 
-    int firstDayVals[] = { 0, 0 };
-    if (getDayIntVector(env, gregorian.get(), firstDayVals)) {
-        setIntegerField(env, localeData, "firstDayOfWeek", firstDayVals[0]);
-        setIntegerField(env, localeData, "minimalDaysInFirstWeek", firstDayVals[1]);
-    }
+    const UnicodeString* longMonthNames =
+       dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
+    setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
+    const UnicodeString* shortMonthNames =
+        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
+    setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
+    const UnicodeString* longWeekdayNames =
+        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
+    setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
+    const UnicodeString* shortWeekdayNames =
+        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
+    setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
 
-    jobjectArray amPmMarkers = getAmPmMarkers(env, gregorian.get());
-    setStringArrayField(env, localeData, "amPm", amPmMarkers);
-    env->DeleteLocalRef(amPmMarkers);
+    const UnicodeString* longStandAloneMonthNames =
+        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
+    setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
+    const UnicodeString* shortStandAloneMonthNames =
+        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
+    setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
+    const UnicodeString* longStandAloneWeekdayNames =
+        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
+    setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
+    const UnicodeString* shortStandAloneWeekdayNames =
+        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
+    setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
 
-    jobjectArray eras = getEras(env, gregorian.get());
-    setStringArrayField(env, localeData, "eras", eras);
-    env->DeleteLocalRef(eras);
-
-    ScopedResourceBundle dayNames(ures_getByKey(gregorian.get(), "dayNames", NULL, &status));
-    ScopedResourceBundle monthNames(ures_getByKey(gregorian.get(), "monthNames", NULL, &status));
-
-    // Get the regular month and weekday names.
-    jobjectArray longMonthNames = getNames(env, monthNames.get(), true, REGULAR, LONG);
-    jobjectArray shortMonthNames = getNames(env, monthNames.get(), true, REGULAR, SHORT);
-    jobjectArray longWeekdayNames = getNames(env, dayNames.get(), false, REGULAR, LONG);
-    jobjectArray shortWeekdayNames = getNames(env, dayNames.get(), false, REGULAR, SHORT);
-    setStringArrayField(env, localeData, "longMonthNames", longMonthNames);
-    setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames);
-    setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames);
-    setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames);
-
-    // Get the stand-alone month and weekday names. If they're not available (as they aren't for
-    // English), we reuse the regular names. If we returned null to Java, the usual fallback
-    // mechanisms would come into play and we'd end up with the bogus stand-alone names from the
-    // root locale ("1" for January, and so on).
-    jobjectArray longStandAloneMonthNames = getNames(env, monthNames.get(), true, STAND_ALONE, LONG);
-    if (longStandAloneMonthNames == NULL) {
-        longStandAloneMonthNames = longMonthNames;
-    }
-    jobjectArray shortStandAloneMonthNames = getNames(env, monthNames.get(), true, STAND_ALONE, SHORT);
-    if (shortStandAloneMonthNames == NULL) {
-        shortStandAloneMonthNames = shortMonthNames;
-    }
-    jobjectArray longStandAloneWeekdayNames = getNames(env, dayNames.get(), false, STAND_ALONE, LONG);
-    if (longStandAloneWeekdayNames == NULL) {
-        longStandAloneWeekdayNames = longWeekdayNames;
-    }
-    jobjectArray shortStandAloneWeekdayNames = getNames(env, dayNames.get(), false, STAND_ALONE, SHORT);
-    if (shortStandAloneWeekdayNames == NULL) {
-        shortStandAloneWeekdayNames = shortWeekdayNames;
-    }
-    setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames);
-    setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames);
-    setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames);
-    setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames);
-
-    ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
-    if (U_SUCCESS(status)) {
-        setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
-        setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
-        setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
-        setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
-        setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
-        setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
-        setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
-        setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
-    }
     status = U_ZERO_ERROR;
 
     // For numberPatterns and symbols.
-    ScopedResourceBundle numberElements(ures_getByKey(root.get(), "NumberElements", NULL, &status));
-    if (U_SUCCESS(status)) {
-        setNumberElements(env, localeData, numberElements.get());
-    }
-    status = U_ZERO_ERROR;
+    setNumberPatterns(env, localeData, locale);
+    setDecimalFormatSymbolsData(env, localeData, locale);
 
     jstring countryCode = env->NewStringUTF(Locale::createFromName(localeName.c_str()).getCountry());
     jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode);
@@ -750,7 +577,7 @@
     NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
 };
-int register_libcore_icu_ICU(JNIEnv* env) {
+void register_libcore_icu_ICU(JNIEnv* env) {
     std::string path;
     path = u_getDataDirectory();
     path += "/";
@@ -758,12 +585,12 @@
     path += ".dat";
 
     #define FAIL_WITH_STRERROR(s) \
-        LOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
-        return -1;
+        ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
+        abort();
     #define MAYBE_FAIL_WITH_ICU_ERROR(s) \
         if (status != U_ZERO_ERROR) {\
-            LOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
-            return -1; \
+            ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
+            abort(); \
         }
 
     // Open the file and get its length.
@@ -800,5 +627,5 @@
     // and bail.
     u_init(&status);
     MAYBE_FAIL_WITH_ICU_ERROR("u_init");
-    return jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
+    jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
index f74822d..d93c229 100644
--- a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
+++ b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
@@ -220,6 +220,6 @@
     NATIVE_METHOD(NativeBreakIterator, previousImpl, "(I)I"),
     NATIVE_METHOD(NativeBreakIterator, setTextImpl, "(ILjava/lang/String;)V"),
 };
-int register_libcore_icu_NativeBreakIterator(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativeBreakIterator", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativeBreakIterator(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativeBreakIterator", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeCollation.cpp b/luni/src/main/native/libcore_icu_NativeCollation.cpp
index 26e276e..3ed49e9 100644
--- a/luni/src/main/native/libcore_icu_NativeCollation.cpp
+++ b/luni/src/main/native/libcore_icu_NativeCollation.cpp
@@ -194,6 +194,6 @@
     NATIVE_METHOD(NativeCollation, setOffset, "(II)V"),
     NATIVE_METHOD(NativeCollation, setText, "(ILjava/lang/String;)V"),
 };
-int register_libcore_icu_NativeCollation(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativeCollation", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativeCollation(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativeCollation", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index 6a7fbc5..5b3761e 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -608,6 +608,6 @@
     NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)I"),
     NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)I"),
 };
-int register_libcore_icu_NativeConverter(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativeConverter(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
index b68c822..1fe4055 100644
--- a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
@@ -366,6 +366,6 @@
     NATIVE_METHOD(NativeDecimalFormat, setTextAttribute, "(IILjava/lang/String;)V"),
     NATIVE_METHOD(NativeDecimalFormat, toPatternImpl, "(IZ)Ljava/lang/String;"),
 };
-int register_libcore_icu_NativeDecimalFormat(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativeDecimalFormat", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativeDecimalFormat(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativeDecimalFormat", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeIDN.cpp b/luni/src/main/native/libcore_icu_NativeIDN.cpp
index 3d0cba3..16a6e1c 100644
--- a/luni/src/main/native/libcore_icu_NativeIDN.cpp
+++ b/luni/src/main/native/libcore_icu_NativeIDN.cpp
@@ -62,6 +62,6 @@
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(NativeIDN, convertImpl, "(Ljava/lang/String;IZ)Ljava/lang/String;"),
 };
-int register_libcore_icu_NativeIDN(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativeIDN", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativeIDN(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativeIDN", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
index 57f31f4..58f460c 100644
--- a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
+++ b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
@@ -45,6 +45,6 @@
     NATIVE_METHOD(NativeNormalizer, normalizeImpl, "(Ljava/lang/String;I)Ljava/lang/String;"),
     NATIVE_METHOD(NativeNormalizer, isNormalizedImpl, "(Ljava/lang/String;I)Z"),
 };
-int register_libcore_icu_NativeNormalizer(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativeNormalizer", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativeNormalizer(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativeNormalizer", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativePluralRules.cpp b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
index 3a443d7..a7164d0 100644
--- a/luni/src/main/native/libcore_icu_NativePluralRules.cpp
+++ b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
@@ -60,6 +60,6 @@
     NATIVE_METHOD(NativePluralRules, forLocaleImpl, "(Ljava/lang/String;)I"),
     NATIVE_METHOD(NativePluralRules, quantityForIntImpl, "(II)I"),
 };
-int register_libcore_icu_NativePluralRules(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/NativePluralRules", gMethods, NELEM(gMethods));
+void register_libcore_icu_NativePluralRules(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/NativePluralRules", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_TimeZones.cpp b/luni/src/main/native/libcore_icu_TimeZones.cpp
index 9da108d..b543238 100644
--- a/luni/src/main/native/libcore_icu_TimeZones.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZones.cpp
@@ -226,6 +226,6 @@
     NATIVE_METHOD(TimeZones, forCountryCode, "(Ljava/lang/String;)[Ljava/lang/String;"),
     NATIVE_METHOD(TimeZones, getZoneStringsImpl, "(Ljava/lang/String;[Ljava/lang/String;)[[Ljava/lang/String;"),
 };
-int register_libcore_icu_TimeZones(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/icu/TimeZones", gMethods, NELEM(gMethods));
+void register_libcore_icu_TimeZones(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/icu/TimeZones", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
index d3fdabf..1b888be 100644
--- a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
+++ b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
@@ -30,7 +30,7 @@
     NATIVE_METHOD(AsynchronousCloseMonitor, signalBlockedThreads, "(Ljava/io/FileDescriptor;)V"),
 };
 
-int register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env) {
+void register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env) {
     AsynchronousSocketCloseMonitor::init();
-    return jniRegisterNativeMethods(env, "libcore/io/AsynchronousCloseMonitor", gMethods, NELEM(gMethods));
+    jniRegisterNativeMethods(env, "libcore/io/AsynchronousCloseMonitor", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index a511978..bffb4a5 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -398,6 +398,6 @@
     NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"),
     NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"),
 };
-int register_libcore_io_Memory(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/io/Memory", gMethods, NELEM(gMethods));
+void register_libcore_io_Memory(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/io/Memory", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_io_OsConstants.cpp b/luni/src/main/native/libcore_io_OsConstants.cpp
index c432973..a1cfcf0 100644
--- a/luni/src/main/native/libcore_io_OsConstants.cpp
+++ b/luni/src/main/native/libcore_io_OsConstants.cpp
@@ -447,6 +447,6 @@
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(OsConstants, initConstants, "()V"),
 };
-int register_libcore_io_OsConstants(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/io/OsConstants", gMethods, NELEM(gMethods));
+void register_libcore_io_OsConstants(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/io/OsConstants", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index a90c593..30ca145 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -137,9 +137,12 @@
 }
 
 static void throwGaiException(JNIEnv* env, const char* functionName, int error) {
-    if (error == EAI_SYSTEM) {
-        // EAI_SYSTEM means "look at errno instead", so we want our GaiException to have the
-        // relevant ErrnoException as its cause.
+    if (errno != 0) {
+        // EAI_SYSTEM should mean "look at errno instead", but both glibc and bionic seem to
+        // mess this up. In particular, if you don't have INTERNET permission, errno will be EACCES
+        // but you'll get EAI_NONAME or EAI_NODATA. So we want our GaiException to have a
+        // potentially-relevant ErrnoException as its cause even if error != EAI_SYSTEM.
+        // http://code.google.com/p/android/issues/detail?id=15722
         throwErrnoException(env, functionName);
         // Deliberately fall through to throw another exception...
     }
@@ -553,6 +556,7 @@
     hints.ai_protocol = env->GetIntField(javaHints, protocolFid);
 
     addrinfo* addressList = NULL;
+    errno = 0;
     int rc = getaddrinfo(node.c_str(), NULL, &hints, &addressList);
     UniquePtr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
     if (rc != 0) {
@@ -566,7 +570,7 @@
         if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
             ++addressCount;
         } else {
-            LOGE("getaddrinfo unexpected ai_family %i", ai->ai_family);
+            ALOGE("getaddrinfo unexpected ai_family %i", ai->ai_family);
         }
     }
     if (addressCount == 0) {
@@ -584,7 +588,7 @@
     for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) {
         if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
             // Unknown address family. Skip this address.
-            LOGE("getaddrinfo unexpected ai_family %i", ai->ai_family);
+            ALOGE("getaddrinfo unexpected ai_family %i", ai->ai_family);
             continue;
         }
 
@@ -631,6 +635,7 @@
     // then remove this hack.
     socklen_t size = (ss.ss_family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
     char buf[NI_MAXHOST]; // NI_MAXHOST is longer than INET6_ADDRSTRLEN.
+    errno = 0;
     int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), size, buf, sizeof(buf), NULL, 0, flags);
     if (rc != 0) {
         throwGaiException(env, "getnameinfo", rc);
@@ -908,7 +913,16 @@
         ++count;
     }
 
-    int rc = TEMP_FAILURE_RETRY(poll(fds.get(), count, timeoutMs));
+    // Since we don't know which fds -- if any -- are sockets, be conservative and register
+    // all fds for asynchronous socket close monitoring.
+    std::vector<AsynchronousSocketCloseMonitor*> monitors;
+    for (size_t i = 0; i < count; ++i) {
+        monitors.push_back(new AsynchronousSocketCloseMonitor(fds[i].fd));
+    }
+    int rc = poll(fds.get(), count, timeoutMs);
+    for (size_t i = 0; i < monitors.size(); ++i) {
+        delete monitors[i];
+    }
     if (rc == -1) {
         throwErrnoException(env, "poll");
         return -1;
@@ -1304,6 +1318,6 @@
     NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
     NATIVE_METHOD(Posix, writev, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
 };
-int register_libcore_io_Posix(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods));
+void register_libcore_io_Posix(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_net_RawSocket.cpp b/luni/src/main/native/libcore_net_RawSocket.cpp
index a4398f4..4e5059f 100644
--- a/luni/src/main/native/libcore_net_RawSocket.cpp
+++ b/luni/src/main/native/libcore_net_RawSocket.cpp
@@ -53,37 +53,36 @@
  * setBlocking() method followed by polymorphic bind().
  */
 static void RawSocket_create(JNIEnv* env, jclass, jobject fileDescriptor,
-    jstring interfaceName) {
+    jshort protocolType, jstring interfaceName)
+{
 
   ScopedUtfChars ifname(env, interfaceName);
   if (ifname.c_str() == NULL) {
     return;
   }
 
-  short protocol = ETH_P_IP;
-  sockunion su;
   memset(&su, 0, sizeof(su));
   su.sll.sll_family = PF_PACKET;
-  su.sll.sll_protocol = htons(protocol);
+  su.sll.sll_protocol = htons(protocolType);
   su.sll.sll_ifindex = if_nametoindex(ifname.c_str());
+  int sock = socket(PF_PACKET, SOCK_DGRAM, htons(protocolType));
 
-  int sock = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
   if (sock == -1) {
-    LOGE("Can't create socket %s", strerror(errno));
+    ALOGE("Can't create socket %s", strerror(errno));
     jniThrowSocketException(env, errno);
     return;
   }
 
   jniSetFileDescriptorOfFD(env, fileDescriptor, sock);
   if (!setBlocking(sock, false)) {
-    LOGE("Can't set non-blocking mode on socket %s", strerror(errno));
+    ALOGE("Can't set non-blocking mode on socket %s", strerror(errno));
     jniThrowSocketException(env, errno);
     return;
   }
 
   int err = bind(sock, &su.sa, sizeof(su));
   if (err != 0) {
-    LOGE("Socket bind error %s", strerror(errno));
+    ALOGE("Socket bind error %s", strerror(errno));
     jniThrowSocketException(env, errno);
     return;
   }
@@ -96,8 +95,8 @@
  * Assumes that the caller has validated the offset & byteCount values.
  */
 static int RawSocket_sendPacket(JNIEnv* env, jclass, jobject fileDescriptor,
-    jstring interfaceName, jbyteArray destMac, jbyteArray packet, jint offset,
-    jint byteCount)
+    jstring interfaceName, jshort protocolType, jbyteArray destMac,
+    jbyteArray packet, jint offset, jint byteCount)
 {
   NetFd fd(env, fileDescriptor);
 
@@ -120,14 +119,12 @@
     return 0;
   }
 
-  short protocol = ETH_P_IP;
-  sockunion su;
   memset(&su, 0, sizeof(su));
   su.sll.sll_hatype = htons(1); // ARPHRD_ETHER
   su.sll.sll_halen = mac.size();
   memcpy(&su.sll.sll_addr, mac.get(), mac.size());
   su.sll.sll_family = AF_PACKET;
-  su.sll.sll_protocol = htons(protocol);
+  su.sll.sll_protocol = htons(protocolType);
   su.sll.sll_ifindex = if_nametoindex(ifname.c_str());
 
   int err;
@@ -185,31 +182,33 @@
     return 0;
   }
 
-  // quick check for UDP type & UDP port
-  // the packet is an IP header, UDP header, and UDP payload
-  if ((size < (sizeof(iphdr) + sizeof(udphdr)))) {
-    return 0;  // runt packet
-  }
+  if (port != -1) {
+    // quick check for UDP type & UDP port
+    // the packet is an IP header, UDP header, and UDP payload
+    if ((size < (sizeof(struct iphdr) + sizeof(struct udphdr)))) {
+      return 0;  // runt packet
+    }
 
-  u_int8_t ip_proto = ((iphdr *) packetData)->protocol;
-  if (ip_proto != IPPROTO_UDP) {
-    return 0;  // something other than UDP
-  }
+    u_int8_t ip_proto = ((iphdr *) packetData)->protocol;
+    if (ip_proto != IPPROTO_UDP) {
+      return 0;  // something other than UDP
+    }
 
-  __be16 destPort = htons((reinterpret_cast<udphdr*>(packetData + sizeof(iphdr)))->dest);
-  if (destPort != port) {
-    return 0; // something other than requested port
+    __be16 destPort = htons((reinterpret_cast<udphdr*>(packetData + sizeof(iphdr)))->dest);
+    if (destPort != port) {
+      return 0; // something other than requested port
+    }
   }
 
   return size;
 }
 
 static JNINativeMethod gRawMethods[] = {
-  NATIVE_METHOD(RawSocket, create, "(Ljava/io/FileDescriptor;Ljava/lang/String;)V"),
-  NATIVE_METHOD(RawSocket, sendPacket, "(Ljava/io/FileDescriptor;Ljava/lang/String;[B[BII)I"),
+  NATIVE_METHOD(RawSocket, create, "(Ljava/io/FileDescriptor;SLjava/lang/String;)V"),
+  NATIVE_METHOD(RawSocket, sendPacket, "(Ljava/io/FileDescriptor;Ljava/lang/String;S[B[BII)I"),
   NATIVE_METHOD(RawSocket, recvPacket, "(Ljava/io/FileDescriptor;[BIIII)I"),
 };
 
-int register_libcore_net_RawSocket(JNIEnv* env) {
-  return jniRegisterNativeMethods(env, "libcore/net/RawSocket", gRawMethods, NELEM(gRawMethods));
+void register_libcore_net_RawSocket(JNIEnv* env) {
+  jniRegisterNativeMethods(env, "libcore/net/RawSocket", gRawMethods, NELEM(gRawMethods));
 }
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 3f6c1aa..d08b7d2 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -27,7 +27,7 @@
 #include "UniquePtr.h"
 #include "jni.h"
 #include "cutils/log.h"
-#include "cutils/jstring.h" // for strcpylen8to16
+#include "unicode/unistr.h"
 
 #include <string.h>
 #include <expat.h>
@@ -410,26 +410,23 @@
  * Copies UTF-8 characters into the buffer. Returns the number of Java chars
  * which were buffered.
  *
- * @param characters to copy into the buffer
- * @param length of characters to copy (in bytes)
  * @returns number of UTF-16 characters which were copied
  */
-static size_t fillBuffer(ParsingContext* parsingContext, const char* characters, int length) {
+static size_t fillBuffer(ParsingContext* parsingContext, const char* utf8, int byteCount) {
     JNIEnv* env = parsingContext->env;
 
-    // Grow buffer if necessary.
-    jcharArray buffer = parsingContext->ensureCapacity(length);
-    if (buffer == NULL) return -1;
+    // Grow buffer if necessary (the length in bytes is always >= the length in chars).
+    jcharArray javaChars = parsingContext->ensureCapacity(byteCount);
+    if (javaChars == NULL) return -1;
 
-    // Decode UTF-8 characters into our buffer.
-    ScopedCharArrayRW nativeBuffer(env, buffer);
-    if (nativeBuffer.get() == NULL) {
+    // Decode UTF-8 characters into our char[].
+    ScopedCharArrayRW chars(env, javaChars);
+    if (chars.get() == NULL) {
         return -1;
     }
-
-    size_t utf16length;
-    strcpylen8to16(nativeBuffer.get(), characters, length, &utf16length);
-    return utf16length;
+    UErrorCode status = U_ZERO_ERROR;
+    UnicodeString utf16(UnicodeString::fromUTF8(StringPiece(utf8, byteCount)));
+    return utf16.extract(chars.get(), byteCount, status);
 }
 
 /**
@@ -1395,13 +1392,7 @@
     NATIVE_METHOD(ExpatAttributes, getValueForQName, "(ILjava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ExpatAttributes, getValue, "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
 };
-int register_org_apache_harmony_xml_ExpatParser(JNIEnv* env) {
-    int result = jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatParser",
-        parserMethods, NELEM(parserMethods));
-    if (result != 0) {
-        return result;
-    }
-
-    return jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatAttributes",
-        attributeMethods, NELEM(attributeMethods));
+void register_org_apache_harmony_xml_ExpatParser(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatParser", parserMethods, NELEM(parserMethods));
+    jniRegisterNativeMethods(env, "org/apache/harmony/xml/ExpatAttributes", attributeMethods, NELEM(attributeMethods));
 }
diff --git a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index 388ead7..687c57e 100644
--- a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -28,6 +28,7 @@
 #include <jni.h>
 
 #include <openssl/dsa.h>
+#include <openssl/engine.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/rand.h>
@@ -50,7 +51,7 @@
 
 #ifdef WITH_JNI_TRACE
 #define JNI_TRACE(...) \
-        ((void)LOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__));     \
+        ((void)ALOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__));     \
 /*
         ((void)printf("I/" LOG_TAG "-jni:"));         \
         ((void)printf(__VA_ARGS__));          \
@@ -104,6 +105,13 @@
 };
 typedef UniquePtr<EVP_MD_CTX, EVP_MD_CTX_Delete> Unique_EVP_MD_CTX;
 
+struct EVP_CIPHER_CTX_Delete {
+    void operator()(EVP_CIPHER_CTX* p) const {
+        EVP_CIPHER_CTX_cleanup(p);
+    }
+};
+typedef UniquePtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_Delete> Unique_EVP_CIPHER_CTX;
+
 struct EVP_PKEY_Delete {
     void operator()(EVP_PKEY* p) const {
         EVP_PKEY_free(p);
@@ -301,7 +309,7 @@
     if (asprintf(&str, "%s: ssl=%p: %s", message, ssl, sslErrorStr) <= 0) {
         // problem with asprintf, just throw argument message, log everything
         throwSSLExceptionStr(env, message);
-        LOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr);
+        ALOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr);
         freeOpenSslErrorState();
         return;
     }
@@ -359,7 +367,7 @@
         throwSSLExceptionStr(env, allocStr);
     }
 
-    LOGV("%s", allocStr);
+    ALOGV("%s", allocStr);
     free(allocStr);
     freeOpenSslErrorState();
 }
@@ -407,21 +415,65 @@
 
 /**
  * Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
- * fly.
+ * fly. Returns true on success. If the return value is false, there is a
+ * pending exception.
  */
-static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) {
-    JNI_TRACE("arrayToBignum(%p)", source);
+static bool arrayToBignum(JNIEnv* env, jbyteArray source, BIGNUM** dest) {
+    JNI_TRACE("arrayToBignum(%p, %p)", source, *dest);
 
     ScopedByteArrayRO sourceBytes(env, source);
     if (sourceBytes.get() == NULL) {
         JNI_TRACE("arrayToBignum(%p) => NULL", source);
-        return NULL;
+        return false;
     }
-    BIGNUM* bn = BN_bin2bn(reinterpret_cast<const unsigned char*>(sourceBytes.get()),
+    *dest = BN_bin2bn(reinterpret_cast<const unsigned char*>(sourceBytes.get()),
                            sourceBytes.size(),
                            NULL);
-    JNI_TRACE("arrayToBignum(%p) => %p", source, bn);
-    return bn;
+    if (*dest == NULL) {
+        jniThrowRuntimeException(env, "Conversion to BIGNUM failed");
+        JNI_TRACE("arrayToBignum(%p) => threw exception", source);
+        return false;
+    }
+
+    JNI_TRACE("arrayToBignum(%p) => %p", source, *dest);
+    return true;
+}
+
+/**
+ * Converts an OpenSSL BIGNUM to a Java byte[] array.
+ */
+static jbyteArray bignumToArray(JNIEnv* env, BIGNUM* source) {
+    JNI_TRACE("bignumToArray(%p)", source);
+
+    if (source == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return NULL;
+    }
+
+    int len = BN_num_bytes(source) + 1;
+    jbyteArray javaBytes = env->NewByteArray(len);
+    ScopedByteArrayRW bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
+        JNI_TRACE("bignumToArray(%p) => NULL", source);
+        return NULL;
+    }
+
+    unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get());
+
+    // Set the sign for the Java code.
+    if (BN_is_negative(source)) {
+        *tmp = 0xFF;
+    } else {
+        *tmp = 0x00;
+    }
+
+    if (BN_bn2bin(source, tmp + 1) <= 0) {
+        throwExceptionIfNecessary(env, "bignumToArray");
+        return NULL;
+    }
+
+    JNI_TRACE("bignumToArray(%p) => %p", source, javaBytes);
+    return javaBytes;
 }
 
 /**
@@ -500,6 +552,94 @@
     THREAD_setup();
 }
 
+static void NativeCrypto_ENGINE_load_dynamic(JNIEnv*, jclass) {
+    JNI_TRACE("ENGINE_load_dynamic()");
+
+    ENGINE_load_dynamic();
+}
+
+static jint NativeCrypto_ENGINE_by_id(JNIEnv* env, jclass, jstring idJava) {
+    JNI_TRACE("ENGINE_by_id(%p)", idJava);
+
+    ScopedUtfChars id(env, idJava);
+    if (id.c_str() == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "id == NULL");
+        return 0;
+    }
+
+    ENGINE* e = ENGINE_by_id(id.c_str());
+    if (e == NULL) {
+        throwExceptionIfNecessary(env, "ENGINE_by_id");
+        return 0;
+    }
+
+    JNI_TRACE("ENGINE_by_id(%p) => %p", idJava, e);
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(e));
+}
+
+static jint NativeCrypto_ENGINE_init(JNIEnv* env, jclass, jint engineRef) {
+    ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
+    JNI_TRACE("ENGINE_init(%p)", e);
+
+    if (e == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0");
+        return 0;
+    }
+
+    int ret = ENGINE_init(e);
+    JNI_TRACE("ENGINE_init(%p) => %d", e, ret);
+    return ret;
+}
+
+static jint NativeCrypto_ENGINE_finish(JNIEnv* env, jclass, jint engineRef) {
+    ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
+    JNI_TRACE("ENGINE_finish(%p)", e);
+
+    if (e == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0");
+        return 0;
+    }
+
+    int ret = ENGINE_finish(e);
+    JNI_TRACE("ENGINE_finish(%p) => %d", e, ret);
+    return ret;
+}
+
+static jint NativeCrypto_ENGINE_free(JNIEnv* env, jclass, jint engineRef) {
+    ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
+    JNI_TRACE("ENGINE_free(%p)", e);
+
+    if (e == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0");
+        return 0;
+    }
+
+    int ret = ENGINE_free(e);
+    JNI_TRACE("ENGINE_free(%p) => %d", e, ret);
+    return ret;
+}
+
+static jint NativeCrypto_ENGINE_load_private_key(JNIEnv* env, jclass, jint engineRef,
+        jstring idJava) {
+    ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
+    JNI_TRACE("ENGINE_load_private_key(%p, %p)", e, idJava);
+
+    ScopedUtfChars id(env, idJava);
+    if (id.c_str() == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "id == NULL");
+        return 0;
+    }
+
+    Unique_EVP_PKEY pkey(ENGINE_load_private_key(e, id.c_str(), NULL, NULL));
+    if (pkey.get() == NULL) {
+        throwExceptionIfNecessary(env, "ENGINE_load_private_key");
+        return 0;
+    }
+
+    JNI_TRACE("ENGINE_load_private_key(%p, %p) => %p", e, idJava, pkey.get());
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
+}
+
 /**
  * public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g,
  *                                           byte[] pub_key, byte[] priv_key);
@@ -516,16 +656,28 @@
         return NULL;
     }
 
-    dsa->p = arrayToBignum(env, p);
-    dsa->q = arrayToBignum(env, q);
-    dsa->g = arrayToBignum(env, g);
-    dsa->pub_key = arrayToBignum(env, pub_key);
-
-    if (priv_key != NULL) {
-        dsa->priv_key = arrayToBignum(env, priv_key);
+    if (!arrayToBignum(env, p, &dsa->p)) {
+        return NULL;
     }
 
-    if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL || dsa->pub_key == NULL) {
+    if (!arrayToBignum(env, q, &dsa->q)) {
+        return NULL;
+    }
+
+    if (!arrayToBignum(env, g, &dsa->g)) {
+        return NULL;
+    }
+
+    if (pub_key != NULL && !arrayToBignum(env, pub_key, &dsa->pub_key)) {
+        return NULL;
+    }
+
+    if (priv_key != NULL && !arrayToBignum(env, priv_key, &dsa->priv_key)) {
+        return NULL;
+    }
+
+    if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL
+            || (dsa->pub_key == NULL && dsa->priv_key == NULL)) {
         jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
         return NULL;
     }
@@ -548,30 +700,56 @@
 /**
  * private static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
  */
-static EVP_PKEY* NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass,
+static jint NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass,
                                                jbyteArray n, jbyteArray e, jbyteArray d,
-                                               jbyteArray p, jbyteArray q) {
-    JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p)", n, e, d, p, q);
+                                               jbyteArray p, jbyteArray q,
+                                               jbyteArray dmp1, jbyteArray dmq1,
+                                               jbyteArray iqmp) {
+    JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p, dmp1=%p, dmq1=%p, iqmp=%p)",
+            n, e, d, p, q, dmp1, dmq1, iqmp);
 
     Unique_RSA rsa(RSA_new());
     if (rsa.get() == NULL) {
         jniThrowRuntimeException(env, "RSA_new failed");
-        return NULL;
+        return 0;
     }
 
-    rsa->n = arrayToBignum(env, n);
-    rsa->e = arrayToBignum(env, e);
-
-    if (d != NULL) {
-        rsa->d = arrayToBignum(env, d);
+    if (e == NULL && d == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "e == NULL && d == NULL");
+        JNI_TRACE("NativeCrypto_EVP_PKEY_new_RSA => e == NULL && d == NULL");
+        return 0;
     }
 
-    if (p != NULL) {
-        rsa->p = arrayToBignum(env, p);
+    if (!arrayToBignum(env, n, &rsa->n)) {
+        return 0;
     }
 
-    if (q != NULL) {
-        rsa->q = arrayToBignum(env, q);
+    if (e != NULL && !arrayToBignum(env, e, &rsa->e)) {
+        return 0;
+    }
+
+    if (d != NULL && !arrayToBignum(env, d, &rsa->d)) {
+        return 0;
+    }
+
+    if (p != NULL && !arrayToBignum(env, p, &rsa->p)) {
+        return 0;
+    }
+
+    if (q != NULL && !arrayToBignum(env, q, &rsa->q)) {
+        return 0;
+    }
+
+    if (dmp1 != NULL && !arrayToBignum(env, dmp1, &rsa->dmp1)) {
+        return 0;
+    }
+
+    if (dmq1 != NULL && !arrayToBignum(env, dmq1, &rsa->dmq1)) {
+        return 0;
+    }
+
+    if (iqmp != NULL && !arrayToBignum(env, iqmp, &rsa->iqmp)) {
+        return 0;
     }
 
 #ifdef WITH_JNI_TRACE
@@ -581,23 +759,73 @@
     }
 #endif
 
-    if (rsa->n == NULL || rsa->e == NULL) {
+    if (rsa->n == NULL || (rsa->e == NULL && rsa->d == NULL)) {
         jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
-        return NULL;
+        return 0;
+    }
+
+    /*
+     * If the private exponent is available, there is the potential to do signing
+     * operations. If the public exponent is also available, OpenSSL will do RSA
+     * blinding. Enable it if possible.
+     */
+    if (rsa->d != NULL) {
+        if (rsa->e != NULL) {
+            JNI_TRACE("EVP_PKEY_new_RSA(...) enabling RSA blinding => %p", rsa.get());
+            RSA_blinding_on(rsa.get(), NULL);
+        } else {
+            JNI_TRACE("EVP_PKEY_new_RSA(...) disabling RSA blinding => %p", rsa.get());
+            RSA_blinding_off(rsa.get());
+        }
     }
 
     Unique_EVP_PKEY pkey(EVP_PKEY_new());
     if (pkey.get() == NULL) {
         jniThrowRuntimeException(env, "EVP_PKEY_new failed");
-        return NULL;
+        return 0;
     }
     if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
         jniThrowRuntimeException(env, "EVP_PKEY_new failed");
-        return NULL;
+        return 0;
     }
     OWNERSHIP_TRANSFERRED(rsa);
-    JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p) => %p", n, e, d, p, q, pkey.get());
-    return pkey.release();
+    JNI_TRACE("EVP_PKEY_new_RSA(n=%p, e=%p, d=%p, p=%p, q=%p dmp1=%p, dmq1=%p, iqmp=%p) => %p",
+            n, e, d, p, q, dmp1, dmq1, iqmp, pkey.get());
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
+}
+
+/**
+ * private static native int EVP_PKEY_size(int pkey);
+ */
+static int NativeCrypto_EVP_PKEY_type(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("EVP_PKEY_type(%p)", pkey);
+
+    if (pkey == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    int result = EVP_PKEY_type(pkey->type);
+    JNI_TRACE("EVP_PKEY_type(%p) => %d", pkey, result);
+    return result;
+}
+
+/**
+ * private static native int EVP_PKEY_size(int pkey);
+ */
+static int NativeCrypto_EVP_PKEY_size(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("EVP_PKEY_size(%p)", pkey);
+
+    if (pkey == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    int result = EVP_PKEY_size(pkey);
+    JNI_TRACE("EVP_PKEY_size(%p) => %d", pkey, result);
+    return result;
 }
 
 /**
@@ -612,6 +840,425 @@
 }
 
 /*
+ * static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int, byte[])
+ */
+static jbyteArray NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("i2d_PKCS8_PRIV_KEY_INFO(%p)", pkey);
+
+    if (pkey == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return NULL;
+    }
+
+    Unique_PKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey));
+    if (pkcs8.get() == NULL) {
+        throwExceptionIfNecessary(env, "NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO");
+        JNI_TRACE("key=%p i2d_PKCS8_PRIV_KEY_INFO => error from key to PKCS8", pkey);
+        return NULL;
+    }
+
+    int len = i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), NULL);
+    if (len < 0) {
+        throwExceptionIfNecessary(env, "i2d_PKCS8_PRIV_KEY_INFO");
+        return NULL;
+    }
+
+    jbyteArray javaBytes = env->NewByteArray(len);
+    ScopedByteArrayRW bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
+        return NULL;
+    }
+
+    unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get());
+    if (i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), &tmp) < 0) {
+        throwExceptionIfNecessary(env, "i2d_PKCS8_PRIV_KEY_INFO");
+        return NULL;
+    }
+
+    JNI_TRACE("pkey=%p i2d_PKCS8_PRIV_KEY_INFO => size=%d", pkey, len);
+    return javaBytes;
+}
+
+/*
+ * static native int d2i_PKCS8_PRIV_KEY_INFO(byte[])
+ */
+static jint NativeCrypto_d2i_PKCS8_PRIV_KEY_INFO(JNIEnv* env, jclass, jbyteArray keyJavaBytes) {
+    JNI_TRACE("d2i_PKCS8_PRIV_KEY_INFO(%p)", keyJavaBytes);
+
+    ScopedByteArrayRO bytes(env, keyJavaBytes);
+    if (bytes.get() == NULL) {
+        JNI_TRACE("bytes=%p d2i_PKCS8_PRIV_KEY_INFO => threw exception", keyJavaBytes);
+        return 0;
+    }
+
+    const unsigned char* tmp = reinterpret_cast<const unsigned char*>(bytes.get());
+    Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(NULL, &tmp, bytes.size()));
+    if (pkcs8.get() == NULL) {
+        throwExceptionIfNecessary(env, "d2i_PKCS8_PRIV_KEY_INFO");
+        JNI_TRACE("ssl=%p d2i_PKCS8_PRIV_KEY_INFO => error from DER to PKCS8", keyJavaBytes);
+        return 0;
+    }
+
+    Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get()));
+    if (pkey.get() == NULL) {
+        throwExceptionIfNecessary(env, "d2i_PKCS8_PRIV_KEY_INFO");
+        JNI_TRACE("ssl=%p d2i_PKCS8_PRIV_KEY_INFO => error from PKCS8 to key", keyJavaBytes);
+        return 0;
+    }
+
+    JNI_TRACE("bytes=%p d2i_PKCS8_PRIV_KEY_INFO => %p", keyJavaBytes, pkey.get());
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
+}
+
+/*
+ * static native byte[] i2d_PUBKEY(int)
+ */
+static jbyteArray NativeCrypto_i2d_PUBKEY(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("i2d_PUBKEY(%p)", pkey);
+
+    if (pkey == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return NULL;
+    }
+
+    int len = i2d_PUBKEY(pkey, NULL);
+    if (len < 0) {
+        throwExceptionIfNecessary(env, "i2d_PUBKEY");
+        JNI_TRACE("i2d_PUBKEY(%p) => threw error measuring key", pkey);
+        return NULL;
+    }
+
+    jbyteArray javaBytes = env->NewByteArray(len);
+    ScopedByteArrayRW bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
+        return NULL;
+    }
+
+    unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get());
+    if (i2d_PUBKEY(pkey, &tmp) < 0) {
+        throwExceptionIfNecessary(env, "i2d_PUBKEY");
+        JNI_TRACE("i2d_PUBKEY(%p) => threw error converting key", pkey);
+        return NULL;
+    }
+
+    JNI_TRACE("pkey=%p i2d_PUBKEY => size=%d", pkey, len);
+    return javaBytes;
+}
+
+/*
+ * static native int d2i_PUBKEY(byte[])
+ */
+static jint NativeCrypto_d2i_PUBKEY(JNIEnv* env, jclass, jbyteArray javaBytes) {
+    JNI_TRACE("d2i_PUBKEY(%p)", javaBytes);
+
+    ScopedByteArrayRO bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
+        JNI_TRACE("d2i_PUBKEY(%p) => threw error", javaBytes);
+        return 0;
+    }
+
+    const unsigned char* tmp = reinterpret_cast<const unsigned char*>(bytes.get());
+    Unique_EVP_PKEY pkey(d2i_PUBKEY(NULL, &tmp, bytes.size()));
+    if (pkey.get() == NULL) {
+        JNI_TRACE("bytes=%p d2i_PUBKEY => threw exception", javaBytes);
+        throwExceptionIfNecessary(env, "d2i_PUBKEY");
+        return 0;
+    }
+
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
+}
+
+/*
+ * public static native int RSA_generate_key(int modulusBits, byte[] publicExponent);
+ */
+static jint NativeCrypto_RSA_generate_key_ex(JNIEnv* env, jclass, jint modulusBits,
+        jbyteArray publicExponent) {
+    JNI_TRACE("RSA_generate_key_ex(%d, %p)", modulusBits, publicExponent);
+
+    BIGNUM* eRef;
+    if (!arrayToBignum(env, publicExponent, &eRef)) {
+        return 0;
+    }
+    Unique_BIGNUM e(eRef);
+
+    Unique_RSA rsa(RSA_new());
+    if (rsa.get() == NULL) {
+        jniThrowOutOfMemoryError(env, "Unable to allocate RSA key");
+        return 0;
+    }
+
+    if (RSA_generate_key_ex(rsa.get(), modulusBits, e.get(), NULL) < 0) {
+        throwExceptionIfNecessary(env, "RSA_generate_key_ex");
+        return 0;
+    }
+
+    Unique_EVP_PKEY pkey(EVP_PKEY_new());
+    if (pkey.get() == NULL) {
+        jniThrowRuntimeException(env, "RSA_generate_key_ex failed");
+        return 0;
+    }
+
+    if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
+        jniThrowRuntimeException(env, "RSA_generate_key_ex failed");
+        return 0;
+    }
+
+    OWNERSHIP_TRANSFERRED(rsa);
+    JNI_TRACE("RSA_generate_key_ex(n=%d, e=%p) => %p", modulusBits, publicExponent, pkey.get());
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
+}
+
+/*
+ * public static native byte[][] get_RSA_public_params(int);
+ */
+static jobjectArray NativeCrypto_get_RSA_public_params(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("get_RSA_public_params(%p)", pkey);
+
+    Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+    if (rsa.get() == NULL) {
+        jniThrowRuntimeException(env, "get_RSA_public_params failed");
+        return 0;
+    }
+
+    jobjectArray joa = env->NewObjectArray(2, JniConstants::byteArrayClass, NULL);
+    if (joa == NULL) {
+        return NULL;
+    }
+
+    jbyteArray n = bignumToArray(env, rsa->n);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+    env->SetObjectArrayElement(joa, 0, n);
+
+    jbyteArray e = bignumToArray(env, rsa->e);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+    env->SetObjectArrayElement(joa, 1, e);
+
+    return joa;
+}
+
+/*
+ * public static native byte[][] get_RSA_private_params(int);
+ */
+static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("get_RSA_public_params(%p)", pkey);
+
+    Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+    if (rsa.get() == NULL) {
+        jniThrowRuntimeException(env, "get_RSA_public_params failed");
+        return 0;
+    }
+
+    jobjectArray joa = env->NewObjectArray(8, JniConstants::byteArrayClass, NULL);
+    if (joa == NULL) {
+        return NULL;
+    }
+
+    jbyteArray n = bignumToArray(env, rsa->n);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+    env->SetObjectArrayElement(joa, 0, n);
+
+    if (rsa->e != NULL) {
+        jbyteArray e = bignumToArray(env, rsa->e);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 1, e);
+    }
+
+    if (rsa->d != NULL) {
+        jbyteArray d = bignumToArray(env, rsa->d);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 2, d);
+    }
+
+    if (rsa->p != NULL) {
+        jbyteArray p = bignumToArray(env, rsa->p);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 3, p);
+    }
+
+    if (rsa->q != NULL) {
+        jbyteArray q = bignumToArray(env, rsa->q);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 4, q);
+    }
+
+    if (rsa->dmp1 != NULL) {
+        jbyteArray dmp1 = bignumToArray(env, rsa->dmp1);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 5, dmp1);
+    }
+
+    if (rsa->dmq1 != NULL) {
+        jbyteArray dmq1 = bignumToArray(env, rsa->dmq1);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 6, dmq1);
+    }
+
+    if (rsa->iqmp != NULL) {
+        jbyteArray iqmp = bignumToArray(env, rsa->iqmp);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 7, iqmp);
+    }
+
+    return joa;
+}
+
+/*
+ * public static native int DSA_generate_key(int, byte[]);
+ */
+static jint NativeCrypto_DSA_generate_key(JNIEnv* env, jclass, jint primeBits,
+        jbyteArray seedJavaBytes, jbyteArray gBytes, jbyteArray pBytes, jbyteArray qBytes) {
+    JNI_TRACE("DSA_generate_key(%d, %p, %p, %p, %p)", primeBits, seedJavaBytes,
+            gBytes, pBytes, qBytes);
+
+    UniquePtr<unsigned char[]> seedPtr;
+    unsigned long seedSize = 0;
+    if (seedJavaBytes != NULL) {
+        ScopedByteArrayRO seed(env, seedJavaBytes);
+        if (seed.get() == NULL) {
+            return 0;
+        }
+
+        seedSize = seed.size();
+        seedPtr.reset(new unsigned char[seedSize]);
+
+        memcpy(seedPtr.get(), seed.get(), seedSize);
+    }
+
+    Unique_DSA dsa(DSA_new());
+    if (dsa.get() == NULL) {
+        JNI_TRACE("DSA_generate_key failed");
+        jniThrowOutOfMemoryError(env, "Unable to allocate DSA key");
+        return 0;
+    }
+
+    if (gBytes != NULL && pBytes != NULL && qBytes != NULL) {
+        JNI_TRACE("DSA_generate_key parameters specified");
+
+        if (!arrayToBignum(env, gBytes, &dsa->g)) {
+            return 0;
+        }
+
+        if (!arrayToBignum(env, pBytes, &dsa->p)) {
+            return 0;
+        }
+
+        if (!arrayToBignum(env, qBytes, &dsa->q)) {
+            return 0;
+        }
+    } else {
+        JNI_TRACE("DSA_generate_key generating parameters");
+
+        if (!DSA_generate_parameters_ex(dsa.get(), primeBits, seedPtr.get(), seedSize, NULL, NULL, NULL)) {
+            JNI_TRACE("DSA_generate_key => param generation failed");
+            throwExceptionIfNecessary(env, "NativeCrypto_DSA_generate_parameters_ex failed");
+            return 0;
+        }
+    }
+
+    if (!DSA_generate_key(dsa.get())) {
+        JNI_TRACE("DSA_generate_key failed");
+        throwExceptionIfNecessary(env, "NativeCrypto_DSA_generate_key failed");
+        return 0;
+    }
+
+    Unique_EVP_PKEY pkey(EVP_PKEY_new());
+    if (pkey.get() == NULL) {
+        JNI_TRACE("DSA_generate_key failed");
+        jniThrowRuntimeException(env, "NativeCrypto_DSA_generate_key failed");
+        return 0;
+    }
+
+    if (EVP_PKEY_assign_DSA(pkey.get(), dsa.get()) != 1) {
+        JNI_TRACE("DSA_generate_key failed");
+        jniThrowRuntimeException(env, "NativeCrypto_DSA_generate_key failed");
+        return 0;
+    }
+
+    OWNERSHIP_TRANSFERRED(dsa);
+    JNI_TRACE("DSA_generate_key(n=%d, e=%p) => %p", primeBits, seedPtr.get(), pkey.get());
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
+}
+
+/*
+ * public static native byte[][] get_DSA_params(int);
+ */
+static jobjectArray NativeCrypto_get_DSA_params(JNIEnv* env, jclass, jint pkeyRef) {
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("get_DSA_params(%p)", pkey);
+
+    Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
+    if (dsa.get() == NULL) {
+        jniThrowRuntimeException(env, "get_DSA_params failed");
+        return 0;
+    }
+
+    jobjectArray joa = env->NewObjectArray(5, JniConstants::byteArrayClass, NULL);
+    if (joa == NULL) {
+        return NULL;
+    }
+
+    jbyteArray g = bignumToArray(env, dsa->g);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+    env->SetObjectArrayElement(joa, 0, g);
+
+    jbyteArray p = bignumToArray(env, dsa->p);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+    env->SetObjectArrayElement(joa, 1, p);
+
+    jbyteArray q = bignumToArray(env, dsa->q);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+    env->SetObjectArrayElement(joa, 2, q);
+
+    if (dsa->pub_key != NULL) {
+        jbyteArray pub_key = bignumToArray(env, dsa->pub_key);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 3, pub_key);
+    }
+
+    if (dsa->priv_key != NULL) {
+        jbyteArray priv_key = bignumToArray(env, dsa->priv_key);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(joa, 4, priv_key);
+    }
+
+    return joa;
+}
+
+/*
  * public static native void EVP_MD_CTX_destroy(int)
  */
 static void NativeCrypto_EVP_MD_CTX_destroy(JNIEnv*, jclass, EVP_MD_CTX* ctx) {
@@ -632,11 +1279,13 @@
         jniThrowNullPointerException(env, NULL);
         return 0;
     }
+
     EVP_MD_CTX* copy = EVP_MD_CTX_create();
     if (copy == NULL) {
         jniThrowOutOfMemoryError(env, "Unable to allocate copy of EVP_MD_CTX");
         return 0;
     }
+
     EVP_MD_CTX_init(copy);
     int result = EVP_MD_CTX_copy_ex(copy, ctx);
     if (result == 0) {
@@ -644,15 +1293,17 @@
         jniThrowRuntimeException(env, "Unable to copy EVP_MD_CTX");
         return 0;
     }
+
     JNI_TRACE("NativeCrypto_EVP_MD_CTX_copy(%p) => %p", ctx, copy);
-    return (jint) copy;
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(copy));
 }
 
 /*
  * public static native int EVP_DigestFinal(int, byte[], int)
  */
-static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass, EVP_MD_CTX* ctx,
+static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass, jint ctxRef,
                                          jbyteArray hash, jint offset) {
+    EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
     JNI_TRACE("NativeCrypto_EVP_DigestFinal(%p, %p, %d)", ctx, hash, offset);
 
     if (ctx == NULL || hash == NULL) {
@@ -680,7 +1331,8 @@
 /*
  * public static native int EVP_DigestInit(int)
  */
-static int NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass, EVP_MD* evp_md) {
+static jint NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass, jint evpMdRef) {
+    EVP_MD* evp_md = reinterpret_cast<EVP_MD*>(evpMdRef);
     JNI_TRACE("NativeCrypto_EVP_DigestInit(%p)", evp_md);
 
     if (evp_md == NULL) {
@@ -702,7 +1354,7 @@
             return 0;
         }
     }
-    return (jint) ctx.release();
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(ctx.release()));
 }
 
 /*
@@ -729,7 +1381,7 @@
     }
 
     JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => %p", algorithmChars.c_str(), evp_md);
-    return (jint) evp_md;
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(evp_md));
 }
 
 /*
@@ -767,8 +1419,9 @@
 /*
  * public static native void EVP_DigestUpdate(int, byte[], int, int)
  */
-static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass, EVP_MD_CTX* ctx,
+static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass, jint ctxRef,
                                           jbyteArray buffer, jint offset, jint length) {
+    EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
     JNI_TRACE("NativeCrypto_EVP_DigestUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
 
     if (offset < 0 || length < 0) {
@@ -794,6 +1447,103 @@
 }
 
 /*
+ * public static native int EVP_SignInit(java.lang.String)
+ */
+static jint NativeCrypto_EVP_SignInit(JNIEnv* env, jclass, jstring algorithm) {
+    JNI_TRACE("NativeCrypto_EVP_SignInit(%p)", algorithm);
+
+    if (algorithm == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return 0;
+    }
+
+    Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create());
+    if (ctx.get() == NULL) {
+        jniThrowOutOfMemoryError(env, "Unable to allocate EVP_MD_CTX");
+        return 0;
+    }
+    JNI_TRACE("NativeCrypto_EVP_SignInit ctx=%p", ctx.get());
+
+    ScopedUtfChars algorithmChars(env, algorithm);
+    if (algorithmChars.c_str() == NULL) {
+        return 0;
+    }
+    JNI_TRACE("NativeCrypto_EVP_SignInit algorithmChars=%s", algorithmChars.c_str());
+
+    const EVP_MD* digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str()));
+    if (digest == NULL) {
+        jniThrowRuntimeException(env, "Hash algorithm not found");
+        return 0;
+    }
+
+    int ok = EVP_SignInit(ctx.get(), digest);
+    if (ok == 0) {
+        bool exception = throwExceptionIfNecessary(env, "NativeCrypto_EVP_SignInit");
+        if (exception) {
+            return 0;
+        }
+    }
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(ctx.release()));
+}
+
+/*
+ * public static native void EVP_SignUpdate(int, byte[], int, int)
+ */
+static void NativeCrypto_EVP_SignUpdate(JNIEnv* env, jclass, jint ctxRef,
+                                          jbyteArray buffer, jint offset, jint length) {
+    EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
+    JNI_TRACE("NativeCrypto_EVP_SignUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
+
+    if (ctx == NULL || buffer == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    ScopedByteArrayRO bufferBytes(env, buffer);
+    if (bufferBytes.get() == NULL) {
+        return;
+    }
+    int ok = EVP_SignUpdate(ctx,
+                            reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
+                            length);
+    if (ok == 0) {
+        throwExceptionIfNecessary(env, "NativeCrypto_EVP_SignUpdate");
+    }
+}
+
+/*
+ * public static native int EVP_SignFinal(int, byte[], int, int)
+ */
+static jint NativeCrypto_EVP_SignFinal(JNIEnv* env, jclass, jint ctxRef, jbyteArray signature,
+        jint offset, jint pkeyRef) {
+    EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("NativeCrypto_EVP_SignFinal(%p, %p, %d, %p)", ctx, signature, offset, pkey);
+
+    if (ctx == NULL || pkey == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    ScopedByteArrayRW signatureBytes(env, signature);
+    if (signatureBytes.get() == NULL) {
+        return -1;
+    }
+    unsigned int bytesWritten = -1;
+    int ok = EVP_SignFinal(ctx,
+                           reinterpret_cast<unsigned char*>(signatureBytes.get() + offset),
+                           &bytesWritten,
+                           pkey);
+    if (ok == 0) {
+        throwExceptionIfNecessary(env, "NativeCrypto_EVP_SignFinal");
+    }
+    JNI_TRACE("NativeCrypto_EVP_SignFinal(%p, %p, %d, %p) => %u",
+              ctx, signature, offset, pkey, bytesWritten);
+
+    return bytesWritten;
+}
+
+/*
  * public static native int EVP_VerifyInit(java.lang.String)
  */
 static jint NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass, jstring algorithm) {
@@ -830,14 +1580,15 @@
             return 0;
         }
     }
-    return (jint) ctx.release();
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(ctx.release()));
 }
 
 /*
  * public static native void EVP_VerifyUpdate(int, byte[], int, int)
  */
-static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass, EVP_MD_CTX* ctx,
+static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass, jint ctxRef,
                                           jbyteArray buffer, jint offset, jint length) {
+    EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
     JNI_TRACE("NativeCrypto_EVP_VerifyUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
 
     if (ctx == NULL || buffer == NULL) {
@@ -860,8 +1611,10 @@
 /*
  * public static native int EVP_VerifyFinal(int, byte[], int, int, int)
  */
-static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, EVP_MD_CTX* ctx, jbyteArray buffer,
-                                        jint offset, jint length, EVP_PKEY* pkey) {
+static jint NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, jint ctxRef, jbyteArray buffer,
+                                        jint offset, jint length, jint pkeyRef) {
+    EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
     JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p)",
               ctx, buffer, offset, length, pkey);
 
@@ -878,7 +1631,7 @@
                              reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
                              length,
                              pkey);
-    if (ok == 0) {
+    if (ok < 0) {
         throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyFinal");
     }
     JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p) => %d",
@@ -887,8 +1640,173 @@
     return ok;
 }
 
+/*
+ * public static native int EVP_get_cipherbyname(java.lang.String)
+ */
+static jint NativeCrypto_EVP_get_cipherbyname(JNIEnv* env, jclass, jstring algorithm) {
+    JNI_TRACE("EVP_get_cipherbyname(%p)", algorithm);
+    if (algorithm == NULL) {
+        JNI_TRACE("EVP_get_cipherbyname(%p) => threw exception algorithm == null", algorithm);
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    ScopedUtfChars algorithmChars(env, algorithm);
+    if (algorithmChars.c_str() == NULL) {
+        return 0;
+    }
+    JNI_TRACE("EVP_get_cipherbyname(%p) => algorithm = %s", algorithm, algorithmChars.c_str());
+
+    const EVP_CIPHER* evp_cipher = EVP_get_cipherbyname(algorithmChars.c_str());
+    if (evp_cipher == NULL) {
+        jniThrowRuntimeException(env, "Cipher algorithm not found");
+        return 0;
+    }
+
+    JNI_TRACE("EVP_get_cipherbyname(%s) => %p", algorithmChars.c_str(), evp_cipher);
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(evp_cipher));
+}
+
+/*
+ * public static native int EVP_CipherInit_ex(int cipherNid, byte[] key, byte[] iv,
+ *          boolean encrypting);
+ */
+static jint NativeCrypto_EVP_CipherInit_ex(JNIEnv* env, jclass, jint cipherRef, jbyteArray keyArray,
+        jbyteArray ivArray, jboolean encrypting) {
+    const EVP_CIPHER* evp_cipher = reinterpret_cast<const EVP_CIPHER*>(cipherRef);
+    JNI_TRACE("EVP_CipherInit_ex(%p, %p, %p, %d)", evp_cipher, keyArray, ivArray, encrypting ? 1 : 0);
+
+    ScopedByteArrayRO keyBytes(env, keyArray);
+    if (keyBytes.get() == NULL) {
+        return 0;
+    }
+
+    // The IV can be null if we're using ECB.
+    UniquePtr<unsigned char[]> ivPtr;
+    if (ivArray != NULL) {
+        ScopedByteArrayRO ivBytes(env, ivArray);
+        if (ivBytes.get() == NULL) {
+            return 0;
+        }
+
+        ivPtr.reset(new unsigned char[ivBytes.size()]);
+        memcpy(ivPtr.get(), ivBytes.get(), ivBytes.size());
+    }
+
+    Unique_EVP_CIPHER_CTX ctx(EVP_CIPHER_CTX_new());
+    if (ctx.get() == NULL) {
+        jniThrowOutOfMemoryError(env, "Unable to allocate cipher context");
+        JNI_TRACE("ctx=%p EVP_CipherInit_ex => context allocation error", evp_cipher);
+        return 0;
+    }
+
+    const unsigned char* key = reinterpret_cast<const unsigned char*>(keyBytes.get());
+    if (!EVP_CipherInit_ex(ctx.get(), evp_cipher, NULL, key, ivPtr.get(), encrypting ? 1 : 0)) {
+        throwExceptionIfNecessary(env, "EVP_CipherInit_ex");
+        JNI_TRACE("EVP_CipherInit_ex => error initializing cipher");
+        return 0;
+    }
+
+    JNI_TRACE("EVP_CipherInit_ex(%p, %p, %p, %d) => %p", evp_cipher, keyArray, ivArray,
+            encrypting ? 1 : 0, ctx.get());
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(ctx.release()));
+}
+
+/*
+ *  public static native int EVP_CipherUpdate(int ctx, byte[] out, int outOffset, byte[] in,
+ *          int inOffset);
+ */
+static jint NativeCrypto_EVP_CipherUpdate(JNIEnv* env, jclass, jint ctxRef, jbyteArray outArray,
+        jint outOffset, jbyteArray inArray, jint inOffset) {
+    EVP_CIPHER_CTX* ctx = reinterpret_cast<EVP_CIPHER_CTX*>(ctxRef);
+    JNI_TRACE("EVP_CipherUpdate(%p, %p, %d, %p, %d)", ctx, outArray, outOffset, inArray, inOffset);
+
+    if (ctx == NULL) {
+        jniThrowNullPointerException(env, "ctx == null");
+        JNI_TRACE("ctx=%p EVP_CipherUpdate => ctx == null", ctx);
+        return 0;
+    }
+
+    ScopedByteArrayRO inBytes(env, inArray);
+    if (inBytes.get() == NULL) {
+        return 0;
+    }
+    const size_t inSize = inBytes.size();
+    if (size_t(inOffset + EVP_CIPHER_CTX_block_size(ctx)) > inSize) {
+        jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
+        return 0;
+    }
+
+    ScopedByteArrayRW outBytes(env, outArray);
+    if (outBytes.get() == NULL) {
+        return 0;
+    }
+    const size_t outSize = outBytes.size();
+    if (size_t(outOffset + EVP_CIPHER_CTX_block_size(ctx)) > outSize) {
+        jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
+        return 0;
+    }
+
+    unsigned char* out = reinterpret_cast<unsigned char*>(outBytes.get());
+    const unsigned char* in = reinterpret_cast<const unsigned char*>(inBytes.get());
+
+    int outl;
+    if (!EVP_CipherUpdate(ctx, out + outOffset, &outl, in+inOffset, inSize - inOffset)) {
+        throwExceptionIfNecessary(env, "EVP_CipherInit_ex");
+        JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx);
+        return 0;
+    }
+
+    JNI_TRACE("EVP_CipherUpdate(%p, %p, %d, %p, %d) => %d", ctx, outArray, outOffset, inArray,
+            inOffset, outl);
+    return outl;
+}
+
+/*
+ *  public static native int EVP_CipherFinal(int ctx, byte[] out, int outOffset);
+ */
+static jint NativeCrypto_EVP_CipherFinal_ex(JNIEnv* env, jclass, jint ctxRef, jbyteArray outArray,
+        jint outOffset) {
+    EVP_CIPHER_CTX* ctx = reinterpret_cast<EVP_CIPHER_CTX*>(ctxRef);
+    JNI_TRACE("EVP_CipherFinal_ex(%p, %p, %d)", ctx, outArray, outOffset);
+
+    if (ctx == NULL) {
+        jniThrowNullPointerException(env, "ctx == null");
+        JNI_TRACE("ctx=%p EVP_CipherFinal_ex => ctx == null", ctx);
+        return 0;
+    }
+
+    ScopedByteArrayRW outBytes(env, outArray);
+    if (outBytes.get() == NULL) {
+        return 0;
+    }
+
+    unsigned char* out = reinterpret_cast<unsigned char*>(outBytes.get());
+
+    int outl;
+    if (!EVP_CipherFinal_ex(ctx, out + outOffset, &outl)) {
+        throwExceptionIfNecessary(env, "EVP_CipherInit_ex");
+        JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx);
+        return 0;
+    }
+
+    JNI_TRACE("EVP_CipherFinal(%p, %p, %d) => %d", ctx, outArray, outOffset, outl);
+    return outl;
+}
+
+/*
+ * public static native void EVP_CIPHER_CTX_cleanup(int ctx);
+ */
+static void NativeCrypto_EVP_CIPHER_CTX_cleanup(JNIEnv*, jclass, jint ctxRef) {
+    EVP_CIPHER_CTX* ctx = reinterpret_cast<EVP_CIPHER_CTX*>(ctxRef);
+
+    if (ctx != NULL) {
+        EVP_CIPHER_CTX_cleanup(ctx);
+    }
+}
+
 /**
- * Verifies an RSA signature.
+ * public static native void RAND_seed(byte[]);
  */
 static void NativeCrypto_RAND_seed(JNIEnv* env, jclass, jbyteArray seed) {
     JNI_TRACE("NativeCrypto_RAND_seed seed=%p", seed);
@@ -899,7 +1817,7 @@
     RAND_seed(randseed.get(), randseed.size());
 }
 
-static int NativeCrypto_RAND_load_file(JNIEnv* env, jclass, jstring filename, jlong max_bytes) {
+static jint NativeCrypto_RAND_load_file(JNIEnv* env, jclass, jstring filename, jlong max_bytes) {
     JNI_TRACE("NativeCrypto_RAND_load_file filename=%p max_bytes=%lld", filename, max_bytes);
     ScopedUtfChars file(env, filename);
     if (file.c_str() == NULL) {
@@ -1109,6 +2027,11 @@
  *
  * (7) a java.io.FileDescriptor wrapper to check for socket close
  *
+ * We store the NPN protocols list so we can either send it (from the server) or
+ * select a protocol (on the client). We eagerly acquire a pointer to the array
+ * data so the callback doesn't need to acquire resources that it cannot
+ * release.
+ *
  * Because renegotiation can be requested by the peer at any time,
  * care should be taken to maintain an appropriate JNIEnv on any
  * downcall to openssl since it could result in an upcall to Java. The
@@ -1134,6 +2057,9 @@
     JNIEnv* env;
     jobject sslHandshakeCallbacks;
     jobject fileDescriptor;
+    jbyteArray npnProtocolsArray;
+    jbyte* npnProtocolsData;
+    size_t npnProtocolsLength;
     Unique_RSA ephemeralRsa;
     Unique_EC_KEY ephemeralEc;
 
@@ -1172,6 +2098,9 @@
             waitingThreads(0),
             env(NULL),
             sslHandshakeCallbacks(NULL),
+            npnProtocolsArray(NULL),
+            npnProtocolsData(NULL),
+            npnProtocolsLength(-1),
             ephemeralRsa(NULL),
             ephemeralEc(NULL) {
         fdsEmergency[0] = -1;
@@ -1187,8 +2116,11 @@
      * @param env The JNIEnv
      * @param shc The SSLHandshakeCallbacks
      * @param fd The FileDescriptor
+     * @param npnProtocols NPN protocols so that they may be advertised (by the
+     *                     server) or selected (by the client). Has no effect
+     *                     unless NPN is enabled.
      */
-    bool setCallbackState(JNIEnv* e, jobject shc, jobject fd) {
+    bool setCallbackState(JNIEnv* e, jobject shc, jobject fd, jbyteArray npnProtocols) {
         NetFd netFd(e, fd);
         if (netFd.isClosed()) {
             return false;
@@ -1196,13 +2128,27 @@
         env = e;
         sslHandshakeCallbacks = shc;
         fileDescriptor = fd;
+        if (npnProtocols != NULL) {
+            npnProtocolsArray = npnProtocols;
+            npnProtocolsLength = e->GetArrayLength(npnProtocols);
+            npnProtocolsData = e->GetByteArrayElements(npnProtocols, NULL);
+            if (npnProtocolsData == NULL) {
+                return false;
+            }
+        }
         return true;
     }
 
     void clearCallbackState() {
-        env = NULL;
         sslHandshakeCallbacks = NULL;
         fileDescriptor = NULL;
+        if (npnProtocolsArray != NULL) {
+            env->ReleaseByteArrayElements(npnProtocolsArray, npnProtocolsData, JNI_ABORT);
+            npnProtocolsArray = NULL;
+            npnProtocolsData = NULL;
+            npnProtocolsLength = -1;
+        }
+        env = NULL;
     }
 
 };
@@ -1221,7 +2167,7 @@
  * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
  * @param fdObject The FileDescriptor, since appData->fileDescriptor should be NULL
  * @param appData The application data structure with mutex info etc.
- * @param timeout The timeout value for select call, with the special value
+ * @param timeout_millis The timeout value for select call, with the special value
  *                0 meaning no timeout at all (wait indefinitely). Note: This is
  *                the Java semantics of the timeout value, not the usual
  *                select() semantics.
@@ -1229,7 +2175,7 @@
  * THROW_SOCKETEXCEPTION if a SocketException was thrown, -1 on
  * additional errors
  */
-static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData, int timeout) {
+static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData, int timeout_millis) {
     // This loop is an expanded version of the NET_FAILURE_RETRY
     // macro. It cannot simply be used in this case because select
     // cannot be restarted without recreating the fd_sets and timeout
@@ -1244,8 +2190,8 @@
             break;
         }
         int intFd = fd.get();
-        JNI_TRACE("sslSelect type=%s fd=%d appData=%p timeout=%d",
-                  (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE", intFd, appData, timeout);
+        JNI_TRACE("sslSelect type=%s fd=%d appData=%p timeout_millis=%d",
+                  (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE", intFd, appData, timeout_millis);
 
         FD_ZERO(&rfds);
         FD_ZERO(&wfds);
@@ -1263,9 +2209,9 @@
         // Build a struct for the timeout data if we actually want a timeout.
         timeval tv;
         timeval* ptv;
-        if (timeout > 0) {
-            tv.tv_sec = timeout / 1000;
-            tv.tv_usec = 0;
+        if (timeout_millis > 0) {
+            tv.tv_sec = timeout_millis / 1000;
+            tv.tv_usec = (timeout_millis % 1000) * 1000;
             ptv = &tv;
         } else {
             ptv = NULL;
@@ -1273,9 +2219,9 @@
 
         AsynchronousSocketCloseMonitor monitor(intFd);
         result = select(maxFd + 1, &rfds, &wfds, NULL, ptv);
-        JNI_TRACE("sslSelect %s fd=%d appData=%p timeout=%d => %d",
+        JNI_TRACE("sslSelect %s fd=%d appData=%p timeout_millis=%d => %d",
                   (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE",
-                  fd.get(), appData, timeout, result);
+                  fd.get(), appData, timeout_millis, result);
         if (result == -1) {
             if (fd.isClosed()) {
                 result = THROWN_EXCEPTION;
@@ -1353,7 +2299,7 @@
     AppData* appData = toAppData(ssl);
     JNIEnv* env = appData->env;
     if (env == NULL) {
-        LOGE("AppData->env missing in cert_verify_callback");
+        ALOGE("AppData->env missing in cert_verify_callback");
         JNI_TRACE("ssl=%p cert_verify_callback => 0", ssl);
         return 0;
     }
@@ -1394,7 +2340,7 @@
     AppData* appData = toAppData(ssl);
     JNIEnv* env = appData->env;
     if (env == NULL) {
-        LOGE("AppData->env missing in info_callback");
+        ALOGE("AppData->env missing in info_callback");
         JNI_TRACE("ssl=%p info_callback env error", ssl);
         return;
     }
@@ -1426,12 +2372,12 @@
     AppData* appData = toAppData(ssl);
     JNIEnv* env = appData->env;
     if (env == NULL) {
-        LOGE("AppData->env missing in client_cert_cb");
+        ALOGE("AppData->env missing in client_cert_cb");
         JNI_TRACE("ssl=%p client_cert_cb env error => 0", ssl);
         return 0;
     }
     if (env->ExceptionCheck()) {
-        JNI_TRACE("ssl=%p client_cert_cb already pending exception", ssl);
+        JNI_TRACE("ssl=%p client_cert_cb already pending exception => 0", ssl);
         return 0;
     }
     jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
@@ -1452,6 +2398,8 @@
             break;
         case SSL3_VERSION:
         case TLS1_VERSION:
+        case TLS1_1_VERSION:
+        case TLS1_2_VERSION:
         case DTLS1_VERSION:
             ctype = ssl->s3->tmp.ctype;
             ctype_num = ssl->s3->tmp.ctype_num;
@@ -1640,10 +2588,6 @@
     mode |= SSL_MODE_SMALL_BUFFERS;  /* lazily allocate record buffers; usually saves
                                       * 44k over the default */
 #endif
-#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
-    mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;  /* enable sending of client data as soon as
-                                             * ClientCCS and ClientFinished are sent */
-#endif
     SSL_CTX_set_mode(sslCtx.get(), mode);
 
     SSL_CTX_set_cert_verify_callback(sslCtx.get(), cert_verify_callback, NULL);
@@ -1671,6 +2615,37 @@
     SSL_CTX_free(ssl_ctx);
 }
 
+static void NativeCrypto_SSL_CTX_set_session_id_context(JNIEnv* env, jclass,
+                                                        jint ssl_ctx_address, jbyteArray sid_ctx)
+{
+    SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+    JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context sid_ctx=%p", ssl_ctx, sid_ctx);
+    if (ssl_ctx == NULL) {
+        return;
+    }
+
+    ScopedByteArrayRO buf(env, sid_ctx);
+    if (buf.get() == NULL) {
+        JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context => threw exception", ssl_ctx);
+        return;
+    }
+
+    unsigned int length = buf.size();
+    if (length > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "length > SSL_MAX_SSL_SESSION_ID_LENGTH");
+        JNI_TRACE("NativeCrypto_SSL_CTX_set_session_id_context => length = %d", length);
+        return;
+    }
+    const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buf.get());
+    int result = SSL_CTX_set_session_id_context(ssl_ctx, bytes, length);
+    if (result == 0) {
+        throwExceptionIfNecessary(env, "NativeCrypto_SSL_CTX_set_session_id_context");
+        return;
+    }
+    JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context => ok", ssl_ctx);
+}
+
 /**
  * public static native int SSL_new(int ssl_ctx) throws SSLException;
  */
@@ -1703,6 +2678,30 @@
     return (jint) ssl.release();
 }
 
+static void NativeCrypto_SSL_use_OpenSSL_PrivateKey(JNIEnv* env, jclass, jint ssl_address, jint pkeyRef) {
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+    JNI_TRACE("ssl=%p SSL_use_OpenSSL_PrivateKey privatekey=%p", ssl, pkey);
+    if (ssl == NULL) {
+        return;
+    }
+
+    if (pkey == NULL) {
+        return;
+    }
+
+    int ret = SSL_use_PrivateKey(ssl, pkey);
+    if (ret != 1) {
+        ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+        throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting private key");
+        SSL_clear(ssl);
+        JNI_TRACE("ssl=%p SSL_use_OpenSSL_PrivateKey => error", ssl);
+        return;
+    }
+
+    JNI_TRACE("ssl=%p SSL_use_OpenSSL_PrivateKey => ok", ssl);
+}
+
 static void NativeCrypto_SSL_use_PrivateKey(JNIEnv* env, jclass,
                                             jint ssl_address, jbyteArray privatekey)
 {
@@ -1720,7 +2719,7 @@
     const unsigned char* tmp = reinterpret_cast<const unsigned char*>(buf.get());
     Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(NULL, &tmp, buf.size()));
     if (pkcs8.get() == NULL) {
-        LOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+        ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
         throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE,
                                        "Error parsing private key from DER to PKCS8");
         SSL_clear(ssl);
@@ -1730,7 +2729,7 @@
 
     Unique_EVP_PKEY privatekeyevp(EVP_PKCS82PKEY(pkcs8.get()));
     if (privatekeyevp.get() == NULL) {
-        LOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+        ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
         throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE,
                                        "Error creating private key from PKCS8");
         SSL_clear(ssl);
@@ -1744,7 +2743,7 @@
     if (ret == 1) {
         OWNERSHIP_TRANSFERRED(privatekeyevp);
     } else {
-        LOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+        ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
         throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting private key");
         SSL_clear(ssl);
         JNI_TRACE("ssl=%p NativeCrypto_SSL_use_PrivateKey => error", ssl);
@@ -1795,7 +2794,7 @@
         certificatesX509[i].reset(d2i_X509(NULL, &tmp, buf.size()));
 
         if (certificatesX509[i].get() == NULL) {
-            LOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+            ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
             throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error parsing certificate");
             SSL_clear(ssl);
             JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificates parsing error", ssl);
@@ -1807,7 +2806,7 @@
     if (ret == 1) {
         OWNERSHIP_TRANSFERRED(certificatesX509[0]);
     } else {
-        LOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+        ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
         throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting certificate");
         SSL_clear(ssl);
         JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => SSL_use_certificate error", ssl);
@@ -1903,7 +2902,7 @@
         Unique_X509_NAME principalX509Name(d2i_X509_NAME(NULL, &tmp, buf.size()));
 
         if (principalX509Name.get() == NULL) {
-            LOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
+            ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
             throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error parsing principal");
             SSL_clear(ssl);
             JNI_TRACE("ssl=%p NativeCrypto_SSL_set_client_CA_list => principals parsing error",
@@ -2171,14 +3170,103 @@
 }
 
 /**
- * Perform SSL handshake
+ * Callback for the client to select a protocol.
  */
-static jint NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass,
-    jint ssl_address, jobject fdObject, jobject shc, jint timeout, jboolean client_mode)
+static int next_proto_select_callback(SSL* ssl, unsigned char **out, unsigned char *outlen,
+        const unsigned char *in, unsigned int inlen, void *)
+{
+    JNI_TRACE("ssl=%p next_proto_select_callback", ssl);
+
+    // Enable False Start on the client if the server understands NPN
+    // http://www.imperialviolet.org/2012/04/11/falsestart.html
+    SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
+
+    AppData* appData = toAppData(ssl);
+    unsigned char* npnProtocols = reinterpret_cast<unsigned char*>(appData->npnProtocolsData);
+    size_t npnProtocolsLength = appData->npnProtocolsLength;
+
+    int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtocols, npnProtocolsLength);
+    switch (status) {
+      case OPENSSL_NPN_NEGOTIATED:
+        JNI_TRACE("ssl=%p next_proto_select_callback NPN negotiated", ssl);
+        break;
+      case OPENSSL_NPN_UNSUPPORTED:
+        JNI_TRACE("ssl=%p next_proto_select_callback NPN unsupported", ssl);
+        break;
+      case OPENSSL_NPN_NO_OVERLAP:
+        JNI_TRACE("ssl=%p next_proto_select_callback NPN no overlap", ssl);
+        break;
+    }
+    return SSL_TLSEXT_ERR_OK;
+}
+
+/**
+ * Callback for the server to advertise available protocols.
+ */
+static int next_protos_advertised_callback(SSL* ssl,
+        const unsigned char **out, unsigned int *outlen, void *)
+{
+    JNI_TRACE("ssl=%p next_protos_advertised_callback", ssl);
+    AppData* appData = toAppData(ssl);
+    unsigned char* npnProtocols = reinterpret_cast<unsigned char*>(appData->npnProtocolsData);
+    if (npnProtocols != NULL) {
+        *out = npnProtocols;
+        *outlen = appData->npnProtocolsLength;
+    }
+    return SSL_TLSEXT_ERR_OK;
+}
+
+static void NativeCrypto_SSL_CTX_enable_npn(JNIEnv* env, jclass, jint ssl_ctx_address)
+{
+    SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+    if (ssl_ctx == NULL) {
+        return;
+    }
+    SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_callback, NULL); // client
+    SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_protos_advertised_callback, NULL); // server
+}
+
+static void NativeCrypto_SSL_CTX_disable_npn(JNIEnv* env, jclass, jint ssl_ctx_address)
+{
+    SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+    if (ssl_ctx == NULL) {
+        return;
+    }
+    SSL_CTX_set_next_proto_select_cb(ssl_ctx, NULL, NULL); // client
+    SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, NULL, NULL); // server
+}
+
+static jbyteArray NativeCrypto_SSL_get_npn_negotiated_protocol(JNIEnv* env, jclass,
+        jint ssl_address)
 {
     SSL* ssl = to_SSL(env, ssl_address, true);
-    JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd=%p shc=%p timeout=%d client_mode=%d",
-              ssl, fdObject, shc, timeout, client_mode);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_get_npn_negotiated_protocol", ssl);
+    if (ssl == NULL) {
+        return NULL;
+    }
+    const jbyte* npn;
+    unsigned npnLength;
+    SSL_get0_next_proto_negotiated(ssl, reinterpret_cast<const unsigned char**>(&npn), &npnLength);
+    if (npnLength == 0) {
+        return NULL;
+    }
+    jbyteArray result = env->NewByteArray(npnLength);
+    if (result != NULL) {
+        env->SetByteArrayRegion(result, 0, npnLength, npn);
+    }
+    return result;
+}
+
+/**
+ * Perform SSL handshake
+ */
+static jint NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jint ssl_address,
+        jobject fdObject, jobject shc, jint timeout_millis, jboolean client_mode,
+        jbyteArray npnProtocols)
+{
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd=%p shc=%p timeout_millis=%d client_mode=%d npn=%p",
+              ssl, fdObject, shc, timeout_millis, client_mode, npnProtocols);
     if (ssl == NULL) {
       return 0;
     }
@@ -2233,6 +3321,7 @@
         JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake appData => 0", ssl);
         return 0;
     }
+
     SSL_set_app_data(ssl, reinterpret_cast<char*>(appData));
     JNI_TRACE("ssl=%p AppData::create => %p", ssl, appData);
 
@@ -2246,7 +3335,7 @@
     while (appData->aliveAndKicking) {
         errno = 0;
 
-        if (!appData->setCallbackState(env, shc, fdObject)) {
+        if (!appData->setCallbackState(env, shc, fdObject, npnProtocols)) {
             // SocketException thrown by NetFd.isClosed
             SSL_clear(ssl);
             JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setCallbackState => 0", ssl);
@@ -2270,8 +3359,8 @@
         }
         // error case
         int sslError = SSL_get_error(ssl, ret);
-        JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake ret=%d errno=%d sslError=%d timeout=%d",
-                  ssl, ret, errno, sslError, timeout);
+        JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake ret=%d errno=%d sslError=%d timeout_millis=%d",
+                  ssl, ret, errno, sslError, timeout_millis);
 
         /*
          * If SSL_do_handshake doesn't succeed due to the socket being
@@ -2283,7 +3372,7 @@
          */
         if (sslError == SSL_ERROR_WANT_READ || sslError == SSL_ERROR_WANT_WRITE) {
             appData->waitingThreads++;
-            int selectResult = sslSelect(env, sslError, fdObject, appData, timeout);
+            int selectResult = sslSelect(env, sslError, fdObject, appData, timeout_millis);
 
             if (selectResult == THROWN_EXCEPTION) {
                 // SocketException thrown by NetFd.isClosed
@@ -2305,7 +3394,7 @@
                 return 0;
             }
         } else {
-            // LOGE("Unknown error %d during handshake", error);
+            // ALOGE("Unknown error %d during handshake", error);
             break;
         }
     }
@@ -2462,7 +3551,7 @@
  * cleanly shut down, or THROW_SSLEXCEPTION if an exception should be thrown.
  */
 static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* buf, jint len,
-                   int* sslReturnCode, int* sslErrorCode, int timeout) {
+                   int* sslReturnCode, int* sslErrorCode, int timeout_millis) {
     JNI_TRACE("ssl=%p sslRead buf=%p len=%d", ssl, buf, len);
 
     if (len == 0) {
@@ -2486,7 +3575,7 @@
 
         unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
 
-        if (!appData->setCallbackState(env, shc, fdObject)) {
+        if (!appData->setCallbackState(env, shc, fdObject, NULL)) {
             MUTEX_UNLOCK(appData->mutex);
             return THROWN_EXCEPTION;
         }
@@ -2541,7 +3630,7 @@
             // Need to wait for availability of underlying layer, then retry.
             case SSL_ERROR_WANT_READ:
             case SSL_ERROR_WANT_WRITE: {
-                int selectResult = sslSelect(env, sslError, fdObject, appData, timeout);
+                int selectResult = sslSelect(env, sslError, fdObject, appData, timeout_millis);
                 if (selectResult == THROWN_EXCEPTION) {
                     return THROWN_EXCEPTION;
                 }
@@ -2592,11 +3681,12 @@
  * Returns 1 (success) or value <= 0 (failure).
  */
 static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jobject fdObject,
-                                  jobject shc, jbyteArray b, jint offset, jint len, jint timeout)
+                                  jobject shc, jbyteArray b, jint offset, jint len,
+                                  jint timeout_millis)
 {
     SSL* ssl = to_SSL(env, ssl_address, true);
-    JNI_TRACE("ssl=%p NativeCrypto_SSL_read fd=%p shc=%p b=%p offset=%d len=%d timeout=%d",
-              ssl, fdObject, shc, b, offset, len, timeout);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_read fd=%p shc=%p b=%p offset=%d len=%d timeout_millis=%d",
+              ssl, fdObject, shc, b, offset, len, timeout_millis);
     if (ssl == NULL) {
         return 0;
     }
@@ -2620,7 +3710,7 @@
     int sslErrorCode = SSL_ERROR_NONE;;
 
     int ret = sslRead(env, ssl, fdObject, shc, reinterpret_cast<char*>(bytes.get() + offset), len,
-                      &returnCode, &sslErrorCode, timeout);
+                      &returnCode, &sslErrorCode, timeout_millis);
 
     int result;
     switch (ret) {
@@ -2686,8 +3776,8 @@
 
         unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
 
-        // LOGD("Doing SSL_write() with %d bytes to go", len);
-        if (!appData->setCallbackState(env, shc, fdObject)) {
+        // ALOGD("Doing SSL_write() with %d bytes to go", len);
+        if (!appData->setCallbackState(env, shc, fdObject, NULL)) {
             MUTEX_UNLOCK(appData->mutex);
             return THROWN_EXCEPTION;
         }
@@ -2890,7 +3980,7 @@
 
     AppData* appData = toAppData(ssl);
     if (appData != NULL) {
-        if (!appData->setCallbackState(env, shc, fdObject)) {
+        if (!appData->setCallbackState(env, shc, fdObject, NULL)) {
             // SocketException thrown by NetFd.isClosed
             SSL_clear(ssl);
             freeOpenSslErrorState();
@@ -3138,9 +4228,26 @@
 #define SSL_CALLBACKS "Lorg/apache/harmony/xnet/provider/jsse/NativeCrypto$SSLHandshakeCallbacks;"
 static JNINativeMethod sNativeCryptoMethods[] = {
     NATIVE_METHOD(NativeCrypto, clinit, "()V"),
+    NATIVE_METHOD(NativeCrypto, ENGINE_load_dynamic, "()V"),
+    NATIVE_METHOD(NativeCrypto, ENGINE_by_id, "(Ljava/lang/String;)I"),
+    NATIVE_METHOD(NativeCrypto, ENGINE_init, "(I)I"),
+    NATIVE_METHOD(NativeCrypto, ENGINE_finish, "(I)I"),
+    NATIVE_METHOD(NativeCrypto, ENGINE_free, "(I)I"),
+    NATIVE_METHOD(NativeCrypto, ENGINE_load_private_key, "(ILjava/lang/String;)I"),
     NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_DSA, "([B[B[B[B[B)I"),
-    NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_RSA, "([B[B[B[B[B)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_RSA, "([B[B[B[B[B[B[B[B)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_PKEY_type, "(I)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_PKEY_size, "(I)I"),
     NATIVE_METHOD(NativeCrypto, EVP_PKEY_free, "(I)V"),
+    NATIVE_METHOD(NativeCrypto, i2d_PKCS8_PRIV_KEY_INFO, "(I)[B"),
+    NATIVE_METHOD(NativeCrypto, d2i_PKCS8_PRIV_KEY_INFO, "([B)I"),
+    NATIVE_METHOD(NativeCrypto, i2d_PUBKEY, "(I)[B"),
+    NATIVE_METHOD(NativeCrypto, d2i_PUBKEY, "([B)I"),
+    NATIVE_METHOD(NativeCrypto, RSA_generate_key_ex, "(I[B)I"),
+    NATIVE_METHOD(NativeCrypto, get_RSA_private_params, "(I)[[B"),
+    NATIVE_METHOD(NativeCrypto, get_RSA_public_params, "(I)[[B"),
+    NATIVE_METHOD(NativeCrypto, DSA_generate_key, "(I[B[B[B[B)I"),
+    NATIVE_METHOD(NativeCrypto, get_DSA_params, "(I)[[B"),
     NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_destroy, "(I)V"),
     NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_copy, "(I)I"),
     NATIVE_METHOD(NativeCrypto, EVP_DigestFinal, "(I[BI)I"),
@@ -3149,14 +4256,24 @@
     NATIVE_METHOD(NativeCrypto, EVP_MD_block_size, "(I)I"),
     NATIVE_METHOD(NativeCrypto, EVP_MD_size, "(I)I"),
     NATIVE_METHOD(NativeCrypto, EVP_DigestUpdate, "(I[BII)V"),
+    NATIVE_METHOD(NativeCrypto, EVP_SignInit, "(Ljava/lang/String;)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_SignUpdate, "(I[BII)V"),
+    NATIVE_METHOD(NativeCrypto, EVP_SignFinal, "(I[BII)I"),
     NATIVE_METHOD(NativeCrypto, EVP_VerifyInit, "(Ljava/lang/String;)I"),
     NATIVE_METHOD(NativeCrypto, EVP_VerifyUpdate, "(I[BII)V"),
     NATIVE_METHOD(NativeCrypto, EVP_VerifyFinal, "(I[BIII)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_get_cipherbyname, "(Ljava/lang/String;)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_CipherInit_ex, "(I[B[BZ)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_CipherUpdate, "(I[BI[BI)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_CipherFinal_ex, "(I[BI)I"),
+    NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_cleanup, "(I)V"),
     NATIVE_METHOD(NativeCrypto, RAND_seed, "([B)V"),
     NATIVE_METHOD(NativeCrypto, RAND_load_file, "(Ljava/lang/String;J)I"),
     NATIVE_METHOD(NativeCrypto, SSL_CTX_new, "()I"),
     NATIVE_METHOD(NativeCrypto, SSL_CTX_free, "(I)V"),
+    NATIVE_METHOD(NativeCrypto, SSL_CTX_set_session_id_context, "(I[B)V"),
     NATIVE_METHOD(NativeCrypto, SSL_new, "(I)I"),
+    NATIVE_METHOD(NativeCrypto, SSL_use_OpenSSL_PrivateKey, "(II)V"),
     NATIVE_METHOD(NativeCrypto, SSL_use_PrivateKey, "(I[B)V"),
     NATIVE_METHOD(NativeCrypto, SSL_use_certificate, "(I[[B)V"),
     NATIVE_METHOD(NativeCrypto, SSL_check_private_key, "(I)V"),
@@ -3173,7 +4290,7 @@
     NATIVE_METHOD(NativeCrypto, SSL_set_session_creation_enabled, "(IZ)V"),
     NATIVE_METHOD(NativeCrypto, SSL_set_tlsext_host_name, "(ILjava/lang/String;)V"),
     NATIVE_METHOD(NativeCrypto, SSL_get_servername, "(I)Ljava/lang/String;"),
-    NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(I" FILE_DESCRIPTOR SSL_CALLBACKS "IZ)I"),
+    NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(I" FILE_DESCRIPTOR SSL_CALLBACKS "IZ[B)I"),
     NATIVE_METHOD(NativeCrypto, SSL_renegotiate, "(I)V"),
     NATIVE_METHOD(NativeCrypto, SSL_get_certificate, "(I)[[B"),
     NATIVE_METHOD(NativeCrypto, SSL_get_peer_cert_chain, "(I)[[B"),
@@ -3190,13 +4307,13 @@
     NATIVE_METHOD(NativeCrypto, SSL_SESSION_free, "(I)V"),
     NATIVE_METHOD(NativeCrypto, i2d_SSL_SESSION, "(I)[B"),
     NATIVE_METHOD(NativeCrypto, d2i_SSL_SESSION, "([B)I"),
+    NATIVE_METHOD(NativeCrypto, SSL_CTX_enable_npn, "(I)V"),
+    NATIVE_METHOD(NativeCrypto, SSL_CTX_disable_npn, "(I)V"),
+    NATIVE_METHOD(NativeCrypto, SSL_get_npn_negotiated_protocol, "(I)[B"),
 };
 
-int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
+void register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
     JNI_TRACE("register_org_apache_harmony_xnet_provider_jsse_NativeCrypto");
     // Register org.apache.harmony.xnet.provider.jsse.NativeCrypto methods
-    return jniRegisterNativeMethods(env,
-                                    "org/apache/harmony/xnet/provider/jsse/NativeCrypto",
-                                    sNativeCryptoMethods,
-                                    NELEM(sNativeCryptoMethods));
+    jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/NativeCrypto", sNativeCryptoMethods, NELEM(sNativeCryptoMethods));
 }
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 9535ce6..b67348b 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -58,19 +58,9 @@
 	external/openssl/include \
 	external/zlib
 
-# Any shared/static libs that are listed here must also
-# be listed in libs/nativehelper/Android.mk.
-# TODO: fix this requirement
-
 LOCAL_SHARED_LIBRARIES += \
-	libcrypto \
-	libcutils \
-	libexpat \
-	libicuuc \
-	libicui18n \
-	libssl \
-	libutils \
-	libz
+	liblog \
+	libnativehelper
 
 LOCAL_STATIC_LIBRARIES += \
 	libfdlibm
diff --git a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
new file mode 100644
index 0000000..85e360e
--- /dev/null
+++ b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 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 com.android.org.bouncycastle.jce.provider;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Set;
+import junit.framework.TestCase;
+import com.android.org.bouncycastle.jce.provider.CertBlacklist;
+import com.android.org.bouncycastle.util.encoders.Hex;
+
+public class CertBlacklistTest extends TestCase {
+
+    private File tmpFile;
+
+    private Set<String> DEFAULT_PUBKEYS;
+    private Set<String> DEFAULT_SERIALS;
+
+    public CertBlacklistTest() throws IOException {
+        tmpFile = File.createTempFile("test", "");
+        DEFAULT_PUBKEYS = getDefaultPubkeys();
+        DEFAULT_SERIALS = getDefaultSerials();
+        tmpFile.delete();
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        tmpFile = File.createTempFile("test", "");
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            tmpFile.delete();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private Set<String> getPubkeyBlacklist(String path) throws IOException {
+        // set our blacklist path
+        CertBlacklist bl = new CertBlacklist(path, CertBlacklist.DEFAULT_SERIAL_BLACKLIST_PATH);
+        // call readPubkeyBlacklist
+        Set<byte[]> arr = bl.pubkeyBlacklist;
+        // convert the results to a hashset of strings
+        Set<String> results = new HashSet<String>();
+        for (byte[] value: arr) {
+            results.add(new String(Hex.encode(value)));
+        }
+        return results;
+    }
+
+    private Set<String> getSerialBlacklist(String path) throws IOException {
+        // set our blacklist path
+        CertBlacklist bl = new CertBlacklist(CertBlacklist.DEFAULT_PUBKEY_BLACKLIST_PATH, path);
+        // call readPubkeyBlacklist
+        Set<BigInteger> arr = bl.serialBlacklist;
+        // convert the results to a hashset of strings
+        Set<String> results = new HashSet<String>();
+        for (BigInteger value: arr) {
+            results.add(value.toString(16));
+        }
+        return results;
+    }
+
+    private Set<String> getDefaultPubkeys() throws IOException {
+        return getPubkeyBlacklist("");
+    }
+
+    private Set<String> getDefaultSerials() throws IOException {
+        return getSerialBlacklist("");
+    }
+
+    private Set<String> getCurrentPubkeyBlacklist() throws IOException {
+        return getPubkeyBlacklist(tmpFile.getCanonicalPath());
+    }
+
+    private Set<String> getCurrentSerialBlacklist() throws IOException {
+        return getSerialBlacklist(tmpFile.getCanonicalPath());
+    }
+
+    private void blacklistToFile(String blacklist) throws IOException {
+        FileOutputStream out = new FileOutputStream(tmpFile);
+        out.write(blacklist.toString().getBytes());
+        out.close();
+    }
+
+    private void writeBlacklist(HashSet<String> values) throws IOException {
+        StringBuilder result = new StringBuilder();
+        // join the values into a string
+        for (String value : values) {
+            if (result.length() != 0) {
+                result.append(",");
+            }
+            result.append(value);
+        }
+        blacklistToFile(result.toString());
+    }
+
+    public void testPubkeyBlacklistLegit() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default pubkeys into the bl
+        bl.addAll(DEFAULT_PUBKEYS);
+        // do the test
+        assertEquals(bl, getCurrentPubkeyBlacklist());
+    }
+
+    public void testSerialBlacklistLegit() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("22e514121e61c643b1e9b06bd4b9f7d0");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default serials into the bl
+        bl.addAll(DEFAULT_SERIALS);
+        // do the test
+        assertEquals(bl, getCurrentSerialBlacklist());
+    }
+
+    public void testPubkeyBlacklistMultipleLegit() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc");
+        bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccd");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default pubkeys into the bl
+        bl.addAll(DEFAULT_PUBKEYS);
+        // do the test
+        assertEquals(bl, getCurrentPubkeyBlacklist());
+    }
+
+    public void testSerialBlacklistMultipleLegit() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("22e514121e61c643b1e9b06bd4b9f7d0");
+        bl.add("22e514121e61c643b1e9b06bd4b9f7d1");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default serials into the bl
+        bl.addAll(DEFAULT_SERIALS);
+        // do the test
+        assertEquals(bl, getCurrentSerialBlacklist());
+    }
+
+    public void testPubkeyBlacklistMultipleBad() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc");
+        bl.add("");
+        bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccd");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default pubkeys into the bl
+        bl.addAll(DEFAULT_PUBKEYS);
+        // remove the bad one
+        bl.remove("");
+        // do the test- results should be all but the bad one are handled
+        assertEquals(bl, getCurrentPubkeyBlacklist());
+    }
+
+    public void testSerialBlacklistMultipleBad() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("22e514121e61c643b1e9b06bd4b9f7d0");
+        bl.add("");
+        bl.add("22e514121e61c643b1e9b06bd4b9f7d1");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default serials into the bl
+        bl.addAll(DEFAULT_SERIALS);
+        // remove the bad one
+        bl.remove("");
+        // do the test- results should be all but the bad one are handled
+        assertEquals(bl, getCurrentSerialBlacklist());
+    }
+
+    public void testPubkeyBlacklistDoesntExist() throws IOException {
+        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlacklist());
+    }
+
+    public void testSerialBlacklistDoesntExist() throws IOException {
+        assertEquals(DEFAULT_SERIALS, getCurrentSerialBlacklist());
+    }
+
+    public void testPubkeyBlacklistNotHexValues() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
+        // write the blacklist
+        writeBlacklist(bl);
+        // do the test
+        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlacklist());
+    }
+
+    public void testSerialBlacklistNotHexValues() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
+        // write the blacklist
+        writeBlacklist(bl);
+        // do the test
+        assertEquals(DEFAULT_SERIALS, getCurrentSerialBlacklist());
+    }
+
+    public void testPubkeyBlacklistIncorrectLength() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091cc");
+        // write the blacklist
+        writeBlacklist(bl);
+        // do the test
+        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlacklist());
+    }
+
+    public void testSerialBlacklistZero() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("0");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default serials
+        bl.addAll(DEFAULT_SERIALS);
+        // do the test
+        assertEquals(bl, getCurrentSerialBlacklist());
+    }
+
+    public void testSerialBlacklistNegative() throws IOException {
+        // build the blacklist
+        HashSet<String> bl = new HashSet<String>();
+        bl.add("-1");
+        // write the blacklist
+        writeBlacklist(bl);
+        // add the default serials
+        bl.addAll(DEFAULT_SERIALS);
+        // do the test
+        assertEquals(bl, getCurrentSerialBlacklist());
+    }
+}
diff --git a/luni/src/test/java/libcore/io/DiskLruCacheTest.java b/luni/src/test/java/libcore/io/DiskLruCacheTest.java
index 808918d..03a6932 100644
--- a/luni/src/test/java/libcore/io/DiskLruCacheTest.java
+++ b/luni/src/test/java/libcore/io/DiskLruCacheTest.java
@@ -602,6 +602,59 @@
         assertAbsent("A");
     }
 
+    public void testEditSameVersion() throws Exception {
+        set("A", "a", "a");
+        DiskLruCache.Snapshot snapshot = cache.get("A");
+        DiskLruCache.Editor editor = snapshot.edit();
+        editor.set(1, "a2");
+        editor.commit();
+        assertValue("A", "a", "a2");
+    }
+
+    public void testEditSnapshotAfterChangeAborted() throws Exception {
+        set("A", "a", "a");
+        DiskLruCache.Snapshot snapshot = cache.get("A");
+        DiskLruCache.Editor toAbort = snapshot.edit();
+        toAbort.set(0, "b");
+        toAbort.abort();
+        DiskLruCache.Editor editor = snapshot.edit();
+        editor.set(1, "a2");
+        editor.commit();
+        assertValue("A", "a", "a2");
+    }
+
+    public void testEditSnapshotAfterChangeCommitted() throws Exception {
+        set("A", "a", "a");
+        DiskLruCache.Snapshot snapshot = cache.get("A");
+        DiskLruCache.Editor toAbort = snapshot.edit();
+        toAbort.set(0, "b");
+        toAbort.commit();
+        assertNull(snapshot.edit());
+    }
+
+    public void testEditSinceEvicted() throws Exception {
+        cache.close();
+        cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+        set("A", "aa", "aaa"); // size 5
+        DiskLruCache.Snapshot snapshot = cache.get("A");
+        set("B", "bb", "bbb"); // size 5
+        set("C", "cc", "ccc"); // size 5; will evict 'A'
+        cache.flush();
+        assertNull(snapshot.edit());
+    }
+
+    public void testEditSinceEvictedAndRecreated() throws Exception {
+        cache.close();
+        cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+        set("A", "aa", "aaa"); // size 5
+        DiskLruCache.Snapshot snapshot = cache.get("A");
+        set("B", "bb", "bbb"); // size 5
+        set("C", "cc", "ccc"); // size 5; will evict 'A'
+        set("A", "a", "aaaa"); // size 5; will evict 'B'
+        cache.flush();
+        assertNull(snapshot.edit());
+    }
+
     private void assertJournalEquals(String... expectedBodyLines) throws Exception {
         List<String> expectedLines = new ArrayList<String>();
         expectedLines.add(MAGIC);
diff --git a/luni/src/test/java/libcore/java/io/CharArrayWriterTest.java b/luni/src/test/java/libcore/java/io/CharArrayWriterTest.java
new file mode 100644
index 0000000..7e4dd42
--- /dev/null
+++ b/luni/src/test/java/libcore/java/io/CharArrayWriterTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 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.io;
+
+import java.io.CharArrayWriter;
+import junit.framework.TestCase;
+
+public final class CharArrayWriterTest extends TestCase {
+    public void testCharArrayWriter() throws Exception {
+        String str = "AbCdEfGhIjKlMnOpQrStUvWxYz";
+        CharArrayWriter a = new CharArrayWriter();
+        CharArrayWriter b = new CharArrayWriter();
+
+        a.write(str, 0, 26);
+        a.write('X');
+        a.writeTo(b);
+
+        assertEquals(27, a.size());
+        assertEquals("AbCdEfGhIjKlMnOpQrStUvWxYzX", a.toString());
+
+        b.write("alphabravodelta", 5, 5);
+        b.append('X');
+        assertEquals("AbCdEfGhIjKlMnOpQrStUvWxYzXbravoX", b.toString());
+        b.append("omega");
+        assertEquals("AbCdEfGhIjKlMnOpQrStUvWxYzXbravoXomega", b.toString());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index d59111d..3cf621e 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -146,13 +146,6 @@
 
     // http://b/3047893 - getCanonicalPath wasn't actually resolving symbolic links.
     public void test_getCanonicalPath() throws Exception {
-        if (new File("/sdcard").exists()) {
-            // This assumes the current Android setup where /sdcard is a symbolic link to
-            // /mnt/sdcard.
-            File testFile = new File("/sdcard/test1.txt");
-            assertEquals("/mnt/sdcard/test1.txt", testFile.getCanonicalPath());
-        }
-
         // This assumes you can create symbolic links in the temporary directory. This isn't
         // true on Android if you're using /sdcard. It will work in /data/local though.
         File base = createTemporaryDirectory();
diff --git a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
index 11142d5..afe49b7 100644
--- a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
+++ b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import junit.framework.TestCase;
+import libcore.java.lang.ref.FinalizationTester;
 
 public final class RandomAccessFileTest extends TestCase {
 
@@ -69,8 +70,7 @@
         File file = File.createTempFile("RandomAccessFileTest", "tmp");
         for (int i = 0; i < tooManyOpenFiles; i++) {
             createRandomAccessFile(file);
-            System.gc();
-            System.runFinalization();
+            FinalizationTester.induceFinalization();
         }
     }
     private void createRandomAccessFile(File file) throws Exception {
diff --git a/luni/src/test/java/libcore/java/io/SerializationTest.java b/luni/src/test/java/libcore/java/io/SerializationTest.java
index 74becfe..434dd56 100644
--- a/luni/src/test/java/libcore/java/io/SerializationTest.java
+++ b/luni/src/test/java/libcore/java/io/SerializationTest.java
@@ -16,9 +16,11 @@
 
 package libcore.java.io;
 
+import java.io.IOException;
+import java.io.InvalidClassException;
 import java.io.Serializable;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public final class SerializationTest extends TestCase {
 
@@ -28,7 +30,7 @@
         String s = "aced0005737200346c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
                 + "374244669656c644d6164655472616e7369656e74000000000000000002000149000c7472616e736"
                 + "9656e74496e747870abababab";
-        FieldMadeTransient deserialized = (FieldMadeTransient) SerializableTester.deserializeHex(s);
+        FieldMadeTransient deserialized = (FieldMadeTransient) SerializationTester.deserializeHex(s);
         assertEquals(0, deserialized.transientInt);
     }
 
@@ -36,4 +38,36 @@
         private static final long serialVersionUID = 0L;
         private transient int transientInt;
     }
+
+    public void testSerialVersionUidChange() throws Exception {
+        // this was created by serializing a SerialVersionUidChanged with serialVersionUID = 0L
+        String s = "aced0005737200396c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+                + "3742453657269616c56657273696f6e5569644368616e67656400000000000000000200014900016"
+                + "1787000000003";
+        try {
+            SerializationTester.deserializeHex(s);
+            fail();
+        } catch (InvalidClassException expected) {
+        }
+    }
+
+    static class SerialVersionUidChanged implements Serializable {
+        private static final long serialVersionUID = 1L; // was 0L
+        private int a;
+    }
+
+    public void testMissingSerialVersionUid() throws Exception {
+        // this was created by serializing a FieldsChanged with one int field named 'a'
+        String s = "aced00057372002f6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+                + "374244669656c64734368616e6765643bcfb934e310fa1c02000149000161787000000003";
+        try {
+            SerializationTester.deserializeHex(s);
+            fail();
+        } catch (InvalidClassException expected) {
+        }
+    }
+
+    static class FieldsChanged implements Serializable {
+        private int b; // was 'a'
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/AssertionErrorTest.java b/luni/src/test/java/libcore/java/lang/AssertionErrorTest.java
new file mode 100644
index 0000000..638b15b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/AssertionErrorTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 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.lang;
+
+import junit.framework.TestCase;
+
+public final class AssertionErrorTest extends TestCase {
+    // http://code.google.com/p/android/issues/detail?id=29378
+    public void test_29378() throws Exception {
+        AssertionError ae = new AssertionError("hello");
+        ae.initCause(new Throwable());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/EnumTest.java b/luni/src/test/java/libcore/java/lang/EnumTest.java
new file mode 100644
index 0000000..bae42c0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/EnumTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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.lang;
+
+import junit.framework.TestCase;
+import libcore.util.SerializationTester;
+
+public final class EnumTest extends TestCase {
+    public void testEnumSerialization() {
+        String s = "aced00057e7200236c6962636f72652e6a6176612e6c616e672e456e756d5465"
+                + "737424526f7368616d626f00000000000000001200007872000e6a6176612e6c6"
+                + "16e672e456e756d000000000000000012000078707400055041504552";
+        Roshambo value = Roshambo.PAPER;
+        assertTrue(value.getClass() == Roshambo.class);
+        new SerializationTester<Roshambo>(value, s).test();
+    }
+
+    public void testEnumSubclassSerialization() {
+        String s = "aced00057e7200236c6962636f72652e6a6176612e6c616e672e456e756d5465"
+                + "737424526f7368616d626f00000000000000001200007872000e6a6176612e6c6"
+                + "16e672e456e756d00000000000000001200007870740004524f434b";
+        Roshambo value = Roshambo.ROCK;
+        assertTrue(value.getClass() != Roshambo.class);
+        new SerializationTester<Roshambo>(value, s).test();
+    }
+
+    enum Roshambo {
+        ROCK {
+            @Override public String toString() {
+                return "rock!";
+            }
+        },
+        PAPER,
+        SCISSORS
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/LongTest.java b/luni/src/test/java/libcore/java/lang/LongTest.java
index 08bdd15..9e143da 100644
--- a/luni/src/test/java/libcore/java/lang/LongTest.java
+++ b/luni/src/test/java/libcore/java/lang/LongTest.java
@@ -31,4 +31,12 @@
         assertTrue(Long.compare(min,  zero) < 0);
         assertTrue(Long.compare(min,  max)  < 0);
     }
+
+    public void test_signum() throws Exception {
+        assertEquals(0, Long.signum(0));
+        assertEquals(1, Long.signum(1));
+        assertEquals(-1, Long.signum(-1));
+        assertEquals(1, Long.signum(Long.MAX_VALUE));
+        assertEquals(-1, Long.signum(Long.MIN_VALUE));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java b/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java
index 4b692d7..0926569 100644
--- a/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java
@@ -17,7 +17,6 @@
 
 package libcore.java.lang;
 
-import dalvik.annotation.KnownFailure;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -462,7 +461,6 @@
         Runtime.getRuntime().traceInstructions(false);
     }
 
-    @KnownFailure("Fails in CTS but passes under run-core-tests")
     public void test_traceMethodCalls() {
         try {
             Runtime.getRuntime().traceMethodCalls(false);
diff --git a/luni/src/test/java/libcore/java/lang/OldThreadTest.java b/luni/src/test/java/libcore/java/lang/OldThreadTest.java
index 1e189f6..2b304f2 100644
--- a/luni/src/test/java/libcore/java/lang/OldThreadTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldThreadTest.java
@@ -21,6 +21,7 @@
 import java.io.PrintStream;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.locks.LockSupport;
+import libcore.java.lang.ref.FinalizationTester;
 
 public class OldThreadTest extends junit.framework.TestCase {
 
@@ -478,7 +479,7 @@
             spinner = null;
             st = null;
             ct = null;
-            System.runFinalization();
+            FinalizationTester.induceFinalization();
         } catch (Exception e) {
         }
     }
diff --git a/luni/src/test/java/libcore/java/lang/ThreadTest.java b/luni/src/test/java/libcore/java/lang/ThreadTest.java
index 3695e3e..998afdb 100644
--- a/luni/src/test/java/libcore/java/lang/ThreadTest.java
+++ b/luni/src/test/java/libcore/java/lang/ThreadTest.java
@@ -17,10 +17,21 @@
 package libcore.java.lang;
 
 import java.util.concurrent.atomic.AtomicInteger;
+import junit.framework.Assert;
 import junit.framework.TestCase;
+import libcore.java.lang.ref.FinalizationTester;
 
 public final class ThreadTest extends TestCase {
 
+    /**
+     * getContextClassLoader returned a non-application class loader.
+     * http://code.google.com/p/android/issues/detail?id=5697
+     */
+    public void testJavaContextClassLoader() throws Exception {
+        Assert.assertNotNull("Must have a Java context ClassLoader",
+                Thread.currentThread().getContextClassLoader());
+    }
+
     public void testLeakingStartedThreads() {
         final AtomicInteger finalizedThreadsCount = new AtomicInteger();
         for (int i = 0; true; i++) {
@@ -30,7 +41,7 @@
                 break;
             }
         }
-        System.runFinalization();
+        FinalizationTester.induceFinalization();
         assertTrue("Started threads were never finalized!", finalizedThreadsCount.get() > 0);
     }
 
@@ -43,7 +54,7 @@
                 break;
             }
         }
-        System.runFinalization();
+        FinalizationTester.induceFinalization();
         assertTrue("Unstarted threads were never finalized!", finalizedThreadsCount.get() > 0);
     }
 
@@ -56,4 +67,46 @@
             }
         };
     }
+
+    /**
+     * Thread.getStackTrace() is broken. http://b/1252043
+     */
+    public void testGetStackTrace() throws Exception {
+        Thread t1 = new Thread("t1") {
+            @Override public void run() {
+                doSomething();
+            }
+            public void doSomething() {
+                for (int i = 0; i < 20;) {
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException ignored) {
+                    }
+                }
+            }
+        };
+        t1.start();
+        Thread.sleep(1000);
+        StackTraceElement[] traces = t1.getStackTrace();
+        StackTraceElement trace = traces[traces.length - 2];
+
+        // Expect to find MyThread.doSomething in the trace
+        assertTrue(trace.getClassName().contains("ThreadTest")
+                && trace.getMethodName().equals("doSomething"));
+    }
+
+    public void testGetAllStackTracesIncludesAllGroups() throws Exception {
+        final AtomicInteger visibleTraces = new AtomicInteger();
+        ThreadGroup group = new ThreadGroup("1");
+        Thread t2 = new Thread(group, "t2") {
+            @Override public void run() {
+                visibleTraces.set(Thread.getAllStackTraces().size());
+            }
+        };
+        t2.start();
+        t2.join();
+
+        // Expect to see the traces of all threads (not just t2)
+        assertTrue("Must have traces for all threads", visibleTraces.get() > 1);
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/ThrowableTest.java b/luni/src/test/java/libcore/java/lang/ThrowableTest.java
index 700f616..1906784c 100644
--- a/luni/src/test/java/libcore/java/lang/ThrowableTest.java
+++ b/luni/src/test/java/libcore/java/lang/ThrowableTest.java
@@ -20,7 +20,7 @@
 import java.io.StringWriter;
 import java.util.Arrays;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public class ThrowableTest extends TestCase {
     private static class NoStackTraceException extends Exception {
@@ -248,7 +248,7 @@
                 + "00000007078";
         Throwable throwable = new SuppressionsThrowable("foo", null, false);
         throwable.setStackTrace(new StackTraceElement[0]);
-        new SerializableTester<Throwable>(throwable, s) {
+        new SerializationTester<Throwable>(throwable, s) {
             @Override protected boolean equals(Throwable a, Throwable b) {
                 return printStackTraceToString(a).equals(printStackTraceToString(b));
             }
@@ -277,7 +277,7 @@
                 + "870000000007704000000007871007e000f78";
         Throwable throwable = new SuppressionsThrowable("foo", null, true);
         throwable.setStackTrace(new StackTraceElement[0]);
-        new SerializableTester<Throwable>(throwable, s) {
+        new SerializationTester<Throwable>(throwable, s) {
             @Override protected boolean equals(Throwable a, Throwable b) {
                 return printStackTraceToString(a).equals(printStackTraceToString(b));
             }
@@ -291,7 +291,7 @@
     }
 
     private void assertSerialized(final Throwable throwable, String golden) {
-        new SerializableTester<Throwable>(throwable, golden) {
+        new SerializationTester<Throwable>(throwable, golden) {
             @Override protected boolean equals(Throwable a, Throwable b) {
                 return printStackTraceToString(a).equals(printStackTraceToString(b));
             }
diff --git a/luni/src/test/java/libcore/java/lang/ref/FinalizationTester.java b/luni/src/test/java/libcore/java/lang/ref/FinalizationTester.java
new file mode 100644
index 0000000..66ac1a4
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/ref/FinalizationTester.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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.lang.ref;
+
+/**
+ * Triggers finalizers to run. Note that live-precise bugs may interfere with
+ * analysis of what is reachable. Unreachable references in local frames may not
+ * be finalized even after a call to {@link #induceFinalization}. To work
+ * around, create finalizable objects in helper methods. http://b/4191345
+ */
+public final class FinalizationTester {
+    private FinalizationTester() {}
+
+    public static void induceFinalization() {
+        System.gc();
+        enqueueReferences();
+        System.runFinalization();
+    }
+
+    public static void enqueueReferences() {
+        /*
+         * Hack. We don't have a programmatic way to wait for the reference queue
+         * daemon to move references to the appropriate queues.
+         */
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException e) {
+            throw new AssertionError();
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java
index b6f930c..10a26fe 100644
--- a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java
+++ b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java
@@ -27,17 +27,13 @@
         AtomicBoolean finalized = new AtomicBoolean();
         createFinalizableObject(finalized);
 
-        induceFinalization();
+        FinalizationTester.induceFinalization();
         if (!finalized.get()) {
             fail();
         }
     }
 
-    /**
-     * Prevent live-precise bugs from interfering with analysis of what is
-     * reachable. Do not inline this method; otherwise tests may fail on VMs
-     * that are not live-precise. http://b/4191345
-     */
+    /** Do not inline this method; that could break non-precise GCs. See FinalizationTester. */
     private void createFinalizableObject(final AtomicBoolean finalized) {
         new X() {
             @Override protected void finalize() throws Throwable {
@@ -56,24 +52,10 @@
         } catch (AssertionError expected) {
         }
 
-        induceFinalization();
+        FinalizationTester.induceFinalization();
         assertTrue("object whose constructor threw was not finalized", ConstructionFails.finalized);
     }
 
-    private void induceFinalization() throws Exception {
-        System.gc();
-        enqueueReferences();
-        System.runFinalization();
-    }
-
-    /**
-     * Hack. We don't have a programmatic way to wait for the reference queue
-     * daemon to move references to the appropriate queues.
-     */
-    private void enqueueReferences() throws InterruptedException {
-        Thread.sleep(100);
-    }
-
     static class ConstructionFails {
         private static boolean finalized;
 
@@ -97,7 +79,7 @@
         createSlowFinalizer(2000, latch);
         createSlowFinalizer(4000, latch);
         createSlowFinalizer(8000, latch);
-        induceFinalization();
+        FinalizationTester.induceFinalization();
         latch.await();
     }
 
@@ -119,7 +101,7 @@
         AtomicInteger count = new AtomicInteger();
         AtomicBoolean keepGoing = new AtomicBoolean(true);
         createChainedFinalizer(count, keepGoing);
-        induceFinalization();
+        FinalizationTester.induceFinalization();
         keepGoing.set(false);
         assertTrue(count.get() > 0);
     }
@@ -133,7 +115,7 @@
                     createChainedFinalizer(counter, keepGoing); // recursive!
                 }
                 System.gc();
-                enqueueReferences();
+                FinalizationTester.enqueueReferences();
             }
         };
     }
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ArrayTest.java b/luni/src/test/java/libcore/java/lang/reflect/ArrayTest.java
new file mode 100644
index 0000000..71fa66d
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/ArrayTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012 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.lang.reflect;
+
+import java.lang.reflect.Array;
+import junit.framework.TestCase;
+
+public class ArrayTest extends TestCase {
+  private static boolean[] booleans;
+  private static byte[] bytes;
+  private static char[] chars;
+  private static double[] doubles;
+  private static float[] floats;
+  private static int[] ints;
+  private static long[] longs;
+  private static short[] shorts;
+
+  @Override protected void setUp() throws Exception {
+    super.setUp();
+    booleans = new boolean[] { true };
+    bytes = new byte[] { (byte) 0xff };
+    chars = new char[] { '\uffff' };
+    doubles = new double[] { (double) 0xffffffffffffffffL };
+    floats = new float[] { (float) 0xffffffff };
+    ints = new int[] { 0xffffffff };
+    longs = new long[] { 0xffffffffffffffffL };
+    shorts = new short[] { (short) 0xffff };
+  }
+
+  public void testGetBoolean() throws Exception {
+    assertEquals(booleans[0], Array.getBoolean(booleans, 0));
+    try { Array.getBoolean(bytes, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(chars, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(floats, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(ints, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(longs, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(shorts, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getBoolean(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetByte() throws Exception {
+    try { Array.getByte(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals(bytes[0], Array.getByte(bytes, 0));
+    try { Array.getByte(chars, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getByte(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getByte(floats, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getByte(ints, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getByte(longs, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getByte(shorts, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getByte(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetChar() throws Exception {
+    try { Array.getChar(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getChar(bytes, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals(chars[0], Array.getChar(chars, 0));
+    try { Array.getChar(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getChar(floats, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getChar(ints, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getChar(longs, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getChar(shorts, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getChar(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetDouble() throws Exception {
+    try { Array.getDouble(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((double) bytes[0], Array.getDouble(bytes, 0));
+    assertEquals((double) chars[0], Array.getDouble(chars, 0));
+    assertEquals(doubles[0], Array.getDouble(doubles, 0));
+    assertEquals((double) floats[0], Array.getDouble(floats, 0));
+    assertEquals((double) ints[0], Array.getDouble(ints, 0));
+    assertEquals((double) longs[0], Array.getDouble(longs, 0));
+    assertEquals((double) shorts[0], Array.getDouble(shorts, 0));
+    try { Array.getDouble(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetFloat() throws Exception {
+    try { Array.getFloat(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((float) bytes[0], Array.getFloat(bytes, 0));
+    assertEquals((float) chars[0], Array.getFloat(chars, 0));
+    assertEquals(floats[0], Array.getFloat(floats, 0));
+    try { Array.getFloat(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((float) ints[0], Array.getFloat(ints, 0));
+    assertEquals((float) longs[0], Array.getFloat(longs, 0));
+    assertEquals((float) shorts[0], Array.getFloat(shorts, 0));
+    try { Array.getFloat(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetInt() throws Exception {
+    try { Array.getInt(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((int) bytes[0], Array.getInt(bytes, 0));
+    assertEquals((int) chars[0], Array.getInt(chars, 0));
+    try { Array.getInt(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getInt(floats, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals(ints[0], Array.getInt(ints, 0));
+    try { Array.getInt(longs, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((int) shorts[0], Array.getInt(shorts, 0));
+    try { Array.getInt(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetLong() throws Exception {
+    try { Array.getLong(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((long) bytes[0], Array.getLong(bytes, 0));
+    assertEquals((long) chars[0], Array.getLong(chars, 0));
+    try { Array.getLong(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getLong(floats, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((long) ints[0], Array.getLong(ints, 0));
+    assertEquals(longs[0], Array.getLong(longs, 0));
+    assertEquals((long) shorts[0], Array.getLong(shorts, 0));
+    try { Array.getLong(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testGetShort() throws Exception {
+    try { Array.getShort(booleans, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals((int) bytes[0], Array.getShort(bytes, 0));
+    try { Array.getShort(chars, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getShort(doubles, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getShort(floats, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getShort(ints, 0); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.getShort(longs, 0); fail(); } catch (IllegalArgumentException expected) {}
+    assertEquals(shorts[0], Array.getShort(shorts, 0));
+    try { Array.getShort(null, 0); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetBoolean() throws Exception {
+    Array.setBoolean(booleans, 0, booleans[0]);
+    try { Array.setBoolean(bytes, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(chars, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(doubles, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(floats, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(ints, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(longs, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(shorts, 0, true); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setBoolean(null, 0, true); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetByte() throws Exception {
+    try { Array.setByte(booleans, 0, bytes[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setByte(bytes, 0, bytes[0]);
+    try { Array.setByte(chars, 0, bytes[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setByte(doubles, 0, bytes[0]);
+    Array.setByte(floats, 0, bytes[0]);
+    Array.setByte(ints, 0, bytes[0]);
+    Array.setByte(longs, 0, bytes[0]);
+    Array.setByte(shorts, 0, bytes[0]);
+    try { Array.setByte(null, 0, bytes[0]); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetChar() throws Exception {
+    try { Array.setChar(booleans, 0, chars[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setChar(bytes, 0, chars[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setChar(chars, 0, chars[0]);
+    Array.setChar(doubles, 0, chars[0]);
+    Array.setChar(floats, 0, chars[0]);
+    Array.setChar(ints, 0, chars[0]);
+    Array.setChar(longs, 0, chars[0]);
+    try { Array.setChar(shorts, 0, chars[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setChar(null, 0, chars[0]); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetDouble() throws Exception {
+    try { Array.setDouble(booleans, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setDouble(bytes, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setDouble(chars, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setDouble(doubles, 0, doubles[0]);
+    try { Array.setDouble(floats, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setDouble(ints, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setDouble(longs, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setDouble(shorts, 0, doubles[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setDouble(null, 0, doubles[0]); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetFloat() throws Exception {
+    try { Array.setFloat(booleans, 0, floats[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setFloat(bytes, 0, floats[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setFloat(chars, 0, floats[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setFloat(floats, 0, floats[0]);
+    Array.setFloat(doubles, 0, floats[0]);
+    try { Array.setFloat(ints, 0, floats[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setFloat(longs, 0, floats[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setFloat(shorts, 0, floats[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setFloat(null, 0, floats[0]); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetInt() throws Exception {
+    try { Array.setInt(booleans, 0, ints[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setInt(bytes, 0, ints[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setInt(chars, 0, ints[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setInt(doubles, 0, ints[0]);
+    Array.setInt(floats, 0, ints[0]);
+    Array.setInt(ints, 0, ints[0]);
+    Array.setInt(longs, 0, ints[0]);
+    try { Array.setInt(shorts, 0, ints[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setInt(null, 0, ints[0]); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetLong() throws Exception {
+    try { Array.setLong(booleans, 0, longs[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setLong(bytes, 0, longs[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setLong(chars, 0, longs[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setLong(doubles, 0, longs[0]);
+    Array.setLong(floats, 0, longs[0]);
+    try { Array.setLong(ints, 0, longs[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setLong(longs, 0, longs[0]);
+    try { Array.setLong(shorts, 0, longs[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setLong(null, 0, longs[0]); fail(); } catch (NullPointerException expected) {}
+  }
+
+  public void testSetShort() throws Exception {
+    try { Array.setShort(booleans, 0, shorts[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setShort(bytes, 0, shorts[0]); fail(); } catch (IllegalArgumentException expected) {}
+    try { Array.setShort(chars, 0, shorts[0]); fail(); } catch (IllegalArgumentException expected) {}
+    Array.setShort(doubles, 0, shorts[0]);
+    Array.setShort(floats, 0, shorts[0]);
+    Array.setShort(ints, 0, shorts[0]);
+    Array.setShort(longs, 0, shorts[0]);
+    Array.setShort(shorts, 0, shorts[0]);
+    try { Array.setShort(null, 0, shorts[0]); fail(); } catch (NullPointerException expected) {}
+  }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
new file mode 100644
index 0000000..eb3d034
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 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.lang.reflect;
+
+import java.lang.reflect.Field;
+import junit.framework.TestCase;
+
+public final class FieldTest extends TestCase {
+    private static final long MY_LONG = 5073258162644648461L;
+
+    // Reflection for static long fields was broken http://b/1120750
+    public void testLongFieldReflection() throws Exception {
+        Field field = getClass().getDeclaredField("MY_LONG");
+        assertEquals(5073258162644648461L, field.getLong(null));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
index a059d96..ef30eb8 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
@@ -197,6 +197,24 @@
         assertEquals(anonymous.getClass(), method.getDeclaringClass());
     }
 
+    // http://b/1045939
+    public void testMethodToString() throws Exception {
+        assertEquals("public final native void java.lang.Object.notify()",
+                Object.class.getMethod("notify", new Class[] { }).toString());
+        assertEquals("public java.lang.String java.lang.Object.toString()",
+                Object.class.getMethod("toString", new Class[] { }).toString());
+        assertEquals("public final native void java.lang.Object.wait(long,int)"
+                + " throws java.lang.InterruptedException",
+                Object.class.getMethod("wait", new Class[] { long.class, int.class }).toString());
+        assertEquals("public boolean java.lang.Object.equals(java.lang.Object)",
+                Object.class.getMethod("equals", new Class[] { Object.class }).toString());
+        assertEquals("public static java.lang.String java.lang.String.valueOf(char[])",
+                String.class.getMethod("valueOf", new Class[] { char[].class }).toString());
+        assertEquals( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])"
+                + " throws java.io.IOException",
+                Runtime.class.getMethod("exec", new Class[] { String[].class }).toString());
+    }
+
     public static class MethodTestHelper {
         public void m1() throws IndexOutOfBoundsException { }
         public void m2(Object o) { }
diff --git a/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java
new file mode 100644
index 0000000..0a95bbf
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2008 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.lang.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import junit.framework.TestCase;
+
+public final class OldAndroidClassTest extends TestCase {
+    private static final String packageName = "libcore.java.lang.reflect";
+
+    public void testNewInstance() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Object instance = helloClass.newInstance();
+        assertNotNull(instance);
+    }
+
+    public void testForName() throws Exception {
+        try {
+            Class.forName("this.class.DoesNotExist");
+            fail();
+        } catch (ClassNotFoundException expected) {
+        }
+    }
+
+    public void testNewInstancePrivateConstructor() throws Exception {
+        try {
+            Class.forName(packageName + ".ClassWithPrivateConstructor").newInstance();
+            fail();
+        } catch (IllegalAccessException expected) {
+        }
+    }
+
+    public void testGetDeclaredMethod() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Method method = helloClass.getDeclaredMethod("method", (Class[]) null);
+        method.invoke(new OldAndroidClassTest(), (Object[]) null);
+    }
+
+    public void testGetDeclaredMethodWithArgs() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Method method = helloClass.getDeclaredMethod("methodWithArgs", Object.class);
+
+        Object invokeArgs[] = new Object[1];
+        invokeArgs[0] = "Hello";
+        Object ret = method.invoke(new OldAndroidClassTest(), invokeArgs);
+        assertEquals(ret, invokeArgs[0]);
+    }
+
+    public void testGetDeclaredMethodPrivate() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Method method = helloClass.getDeclaredMethod("privateMethod", (Class[]) null);
+        method.invoke(new OldAndroidClassTest(), (Object[]) null);
+    }
+
+    public void testGetSuperclass() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Class objectClass = Class.forName("java.lang.Object");
+        assertEquals(helloClass.getSuperclass().getSuperclass().getSuperclass(), objectClass);
+    }
+
+    public void testIsAssignableFrom() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Class objectClass = Class.forName("java.lang.Object");
+        assertTrue(objectClass.isAssignableFrom(helloClass));
+        assertFalse(helloClass.isAssignableFrom(objectClass));
+    }
+
+    public void testGetConstructor() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        Constructor constructor = helloClass.getConstructor((Class[]) null);
+        assertNotNull(constructor);
+    }
+
+    public void testGetModifiers() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        assertTrue(Modifier.isPublic(helloClass.getModifiers()));
+    }
+
+    public void testGetMethod() throws Exception {
+        Class helloClass = Class.forName(OldAndroidClassTest.class.getName());
+        helloClass.getMethod("method", (Class[]) null);
+        try {
+            Class[] argTypes = new Class[1];
+            argTypes[0] = helloClass;
+            helloClass.getMethod("method", argTypes);
+            fail();
+        } catch (NoSuchMethodException expected) {
+        }
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=14
+    public void testFieldSet() throws Exception {
+        OldAndroidClassTest.SimpleClass obj = new OldAndroidClassTest.SimpleClass();
+        Field field = obj.getClass().getDeclaredField("str");
+        field.set(obj, null);
+    }
+
+    public class SimpleClass {
+        public String str;
+    }
+
+    public Object methodWithArgs(Object o) {
+        return o;
+    }
+
+    boolean methodInvoked;
+
+    public void method() {
+        methodInvoked = true;
+    }
+
+    boolean privateMethodInvoked;
+
+    public void privateMethod() {
+        privateMethodInvoked = true;
+    }
+
+    // Regression for 1018067: Class.getMethods() returns the same method over
+    // and over again from all base classes
+    public void testClassGetMethodsNoDupes() {
+        Method[] methods = ArrayList.class.getMethods();
+        Set<String> set = new HashSet<String>();
+
+        for (Method method : methods) {
+            String signature = method.toString();
+
+            int par = signature.indexOf('(');
+            int dot = signature.lastIndexOf('.', par);
+
+            signature = signature.substring(dot + 1);
+
+            assertFalse("Duplicate " + signature, set.contains(signature));
+            set.add(signature);
+        }
+    }
+
+    interface MyInterface {
+        void foo();
+    }
+
+    interface MyOtherInterface extends MyInterface {
+        void bar();
+    }
+
+    abstract class MyClass implements MyOtherInterface {
+        public void gabba() {
+        }
+
+        public void hey() {
+        }
+    }
+
+    // Check if we also reflect methods from interfaces
+    public void testGetMethodsInterfaces() {
+        Method[] methods = MyInterface.class.getMethods();
+        assertTrue(hasMethod(methods, ".foo("));
+
+        methods = MyOtherInterface.class.getMethods();
+        assertTrue(hasMethod(methods, ".foo("));
+        assertTrue(hasMethod(methods, ".bar("));
+
+        methods = MyClass.class.getMethods();
+        assertTrue(hasMethod(methods, ".foo("));
+        assertTrue(hasMethod(methods, ".bar("));
+
+        assertTrue(hasMethod(methods, ".gabba("));
+        assertTrue(hasMethod(methods, ".hey("));
+
+        assertTrue(hasMethod(methods, ".toString("));
+    }
+
+    private boolean hasMethod(Method[] methods, String signature) {
+        for (Method method : methods) {
+            if (method.toString().contains(signature)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Test for Class.getPackage();
+    public void testClassGetPackage() {
+        assertNotNull(getClass().getPackage());
+        assertEquals(packageName, getClass().getPackage().getName());
+        assertEquals("Unknown", getClass().getPackage().getSpecificationTitle());
+
+        Package p = Object.class.getPackage();
+        assertNotNull(p);
+        assertEquals("java.lang", p.getName());
+        assertSame(p, Object.class.getPackage());
+    }
+
+    // Regression test for #1123708: Problem with getCanonicalName(),
+    // getSimpleName(), and getPackage().
+    //
+    // A couple of interesting cases need to be checked: Top-level classes,
+    // member classes, local classes, and anonymous classes. Also, boundary
+    // cases with '$' in the class names are checked, since the '$' is used
+    // as the separator between outer and inner class, so this might lead
+    // to problems (it did in the previous implementation).
+    //
+    // Caution: Adding local or anonymous classes elsewhere in this
+    // file might affect the test.
+    private class MemberClass {
+    }
+
+    private class Mi$o$oup {
+    }
+
+    public void testVariousClassNames() {
+        Class<?> clazz = this.getClass();
+        String pkg = (clazz.getPackage() == null ? "" : clazz.getPackage().getName() + ".");
+
+        // Simple, top-level class
+
+        assertEquals(pkg + "OldAndroidClassTest", clazz.getName());
+        assertEquals("OldAndroidClassTest", clazz.getSimpleName());
+        assertEquals(pkg + "OldAndroidClassTest", clazz.getCanonicalName());
+
+        clazz = MemberClass.class;
+
+        assertEquals(pkg + "OldAndroidClassTest$MemberClass", clazz.getName());
+        assertEquals("MemberClass", clazz.getSimpleName());
+        assertEquals(pkg + "OldAndroidClassTest.MemberClass", clazz.getCanonicalName());
+
+        class LocalClass {
+            // This space intentionally left blank.
+        }
+
+        clazz = LocalClass.class;
+
+        assertEquals(pkg + "OldAndroidClassTest$1LocalClass", clazz.getName());
+        assertEquals("LocalClass", clazz.getSimpleName());
+        assertNull(clazz.getCanonicalName());
+
+        clazz = new Object() { }.getClass();
+
+        assertEquals(pkg + "OldAndroidClassTest$1", clazz.getName());
+        assertEquals("", clazz.getSimpleName());
+        assertNull(clazz.getCanonicalName());
+
+        // Weird special cases with dollar in name.
+
+        clazz = Mou$$aka.class;
+
+        assertEquals(pkg + "Mou$$aka", clazz.getName());
+        assertEquals("Mou$$aka", clazz.getSimpleName());
+        assertEquals(pkg + "Mou$$aka", clazz.getCanonicalName());
+
+        clazz = Mi$o$oup.class;
+
+        assertEquals(pkg + "OldAndroidClassTest$Mi$o$oup", clazz.getName());
+        assertEquals("Mi$o$oup", clazz.getSimpleName());
+        assertEquals(pkg + "OldAndroidClassTest.Mi$o$oup", clazz.getCanonicalName());
+
+        class Ma$hedPotatoe$ {
+        }
+
+        clazz = Ma$hedPotatoe$.class;
+
+        assertEquals(pkg + "OldAndroidClassTest$1Ma$hedPotatoe$", clazz.getName());
+        assertEquals("Ma$hedPotatoe$", clazz.getSimpleName());
+        assertNull(clazz.getCanonicalName());
+    }
+
+    public void testLocalMemberClass() {
+        Class<?> clazz = this.getClass();
+
+        assertFalse(clazz.isMemberClass());
+        assertFalse(clazz.isLocalClass());
+
+        clazz = MemberClass.class;
+
+        assertTrue(clazz.isMemberClass());
+        assertFalse(clazz.isLocalClass());
+
+        class OtherLocalClass {
+        }
+
+        clazz = OtherLocalClass.class;
+
+        assertFalse(clazz.isMemberClass());
+        assertTrue(clazz.isLocalClass());
+
+        clazz = new Object() { }.getClass();
+
+        assertFalse(clazz.isMemberClass());
+        assertFalse(clazz.isLocalClass());
+    }
+}
+
+class ClassWithPrivateConstructor {
+    private ClassWithPrivateConstructor() {
+    }
+}
+
+class Mou$$aka {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/OldGenericReflectionCornerCases.java b/luni/src/test/java/libcore/java/lang/reflect/OldGenericReflectionCornerCases.java
index 907b8eb..168a00d 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/OldGenericReflectionCornerCases.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/OldGenericReflectionCornerCases.java
@@ -16,7 +16,6 @@
 
 package libcore.java.lang.reflect;
 
-import dalvik.annotation.KnownFailure;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -122,7 +121,6 @@
         void multipleBoundedWildcardUnEquality(Pair<? extends T, ? super T> param) {}
     }
     @SuppressWarnings("unchecked")
-    @KnownFailure("Fails in CTS but passes under run-core-tests")
     public void testMultipleBoundedWildcardUnEquality() throws Exception {
         Class<? extends MultipleBoundedWildcardUnEquality> clazz = MultipleBoundedWildcardUnEquality.class;
 
@@ -178,7 +176,6 @@
         void multipleBoundedWildcardEquality(Pair<? extends T, ? extends T> param) {}
     }
     @SuppressWarnings("unchecked")
-    @KnownFailure("Fails in CTS but passes under run-core-tests")
     public void testMultipleBoundedWildcard() throws Exception {
         Class<? extends MultipleBoundedWildcardEquality> clazz = MultipleBoundedWildcardEquality.class;
 
diff --git a/luni/src/test/java/libcore/java/lang/reflect/OldGenericTypesTest.java b/luni/src/test/java/libcore/java/lang/reflect/OldGenericTypesTest.java
index f93b683..69eff4f 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/OldGenericTypesTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/OldGenericTypesTest.java
@@ -16,7 +16,6 @@
 
 package libcore.java.lang.reflect;
 
-import dalvik.annotation.KnownFailure;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
@@ -160,7 +159,6 @@
         assertEquals(typeVariableT, boundS);
     }
     @SuppressWarnings("unchecked")
-    @KnownFailure("Fails in CTS but passes under run-core-tests")
     public void testSimpleInheritance() throws Exception {
         Class<? extends SimpleInheritance> clazz = SimpleInheritance.class;
         TypeVariable<Class> subTypeVariable = getTypeParameter(clazz);
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
index 3b092e5..cdce405 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
@@ -29,10 +29,10 @@
 import java.util.Map;
 import java.util.RandomAccess;
 import java.util.Set;
+import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public final class ReflectionTest extends TestCase {
-
     String classA = "libcore.java.lang.reflect.ReflectionTest$A";
     String classB = "libcore.java.lang.reflect.ReflectionTest$B";
     String classC = "libcore.java.lang.reflect.ReflectionTest$C";
@@ -45,6 +45,29 @@
                 AList.class.getGenericSuperclass().toString());
     }
 
+    public void testClassGetName() {
+        assertEquals("int", int.class.getName());
+        assertEquals("[I", int[].class.getName());
+        assertEquals("java.lang.String", String.class.getName());
+        assertEquals("[Ljava.lang.String;", String[].class.getName());
+        assertEquals("libcore.java.lang.reflect.ReflectionTest", getClass().getName());
+        assertEquals(getClass().getName() + "$A", A.class.getName());
+        assertEquals(getClass().getName() + "$B", B.class.getName());
+        assertEquals(getClass().getName() + "$DefinesMember", DefinesMember.class.getName());
+    }
+
+    public void testClassGetCanonicalName() {
+        assertEquals("int", int.class.getCanonicalName());
+        assertEquals("int[]", int[].class.getCanonicalName());
+        assertEquals("java.lang.String", String.class.getCanonicalName());
+        assertEquals("java.lang.String[]", String[].class.getCanonicalName());
+        assertEquals("libcore.java.lang.reflect.ReflectionTest", getClass().getCanonicalName());
+        assertEquals(getClass().getName() + ".A", A.class.getCanonicalName());
+        assertEquals(getClass().getName() + ".B", B.class.getCanonicalName());
+        assertEquals(getClass().getName() + ".DefinesMember",
+                DefinesMember.class.getCanonicalName());
+    }
+
     public void testFieldToString() throws Exception {
         Field fieldOne = C.class.getDeclaredField("fieldOne");
         String fieldOneRaw = "public static " + classA + " " + classC + ".fieldOne";
@@ -254,6 +277,28 @@
         assertEquals(1, count(names(fields), "field"));
     }
 
+    /**
+     * Class.isEnum() erroneously returned true for indirect descendants of
+     * Enum. http://b/1062200.
+     */
+    public void testClassIsEnum() {
+        Class<?> trafficClass = TrafficLights.class;
+        Class<?> redClass = TrafficLights.RED.getClass();
+        Class<?> yellowClass = TrafficLights.YELLOW.getClass();
+        Class<?> greenClass = TrafficLights.GREEN.getClass();
+        assertSame(trafficClass, redClass);
+        assertNotSame(trafficClass, yellowClass);
+        assertNotSame(trafficClass, greenClass);
+        assertNotSame(yellowClass, greenClass);
+        assertTrue(trafficClass.isEnum());
+        assertTrue(redClass.isEnum());
+        assertFalse(yellowClass.isEnum());
+        assertFalse(greenClass.isEnum());
+        assertNotNull(trafficClass.getEnumConstants());
+        assertNull(yellowClass.getEnumConstants());
+        assertNull(greenClass.getEnumConstants());
+    }
+
     static class A {}
     static class AList extends ArrayList<A> {}
 
@@ -319,4 +364,15 @@
         }
         return result;
     }
+
+    enum TrafficLights {
+        RED,
+        YELLOW {},
+        GREEN {
+            @SuppressWarnings("unused")
+            int i;
+            @SuppressWarnings("unused")
+            void foobar() {}
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
index 726b05a..d33c5f3 100644
--- a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
+++ b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
@@ -62,6 +62,21 @@
         }
     }
 
+    public void test_connect_timeout() throws Exception {
+        StuckServer ss = new StuckServer();
+        Socket s = new Socket();
+        new Killer(s).start();
+        try {
+            System.err.println("connect (with timeout)...");
+            s.connect(ss.getLocalSocketAddress(), 3600 * 1000);
+            fail("connect returned!");
+        } catch (SocketException expected) {
+            assertEquals("Socket closed", expected.getMessage());
+        } finally {
+            ss.close();
+        }
+    }
+
     public void test_connect_nonBlocking() throws Exception {
         StuckServer ss = new StuckServer();
         SocketChannel s = SocketChannel.open();
diff --git a/luni/src/test/java/libcore/java/net/CookiesTest.java b/luni/src/test/java/libcore/java/net/CookiesTest.java
index a661673..f14a91e 100644
--- a/luni/src/test/java/libcore/java/net/CookiesTest.java
+++ b/luni/src/test/java/libcore/java/net/CookiesTest.java
@@ -53,7 +53,7 @@
         server.enqueue(new MockResponse().addHeader("Set-Cookie: a=android; "
                 + "expires=Fri, 31-Dec-9999 23:59:59 GMT; "
                 + "path=/path; "
-                + "domain=.local; "
+                + "domain=" + server.getCookieDomain() + "; "
                 + "secure"));
         get(server, "/path/foo");
 
@@ -65,7 +65,7 @@
         assertEquals(null, cookie.getComment());
         assertEquals(null, cookie.getCommentURL());
         assertEquals(false, cookie.getDiscard());
-        assertEquals(".local", cookie.getDomain());
+        assertEquals(server.getCookieDomain(), cookie.getDomain());
         assertTrue(cookie.getMaxAge() > 100000000000L);
         assertEquals("/path", cookie.getPath());
         assertEquals(true, cookie.getSecure());
@@ -80,7 +80,7 @@
 
         server.enqueue(new MockResponse().addHeader("Set-Cookie: a=android; "
                 + "Comment=this cookie is delicious; "
-                + "Domain=.local; "
+                + "Domain=" + server.getCookieDomain() + "; "
                 + "Max-Age=60; "
                 + "Path=/path; "
                 + "Secure; "
@@ -95,7 +95,7 @@
         assertEquals("this cookie is delicious", cookie.getComment());
         assertEquals(null, cookie.getCommentURL());
         assertEquals(false, cookie.getDiscard());
-        assertEquals(".local", cookie.getDomain());
+        assertEquals(server.getCookieDomain(), cookie.getDomain());
         assertEquals(60, cookie.getMaxAge());
         assertEquals("/path", cookie.getPath());
         assertEquals(true, cookie.getSecure());
@@ -112,7 +112,7 @@
                 + "Comment=this cookie is delicious; "
                 + "CommentURL=http://google.com/; "
                 + "Discard; "
-                + "Domain=.local; "
+                + "Domain=" + server.getCookieDomain() + "; "
                 + "Max-Age=60; "
                 + "Path=/path; "
                 + "Port=\"80,443," + server.getPort() + "\"; "
@@ -128,7 +128,7 @@
         assertEquals("this cookie is delicious", cookie.getComment());
         assertEquals("http://google.com/", cookie.getCommentURL());
         assertEquals(true, cookie.getDiscard());
-        assertEquals(".local", cookie.getDomain());
+        assertEquals(server.getCookieDomain(), cookie.getDomain());
         assertEquals(60, cookie.getMaxAge());
         assertEquals("/path", cookie.getPath());
         assertEquals("80,443," + server.getPort(), cookie.getPortlist());
@@ -146,7 +146,7 @@
                 + "Comment=\"this cookie is delicious\"; "
                 + "CommentURL=\"http://google.com/\"; "
                 + "Discard; "
-                + "Domain=\".local\"; "
+                + "Domain=\"" + server.getCookieDomain() + "\"; "
                 + "Max-Age=\"60\"; "
                 + "Path=\"/path\"; "
                 + "Port=\"80,443," + server.getPort() + "\"; "
@@ -162,7 +162,7 @@
         assertEquals("this cookie is delicious", cookie.getComment());
         assertEquals("http://google.com/", cookie.getCommentURL());
         assertEquals(true, cookie.getDiscard());
-        assertEquals(".local", cookie.getDomain());
+        assertEquals(server.getCookieDomain(), cookie.getDomain());
         assertEquals(60, cookie.getMaxAge());
         assertEquals("/path", cookie.getPath());
         assertEquals("80,443," + server.getPort(), cookie.getPortlist());
@@ -250,11 +250,11 @@
 
         CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
         HttpCookie cookieA = new HttpCookie("a", "android");
-        cookieA.setDomain(".local");
+        cookieA.setDomain(server.getCookieDomain());
         cookieA.setPath("/");
         cookieManager.getCookieStore().add(server.getUrl("/").toURI(), cookieA);
         HttpCookie cookieB = new HttpCookie("b", "banana");
-        cookieB.setDomain(".local");
+        cookieB.setDomain(server.getCookieDomain());
         cookieB.setPath("/");
         cookieManager.getCookieStore().add(server.getUrl("/").toURI(), cookieB);
         CookieHandler.setDefault(cookieManager);
@@ -264,8 +264,8 @@
 
         List<String> receivedHeaders = request.getHeaders();
         assertContains(receivedHeaders, "Cookie: $Version=\"1\"; "
-                + "a=\"android\";$Path=\"/\";$Domain=\".local\"; "
-                + "b=\"banana\";$Path=\"/\";$Domain=\".local\"");
+                + "a=\"android\";$Path=\"/\";$Domain=\"" + server.getCookieDomain() + "\"; "
+                + "b=\"banana\";$Path=\"/\";$Domain=\"" + server.getCookieDomain() + "\"");
     }
 
     public void testRedirectsDoNotIncludeTooManyCookies() throws Exception {
@@ -281,7 +281,7 @@
 
         CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
         HttpCookie cookie = new HttpCookie("c", "cookie");
-        cookie.setDomain(".local");
+        cookie.setDomain(redirectSource.getCookieDomain());
         cookie.setPath("/");
         String portList = Integer.toString(redirectSource.getPort());
         cookie.setPortlist(portList);
@@ -292,7 +292,8 @@
         RecordedRequest request = redirectSource.takeRequest();
 
         assertContains(request.getHeaders(), "Cookie: $Version=\"1\"; "
-                + "c=\"cookie\";$Path=\"/\";$Domain=\".local\";$Port=\"" + portList + "\"");
+                + "c=\"cookie\";$Path=\"/\";$Domain=\"" + redirectSource.getCookieDomain()
+                + "\";$Port=\"" + portList + "\"");
 
         for (String header : redirectTarget.takeRequest().getHeaders()) {
             if (header.startsWith("Cookie")) {
@@ -308,7 +309,7 @@
      * manager should show up in the request and in {@code
      * getRequestProperties}.
      */
-    public void     testHeadersSentToCookieHandler() throws IOException, InterruptedException {
+    public void testHeadersSentToCookieHandler() throws IOException, InterruptedException {
         final Map<String, List<String>> cookieHandlerHeaders = new HashMap<String, List<String>>();
         CookieHandler.setDefault(new CookieManager() {
             @Override public Map<String, List<String>> get(URI uri,
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 36ea464..edca1a6 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -16,19 +16,13 @@
 
 package libcore.java.net;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.UnknownHostException;
 import java.util.Collections;
-import tests.util.SerializationTester;
+import libcore.util.SerializationTester;
 
 public class InetAddressTest extends junit.framework.TestCase {
     private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
@@ -138,11 +132,23 @@
 
     public void test_isReachable() throws Exception {
         // http://code.google.com/p/android/issues/detail?id=20203
-        InetAddress addr = SerializationTester.getDeserializedObject(InetAddress.getByName("www.google.com"));
-        addr.isReachable(500);
-        for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) {
-            addr.isReachable(nif, 20, 500);
-        }
+        String s = "aced0005737200146a6176612e6e65742e496e6574416464726573732d9b57af"
+                + "9fe3ebdb0200034900076164647265737349000666616d696c794c0008686f737"
+                + "44e616d657400124c6a6176612f6c616e672f537472696e673b78704a7d9d6300"
+                + "00000274000e7777772e676f6f676c652e636f6d";
+        InetAddress inetAddress = InetAddress.getByName("www.google.com");
+        new SerializationTester<InetAddress>(inetAddress, s) {
+            @Override protected void verify(InetAddress deserialized) throws Exception {
+                deserialized.isReachable(500);
+                for (NetworkInterface nif
+                        : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+                    deserialized.isReachable(nif, 20, 500);
+                }
+            }
+            @Override protected boolean equals(InetAddress a, InetAddress b) {
+                return a.getHostName().equals(b.getHostName());
+            }
+        }.test();
     }
 
     public void test_isSiteLocalAddress() throws Exception {
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index e796e1e..42b7250 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -248,6 +248,44 @@
         server.shutdown();
     }
 
+    // http://b/5534202
+    public void testAvailable() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            assertAvailableReturnsZeroAfterSocketReadsAllData();
+            System.out.println("Success on rep " + i);
+        }
+    }
+
+    private void assertAvailableReturnsZeroAfterSocketReadsAllData() throws Exception {
+        final byte[] data = "foo".getBytes();
+        final ServerSocket serverSocket = new ServerSocket(0);
+
+        new Thread() {
+            @Override public void run() {
+                try {
+                    Socket socket = serverSocket.accept();
+                    socket.getOutputStream().write(data);
+                    socket.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }.start();
+
+        Socket socket = new Socket("localhost", serverSocket.getLocalPort());
+        byte[] readBuffer = new byte[128];
+        InputStream in = socket.getInputStream();
+        int total = 0;
+        // to prevent available() from cheating after EOF, stop reading before -1 is returned
+        while (total < data.length) {
+            total += in.read(readBuffer);
+        }
+        assertEquals(0, in.available());
+
+        socket.close();
+        serverSocket.close();
+    }
+
     static class MockServer {
         private ExecutorService executor;
         private ServerSocket serverSocket;
diff --git a/luni/src/test/java/libcore/java/net/URITest.java b/luni/src/test/java/libcore/java/net/URITest.java
index d45facc..b37358c 100644
--- a/luni/src/test/java/libcore/java/net/URITest.java
+++ b/luni/src/test/java/libcore/java/net/URITest.java
@@ -19,7 +19,7 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public final class URITest extends TestCase {
 
@@ -67,7 +67,7 @@
                 + "77400124c6a6176612f6c616e672f537472696e673b787074002a687474703a2f2f757365723a706"
                 + "1737340686f73742f706174682f66696c653f7175657279236861736878";
         URI uri = new URI("http://user:pass@host/path/file?query#hash");
-        new SerializableTester<URI>(uri, s).test();
+        new SerializationTester<URI>(uri, s).test();
     }
 
     public void testEmptyHost() throws Exception {
@@ -621,5 +621,43 @@
         assertEquals(new URI(""), relative.relativize(relative));
     }
 
+    public void testPartContainsSpace() throws Exception {
+        try {
+            new URI("ht tp://host/");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http://user name@host/");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http://ho st/");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http://host:80 80/");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http://host/fi le");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http://host/file?que ry");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http://host/file?query#re f");
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+    }
+
     // Adding a new test? Consider adding an equivalent test to URLTest.java
 }
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index 1305a81..347242a 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -25,6 +25,7 @@
 import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
 import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -34,6 +35,7 @@
 import java.net.ConnectException;
 import java.net.HttpRetryException;
 import java.net.HttpURLConnection;
+import java.net.InetAddress;
 import java.net.PasswordAuthentication;
 import java.net.ProtocolException;
 import java.net.Proxy;
@@ -42,16 +44,24 @@
 import java.net.URI;
 import java.net.URL;
 import java.net.URLConnection;
+import java.net.UnknownHostException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.GZIPInputStream;
@@ -68,20 +78,12 @@
 import junit.framework.TestCase;
 import libcore.java.security.TestKeyStore;
 import libcore.javax.net.ssl.TestSSLContext;
+import libcore.net.http.HttpResponseCache;
 import tests.net.StuckServer;
 
 public final class URLConnectionTest extends TestCase {
-
-    private static final Authenticator SIMPLE_AUTHENTICATOR = new Authenticator() {
-        protected PasswordAuthentication getPasswordAuthentication() {
-            return new PasswordAuthentication("username", "password".toCharArray());
-        }
-    };
-
-    /** base64("username:password") */
-    private static final String BASE_64_CREDENTIALS = "dXNlcm5hbWU6cGFzc3dvcmQ=";
-
     private MockWebServer server = new MockWebServer();
+    private HttpResponseCache cache;
     private String hostName;
 
     @Override protected void setUp() throws Exception {
@@ -99,6 +101,9 @@
         System.clearProperty("https.proxyHost");
         System.clearProperty("https.proxyPort");
         server.shutdown();
+        if (cache != null) {
+            cache.getCache().delete();
+        }
         super.tearDown();
     }
 
@@ -275,24 +280,36 @@
         assertEquals(2, server.takeRequest().getSequenceNumber());
     }
 
+    /**
+     * Test that connections are added to the pool as soon as the response has
+     * been consumed.
+     */
+    public void testConnectionsArePooledWithoutExplicitDisconnect() throws Exception {
+        server.enqueue(new MockResponse().setBody("ABC"));
+        server.enqueue(new MockResponse().setBody("DEF"));
+        server.play();
+
+        URLConnection connection1 = server.getUrl("/").openConnection();
+        assertEquals("ABC", readAscii(connection1.getInputStream(), Integer.MAX_VALUE));
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+        URLConnection connection2 = server.getUrl("/").openConnection();
+        assertEquals("DEF", readAscii(connection2.getInputStream(), Integer.MAX_VALUE));
+        assertEquals(1, server.takeRequest().getSequenceNumber());
+    }
+
     public void testServerClosesSocket() throws Exception {
-        testServerClosesOutput(DISCONNECT_AT_END);
+        testServerClosesSocket(DISCONNECT_AT_END);
     }
 
     public void testServerShutdownInput() throws Exception {
-        testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
+        testServerClosesSocket(SHUTDOWN_INPUT_AT_END);
     }
 
-    public void testServerShutdownOutput() throws Exception {
-        testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
-    }
-
-    private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
+    private void testServerClosesSocket(SocketPolicy socketPolicy) throws Exception {
         server.enqueue(new MockResponse()
                 .setBody("This connection won't pool properly")
                 .setSocketPolicy(socketPolicy));
-        server.enqueue(new MockResponse()
-                .setBody("This comes after a busted connection"));
+        server.enqueue(new MockResponse().setBody("This comes after a busted connection"));
         server.play();
 
         assertContent("This connection won't pool properly", server.getUrl("/a").openConnection());
@@ -302,6 +319,55 @@
         assertEquals(0, server.takeRequest().getSequenceNumber());
     }
 
+    public void testServerShutdownOutput() throws Exception {
+        // This test causes MockWebServer to log a "connection failed" stack trace
+        server.enqueue(new MockResponse()
+                .setBody("Output shutdown after this response")
+                .setSocketPolicy(SHUTDOWN_OUTPUT_AT_END));
+        server.enqueue(new MockResponse().setBody("This response will fail to write"));
+        server.enqueue(new MockResponse().setBody("This comes after a busted connection"));
+        server.play();
+
+        assertContent("Output shutdown after this response", server.getUrl("/a").openConnection());
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+        assertContent("This comes after a busted connection", server.getUrl("/b").openConnection());
+        assertEquals(1, server.takeRequest().getSequenceNumber());
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+    }
+
+    public void testRetryableRequestBodyAfterBrokenConnection() throws Exception {
+        server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END));
+        server.enqueue(new MockResponse().setBody("def"));
+        server.play();
+
+        assertContent("abc", server.getUrl("/a").openConnection());
+        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection();
+        connection.setDoOutput(true);
+        OutputStream out = connection.getOutputStream();
+        out.write(new byte[] {1, 2, 3});
+        out.close();
+        assertContent("def", connection);
+    }
+
+    public void testNonRetryableRequestBodyAfterBrokenConnection() throws Exception {
+        server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END));
+        server.enqueue(new MockResponse().setBody("def"));
+        server.play();
+
+        assertContent("abc", server.getUrl("/a").openConnection());
+        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection();
+        connection.setDoOutput(true);
+        connection.setFixedLengthStreamingMode(3);
+        OutputStream out = connection.getOutputStream();
+        out.write(new byte[] {1, 2, 3});
+        out.close();
+        try {
+            connection.getInputStream();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
     enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }
 
     public void test_chunkedUpload_byteByByte() throws Exception {
@@ -396,10 +462,12 @@
 
         RecordedRequest request = server.takeRequest();
         assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
+        assertEquals("TLSv1", request.getSslProtocol());
     }
 
     public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException {
         TestSSLContext testSSLContext = TestSSLContext.create();
+        SSLSocketFactory clientSocketFactory = testSSLContext.clientContext.getSocketFactory();
 
         server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
         server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
@@ -407,11 +475,11 @@
         server.play();
 
         HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
-        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+        connection.setSSLSocketFactory(clientSocketFactory);
         assertContent("this response comes via HTTPS", connection);
 
         connection = (HttpsURLConnection) server.getUrl("/").openConnection();
-        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+        connection.setSSLSocketFactory(clientSocketFactory);
         assertContent("another response via HTTPS", connection);
 
         assertEquals(0, server.takeRequest().getSequenceNumber());
@@ -609,6 +677,50 @@
         assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
     }
 
+
+    /**
+     * Tolerate bad https proxy response when using HttpResponseCache. http://b/6754912
+     */
+    public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception {
+        ProxyConfig proxyConfig = ProxyConfig.PROXY_SYSTEM_PROPERTY;
+
+        TestSSLContext testSSLContext = TestSSLContext.create();
+
+        initResponseCache();
+
+        server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
+        server.enqueue(new MockResponse()
+                .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
+                .clearHeaders()
+                .setBody("bogus proxy connect response content")); // Key to reproducing b/6754912
+        server.play();
+
+        URL url = new URL("https://android.com/foo");
+        HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url);
+        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+
+        try {
+            connection.connect();
+            fail();
+        } catch (IOException expected) {
+            // Thrown when the connect causes SSLSocket.startHandshake() to throw
+            // when it sees the "bogus proxy connect response content"
+            // instead of a ServerHello handshake message.
+        }
+
+        RecordedRequest connect = server.takeRequest();
+        assertEquals("Connect line failure on proxy",
+                "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
+        assertContains(connect.getHeaders(), "Host: android.com");
+    }
+
+    private void initResponseCache() throws IOException {
+        String tmp = System.getProperty("java.io.tmpdir");
+        File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
+        cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
+        ResponseCache.setDefault(cache);
+    }
+
     /**
      * Test which headers are sent unencrypted to the HTTP proxy.
      */
@@ -647,7 +759,7 @@
     }
 
     public void testProxyAuthenticateOnConnect() throws Exception {
-        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
+        Authenticator.setDefault(new SimpleAuthenticator());
         TestSSLContext testSSLContext = TestSSLContext.create();
         server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
         server.enqueue(new MockResponse()
@@ -672,7 +784,8 @@
 
         RecordedRequest connect2 = server.takeRequest();
         assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine());
-        assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " + BASE_64_CREDENTIALS);
+        assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic "
+                + SimpleAuthenticator.BASE_64_CREDENTIALS);
 
         RecordedRequest get = server.takeRequest();
         assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
@@ -767,7 +880,7 @@
      */
     public void testUnauthorizedResponseHandling() throws IOException {
         MockResponse response = new MockResponse()
-                .addHeader("WWW-Authenticate: challenge")
+                .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
                 .setResponseCode(401) // UNAUTHORIZED
                 .setBody("Unauthorized");
         server.enqueue(response);
@@ -899,6 +1012,29 @@
     }
 
     /**
+     * Test that HEAD requests don't have a body regardless of the response
+     * headers. http://code.google.com/p/android/issues/detail?id=24672
+     */
+    public void testHeadAndContentLength() throws Exception {
+        server.enqueue(new MockResponse()
+                .clearHeaders()
+                .addHeader("Content-Length: 100"));
+        server.enqueue(new MockResponse().setBody("A"));
+        server.play();
+
+        HttpURLConnection connection1 = (HttpURLConnection) server.getUrl("/").openConnection();
+        connection1.setRequestMethod("HEAD");
+        assertEquals("100", connection1.getHeaderField("Content-Length"));
+        assertContent("", connection1);
+
+        HttpURLConnection connection2 = (HttpURLConnection) server.getUrl("/").openConnection();
+        assertEquals("A", readAscii(connection2.getInputStream(), Integer.MAX_VALUE));
+
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+        assertEquals(1, server.takeRequest().getSequenceNumber());
+    }
+
+    /**
      * Obnoxiously test that the chunk sizes transmitted exactly equal the
      * requested data+chunk header size. Although setChunkedStreamingMode()
      * isn't specific about whether the size applies to the data or the
@@ -936,7 +1072,7 @@
         server.enqueue(pleaseAuthenticate);
         server.play();
 
-        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
+        Authenticator.setDefault(new SimpleAuthenticator());
         HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
         connection.setDoOutput(true);
         byte[] requestBody = { 'A', 'B', 'C', 'D' };
@@ -1117,7 +1253,7 @@
         server.enqueue(new MockResponse().setBody("Successful auth!"));
         server.play();
 
-        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
+        Authenticator.setDefault(new SimpleAuthenticator());
         HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
         connection.setDoOutput(true);
         byte[] requestBody = { 'A', 'B', 'C', 'D' };
@@ -1128,13 +1264,14 @@
 
         // no authorization header for the first request...
         RecordedRequest request = server.takeRequest();
-        assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*");
+        assertContainsNoneMatching(request.getHeaders(), "Authorization: .*");
 
         // ...but the three requests that follow include an authorization header
         for (int i = 0; i < 3; i++) {
             request = server.takeRequest();
             assertEquals("POST / HTTP/1.1", request.getRequestLine());
-            assertContains(request.getHeaders(), "Authorization: Basic " + BASE_64_CREDENTIALS);
+            assertContains(request.getHeaders(), "Authorization: Basic "
+                    + SimpleAuthenticator.BASE_64_CREDENTIALS);
             assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody()));
         }
     }
@@ -1152,22 +1289,74 @@
         server.enqueue(new MockResponse().setBody("Successful auth!"));
         server.play();
 
-        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
+        SimpleAuthenticator authenticator = new SimpleAuthenticator();
+        Authenticator.setDefault(authenticator);
         HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
         assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+        assertEquals(Authenticator.RequestorType.SERVER, authenticator.requestorType);
+        assertEquals(server.getPort(), authenticator.requestingPort);
+        assertEquals(InetAddress.getByName(server.getHostName()), authenticator.requestingSite);
+        assertEquals("protected area", authenticator.requestingPrompt);
+        assertEquals("http", authenticator.requestingProtocol);
+        assertEquals("Basic", authenticator.requestingScheme);
 
         // no authorization header for the first request...
         RecordedRequest request = server.takeRequest();
-        assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*");
+        assertContainsNoneMatching(request.getHeaders(), "Authorization: .*");
 
         // ...but the three requests that follow requests include an authorization header
         for (int i = 0; i < 3; i++) {
             request = server.takeRequest();
             assertEquals("GET / HTTP/1.1", request.getRequestLine());
-            assertContains(request.getHeaders(), "Authorization: Basic " + BASE_64_CREDENTIALS);
+            assertContains(request.getHeaders(), "Authorization: Basic "
+                    + SimpleAuthenticator.BASE_64_CREDENTIALS);
         }
     }
 
+    // http://code.google.com/p/android/issues/detail?id=19081
+    public void testAuthenticateWithCommaSeparatedAuthenticationMethods() throws Exception {
+        server.enqueue(new MockResponse()
+                .setResponseCode(401)
+                .addHeader("WWW-Authenticate: Scheme1 realm=\"a\", Scheme2 realm=\"b\", "
+                        + "Scheme3 realm=\"c\"")
+                .setBody("Please authenticate."));
+        server.enqueue(new MockResponse().setBody("Successful auth!"));
+        server.play();
+
+        SimpleAuthenticator authenticator = new SimpleAuthenticator();
+        authenticator.expectedPrompt = "b";
+        Authenticator.setDefault(authenticator);
+        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+        assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+        assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*");
+        assertContains(server.takeRequest().getHeaders(),
+                "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS);
+        assertEquals("Scheme2", authenticator.requestingScheme);
+    }
+
+    public void testAuthenticateWithMultipleAuthenticationHeaders() throws Exception {
+        server.enqueue(new MockResponse()
+                .setResponseCode(401)
+                .addHeader("WWW-Authenticate: Scheme1 realm=\"a\"")
+                .addHeader("WWW-Authenticate: Scheme2 realm=\"b\"")
+                .addHeader("WWW-Authenticate: Scheme3 realm=\"c\"")
+                .setBody("Please authenticate."));
+        server.enqueue(new MockResponse().setBody("Successful auth!"));
+        server.play();
+
+        SimpleAuthenticator authenticator = new SimpleAuthenticator();
+        authenticator.expectedPrompt = "b";
+        Authenticator.setDefault(authenticator);
+        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+        assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+        assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*");
+        assertContains(server.takeRequest().getHeaders(),
+                "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS);
+        assertEquals("Scheme2", authenticator.requestingScheme);
+    }
+
     public void testRedirectedWithChunkedEncoding() throws Exception {
         testRedirected(TransferKind.CHUNKED, true);
     }
@@ -1378,6 +1567,11 @@
         }
     }
 
+    /**
+     * Test that the timeout period is honored. The timeout may be doubled!
+     * HttpURLConnection will wait the full timeout for each of the server's IP
+     * addresses. This is typically one IPv4 address and one IPv6 address.
+     */
     public void testConnectTimeouts() throws IOException {
         StuckServer ss = new StuckServer();
         int serverPort = ss.getLocalPort();
@@ -1389,8 +1583,10 @@
             urlConnection.getInputStream();
             fail();
         } catch (SocketTimeoutException expected) {
-            long actual = System.currentTimeMillis() - start;
-            assertTrue(Math.abs(timeout - actual) < 500);
+            long elapsed = System.currentTimeMillis() - start;
+            int attempts = InetAddress.getAllByName("localhost").length; // one per IP address
+            assertTrue("timeout=" +timeout + ", elapsed=" + elapsed + ", attempts=" + attempts,
+                    Math.abs((attempts * timeout) - elapsed) < 500);
         } finally {
             ss.close();
         }
@@ -1895,6 +2091,57 @@
         assertEquals(-1, in.read());
     }
 
+    // http://code.google.com/p/android/issues/detail?id=28095
+    public void testInvalidIpv4Address() throws Exception {
+        try {
+            URI uri = new URI("http://1111.111.111.111/index.html");
+            uri.toURL().openConnection().connect();
+            fail();
+        } catch (UnknownHostException expected) {
+        }
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=16895
+    public void testUrlWithSpaceInHost() throws Exception {
+        URLConnection urlConnection = new URL("http://and roid.com/").openConnection();
+        try {
+            urlConnection.getInputStream();
+            fail();
+        } catch (UnknownHostException expected) {
+        }
+    }
+
+    public void testUrlWithSpaceInHostViaHttpProxy() throws Exception {
+        server.enqueue(new MockResponse());
+        server.play();
+        URLConnection urlConnection = new URL("http://and roid.com/")
+                .openConnection(server.toProxyAddress());
+        try {
+            urlConnection.getInputStream();
+            fail(); // the RI makes a bogus proxy request for "GET http://and roid.com/ HTTP/1.1"
+        } catch (UnknownHostException expected) {
+        }
+    }
+
+    public void testSslFallback() throws Exception {
+        TestSSLContext testSSLContext = TestSSLContext.create();
+        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+        server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setBody("This required a 2nd handshake"));
+        server.play();
+
+        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
+        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+        assertEquals("This required a 2nd handshake",
+                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+        RecordedRequest first = server.takeRequest();
+        assertEquals(0, first.getSequenceNumber());
+        RecordedRequest retry = server.takeRequest();
+        assertEquals(0, retry.getSequenceNumber());
+        assertEquals("SSLv3", retry.getSslProtocol());
+    }
+
     public void testInspectSslBeforeConnect() throws Exception {
         TestSSLContext testSSLContext = TestSSLContext.create();
         server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
@@ -2101,4 +2348,29 @@
             return true;
         }
     }
+
+    private static class SimpleAuthenticator extends Authenticator {
+        /** base64("username:password") */
+        private static final String BASE_64_CREDENTIALS = "dXNlcm5hbWU6cGFzc3dvcmQ=";
+
+        private String expectedPrompt;
+        private RequestorType requestorType;
+        private int requestingPort;
+        private InetAddress requestingSite;
+        private String requestingPrompt;
+        private String requestingProtocol;
+        private String requestingScheme;
+
+        protected PasswordAuthentication getPasswordAuthentication() {
+            requestorType = getRequestorType();
+            requestingPort = getRequestingPort();
+            requestingSite = getRequestingSite();
+            requestingPrompt = getRequestingPrompt();
+            requestingProtocol = getRequestingProtocol();
+            requestingScheme = getRequestingScheme();
+            return (expectedPrompt == null || expectedPrompt.equals(requestingPrompt))
+                    ? new PasswordAuthentication("username", "password".toCharArray())
+                    : null;
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java
index af5915a..ced8314 100644
--- a/luni/src/test/java/libcore/java/net/URLTest.java
+++ b/luni/src/test/java/libcore/java/net/URLTest.java
@@ -21,7 +21,7 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public final class URLTest extends TestCase {
 
@@ -103,7 +103,7 @@
                 + "e00017870ffffffff74000e757365723a7061737340686f73747400102f706174682f66696c653f7"
                 + "175657279740004686f7374740004687474707400046861736878";
         URL url = new URL("http://user:pass@host/path/file?query#hash");
-        new SerializableTester<URL>(url, s).test();
+        new SerializationTester<URL>(url, s).test();
     }
 
     /**
@@ -119,7 +119,7 @@
                 + "47400102f706174682f66696c653f7175657279740004686f7374740004687474707400046861736"
                 + "878";
         final URL url = new URL("http://user:pass@host/path/file?query#hash");
-        new SerializableTester<URL>(url, s) {
+        new SerializationTester<URL>(url, s) {
             @Override protected void verify(URL deserialized) {
                 assertEquals(url.hashCode(), deserialized.hashCode());
             }
@@ -668,5 +668,23 @@
         assertEquals("", new URL("http", "host", -1, "", null).getPath());
     }
 
+    public void testPartContainsSpace() throws Exception {
+        try {
+            new URL("ht tp://host/");
+            fail();
+        } catch (MalformedURLException expected) {
+        }
+        assertEquals("user name", new URL("http://user name@host/").getUserInfo());
+        assertEquals("ho st", new URL("http://ho st/").getHost());
+        try {
+            new URL("http://host:80 80/");
+            fail();
+        } catch (MalformedURLException expected) {
+        }
+        assertEquals("/fi le", new URL("http://host/fi le").getFile());
+        assertEquals("que ry", new URL("http://host/file?que ry").getQuery());
+        assertEquals("re f", new URL("http://host/file?query#re f").getRef());
+    }
+
     // Adding a new test? Consider adding an equivalent test to URITest.java
 }
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 255db36..06a8e94 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -17,6 +17,7 @@
 package libcore.java.nio;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.nio.*;
 import java.nio.channels.*;
 import java.util.Arrays;
@@ -654,4 +655,24 @@
         CharSequence cs = cb.subSequence(0, cb.length());
         assertEquals("Hello", cs.toString());
     }
+
+    public void testHasArrayOnJniDirectByteBuffer() throws Exception {
+        // Simulate a call to JNI's NewDirectByteBuffer.
+        Class<?> c = Class.forName("java.nio.ReadWriteDirectByteBuffer");
+        Constructor<?> ctor = c.getDeclaredConstructor(int.class, int.class);
+        ctor.setAccessible(true);
+        ByteBuffer bb = (ByteBuffer) ctor.newInstance(0, 0);
+
+        try {
+            bb.array();
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+        try {
+            bb.arrayOffset();
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+        assertFalse(bb.hasArray());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
index aa958ad..f73b6d5 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -25,6 +25,8 @@
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
 import junit.framework.TestCase;
 import tests.net.StuckServer;
 
@@ -69,6 +71,25 @@
         }
     }
 
+    // http://b/6453247
+    // This test won't work on the host until/unless we start using libcorkscrew there.
+    // The runtime itself blocks SIGQUIT, so that doesn't cause poll(2) to EINTR directly.
+    // The EINTR is caused by the way libcorkscrew works.
+    public void testEINTR() throws Exception {
+        Selector selector = Selector.open();
+        new Thread(new Runnable() {
+            @Override public void run() {
+                try {
+                    Thread.sleep(2000);
+                    Libcore.os.kill(Libcore.os.getpid(), OsConstants.SIGQUIT);
+                } catch (Exception ex) {
+                    fail();
+                }
+            }
+        }).start();
+        assertEquals(0, selector.select());
+    }
+
     // http://code.google.com/p/android/issues/detail?id=15388
     public void testInterrupted() throws IOException {
         Selector selector = Selector.open();
diff --git a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
index be353cc..cc90147 100644
--- a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
@@ -16,17 +16,30 @@
 
 package libcore.java.security;
 
+import java.math.BigInteger;
 import java.security.Key;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
 import java.security.Provider;
+import java.security.Provider.Service;
+import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.Security;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
 import junit.framework.TestCase;
 
 public class KeyPairGeneratorTest extends TestCase {
@@ -135,5 +148,125 @@
         assertEquals(expectedAlgorithm, k.getAlgorithm().toUpperCase());
         assertNotNull(k.getEncoded());
         assertNotNull(k.getFormat());
+
+        test_KeyWithAllKeyFactories(k);
+    }
+
+    private void test_KeyWithAllKeyFactories(Key k) throws Exception {
+        byte[] encoded = k.getEncoded();
+
+        String keyAlgo = k.getAlgorithm();
+
+        Provider[] providers = Security.getProviders();
+        for (Provider p : providers) {
+            Set<Provider.Service> services = p.getServices();
+            for (Provider.Service service : services) {
+                if (!"KeyFactory".equals(service.getType())) {
+                    continue;
+                }
+                if (!service.getAlgorithm().equals(keyAlgo)) {
+                    continue;
+                }
+
+                if ("PKCS#8".equals(k.getFormat())) {
+                    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
+                    KeyFactory kf = KeyFactory.getInstance(k.getAlgorithm(), p);
+                    PrivateKey privKey = kf.generatePrivate(spec);
+                    assertNotNull(privKey);
+                    assertTrue(Arrays.equals(privKey.getEncoded(), encoded));
+                } else if ("X.509".equals(k.getFormat())) {
+                    X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);
+                    KeyFactory kf = KeyFactory.getInstance(k.getAlgorithm(), p);
+                    PublicKey pubKey = kf.generatePublic(spec);
+                    assertNotNull(pubKey);
+                    assertTrue(Arrays.equals(encoded, pubKey.getEncoded()));
+                }
+            }
+        }
+    }
+
+    private static final BigInteger DSA_P = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0x9e, (byte) 0x61, (byte) 0xc2, (byte) 0x89, (byte) 0xef, (byte) 0x77, (byte) 0xa9,
+        (byte) 0x4e, (byte) 0x13, (byte) 0x67, (byte) 0x64, (byte) 0x1f, (byte) 0x09, (byte) 0x01, (byte) 0xfe,
+        (byte) 0x24, (byte) 0x13, (byte) 0x53, (byte) 0xe0, (byte) 0xb7, (byte) 0x90, (byte) 0xa8, (byte) 0x4e,
+        (byte) 0x76, (byte) 0xfe, (byte) 0x89, (byte) 0x82, (byte) 0x7f, (byte) 0x7a, (byte) 0xc5, (byte) 0x3c,
+        (byte) 0x4e, (byte) 0x0c, (byte) 0x20, (byte) 0x55, (byte) 0x30, (byte) 0x95, (byte) 0x42, (byte) 0x85,
+        (byte) 0xe1, (byte) 0x40, (byte) 0x7d, (byte) 0x27, (byte) 0x8f, (byte) 0x07, (byte) 0x0d, (byte) 0xe8,
+        (byte) 0xdc, (byte) 0x99, (byte) 0xef, (byte) 0xb3, (byte) 0x07, (byte) 0x94, (byte) 0x34, (byte) 0xd6,
+        (byte) 0x7c, (byte) 0xff, (byte) 0x9c, (byte) 0xbe, (byte) 0x69, (byte) 0xd3, (byte) 0xeb, (byte) 0x44,
+        (byte) 0x37, (byte) 0x50, (byte) 0xef, (byte) 0x49, (byte) 0xf8, (byte) 0xe2, (byte) 0x5b, (byte) 0xd8,
+        (byte) 0xd1, (byte) 0x10, (byte) 0x84, (byte) 0x97, (byte) 0xea, (byte) 0xe3, (byte) 0xa5, (byte) 0x1c,
+        (byte) 0xc0, (byte) 0x4e, (byte) 0x69, (byte) 0xca, (byte) 0x70, (byte) 0x3d, (byte) 0x78, (byte) 0xb9,
+        (byte) 0x16, (byte) 0xe5, (byte) 0xfe, (byte) 0x61, (byte) 0x5d, (byte) 0x8a, (byte) 0x5a, (byte) 0xb3,
+        (byte) 0x2c, (byte) 0x61, (byte) 0xb6, (byte) 0x01, (byte) 0x3b, (byte) 0xd0, (byte) 0x01, (byte) 0x7c,
+        (byte) 0x32, (byte) 0x8d, (byte) 0xe1, (byte) 0xf3, (byte) 0x69, (byte) 0x0e, (byte) 0x8b, (byte) 0x58,
+        (byte) 0xc6, (byte) 0xcf, (byte) 0x00, (byte) 0x94, (byte) 0xf8, (byte) 0x49, (byte) 0x2a, (byte) 0x4b,
+        (byte) 0xea, (byte) 0xda, (byte) 0x00, (byte) 0xff, (byte) 0x4b, (byte) 0xd0, (byte) 0xbe, (byte) 0x40,
+        (byte) 0x23,
+    });
+
+    private static final BigInteger DSA_Q = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xbf, (byte) 0xee, (byte) 0xaa, (byte) 0x0f, (byte) 0x12, (byte) 0x34, (byte) 0x50,
+        (byte) 0x72, (byte) 0xf8, (byte) 0x60, (byte) 0x13, (byte) 0xd8, (byte) 0xf1, (byte) 0x41, (byte) 0x01,
+        (byte) 0x10, (byte) 0xa5, (byte) 0x2f, (byte) 0x57, (byte) 0x5f,
+    });
+
+    private static final BigInteger DSA_G = new BigInteger(new byte[] {
+        (byte) 0x77, (byte) 0xd4, (byte) 0x7a, (byte) 0x12, (byte) 0xcc, (byte) 0x81, (byte) 0x7e, (byte) 0x7e,
+        (byte) 0xeb, (byte) 0x3a, (byte) 0xfb, (byte) 0xe6, (byte) 0x86, (byte) 0x6d, (byte) 0x5a, (byte) 0x10,
+        (byte) 0x1d, (byte) 0xad, (byte) 0xa9, (byte) 0x4f, (byte) 0xb9, (byte) 0x03, (byte) 0x5d, (byte) 0x21,
+        (byte) 0x1a, (byte) 0xe4, (byte) 0x30, (byte) 0x95, (byte) 0x75, (byte) 0x8e, (byte) 0xcd, (byte) 0x5e,
+        (byte) 0xd1, (byte) 0xbd, (byte) 0x0a, (byte) 0x45, (byte) 0xee, (byte) 0xe7, (byte) 0xf7, (byte) 0x6b,
+        (byte) 0x65, (byte) 0x02, (byte) 0x60, (byte) 0xd0, (byte) 0x2e, (byte) 0xaf, (byte) 0x3d, (byte) 0xbc,
+        (byte) 0x07, (byte) 0xdd, (byte) 0x2b, (byte) 0x8e, (byte) 0x33, (byte) 0xc0, (byte) 0x93, (byte) 0x80,
+        (byte) 0xd9, (byte) 0x2b, (byte) 0xa7, (byte) 0x71, (byte) 0x57, (byte) 0x76, (byte) 0xbc, (byte) 0x8e,
+        (byte) 0xb9, (byte) 0xe0, (byte) 0xd7, (byte) 0xf4, (byte) 0x23, (byte) 0x8d, (byte) 0x41, (byte) 0x1a,
+        (byte) 0x97, (byte) 0x4f, (byte) 0x2c, (byte) 0x1b, (byte) 0xd5, (byte) 0x4b, (byte) 0x66, (byte) 0xe8,
+        (byte) 0xfa, (byte) 0xd2, (byte) 0x50, (byte) 0x0d, (byte) 0x17, (byte) 0xab, (byte) 0x34, (byte) 0x31,
+        (byte) 0x3d, (byte) 0xa4, (byte) 0x88, (byte) 0xd8, (byte) 0x8e, (byte) 0xa8, (byte) 0xa7, (byte) 0x6e,
+        (byte) 0x17, (byte) 0x03, (byte) 0xb7, (byte) 0x0f, (byte) 0x68, (byte) 0x7c, (byte) 0x64, (byte) 0x7b,
+        (byte) 0x92, (byte) 0xb8, (byte) 0x63, (byte) 0xe4, (byte) 0x9a, (byte) 0x67, (byte) 0x18, (byte) 0x81,
+        (byte) 0x27, (byte) 0xd4, (byte) 0x0b, (byte) 0x13, (byte) 0x48, (byte) 0xd3, (byte) 0x7d, (byte) 0x4e,
+        (byte) 0xf6, (byte) 0xa8, (byte) 0x8f, (byte) 0x56, (byte) 0x17, (byte) 0x2d, (byte) 0x08, (byte) 0x51,
+    });
+
+    public void testDSAGeneratorWithParams() throws Exception {
+        final DSAParameterSpec dsaSpec = new DSAParameterSpec(DSA_P, DSA_Q, DSA_G);
+
+        boolean failure = false;
+
+        final Provider[] providers = Security.getProviders();
+        for (final Provider p : providers) {
+            Service s = p.getService("KeyPairGenerator", "DSA");
+            if (s == null) {
+                continue;
+            }
+
+            final KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", p);
+            kpg.initialize(dsaSpec);
+            KeyPair pair = kpg.generateKeyPair();
+            DSAPrivateKey privKey = (DSAPrivateKey) pair.getPrivate();
+            DSAPublicKey pubKey = (DSAPublicKey) pair.getPublic();
+
+            DSAParams actualParams = privKey.getParams();
+            assertNotNull("DSA params should not be null", actualParams);
+
+            assertEquals("DSA P should be the same as supplied with provider " + p.getName(),
+                    DSA_P, actualParams.getP());
+            assertEquals("DSA Q should be the same as supplied with provider " + p.getName(),
+                    DSA_Q, actualParams.getQ());
+            assertEquals("DSA G should be the same as supplied with provider " + p.getName(),
+                    DSA_G, actualParams.getG());
+
+            actualParams = pubKey.getParams();
+            assertNotNull("DSA params should not be null", actualParams);
+
+            assertEquals("DSA P should be the same as supplied with provider " + p.getName(),
+                    DSA_P, actualParams.getP());
+            assertEquals("DSA Q should be the same as supplied with provider " + p.getName(),
+                    DSA_Q, actualParams.getQ());
+            assertEquals("DSA G should be the same as supplied with provider " + p.getName(),
+                    DSA_G, actualParams.getG());
+        }
     }
 }
diff --git a/luni/src/test/java/libcore/java/security/KeyStoreTest.java b/luni/src/test/java/libcore/java/security/KeyStoreTest.java
index 0a4d966..14d0987 100644
--- a/luni/src/test/java/libcore/java/security/KeyStoreTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyStoreTest.java
@@ -68,6 +68,10 @@
     private static final String ALIAS_ALT_CASE_CERTIFICATE = "cErTiFiCaTe";
     private static final String ALIAS_ALT_CASE_SECRET = "sEcRet";
 
+    private static final String ALIAS_UNICODE_PRIVATE = "\u6400\u7902\u3101\u8c02\u5002\u8702\udd01";
+    private static final String ALIAS_UNICODE_CERTIFICATE = "\u5402\udd01\u7902\u8702\u3101\u5f02\u3101\u5402\u5002\u8702\udd01";
+    private static final String ALIAS_UNICODE_SECRET = "\ue224\ud424\ud224\ue124\ud424\ue324";
+
     private static final String ALIAS_NO_PASSWORD_PRIVATE = "private-no-password";
     private static final String ALIAS_NO_PASSWORD_SECRET = "secret-no-password";
 
@@ -1208,7 +1212,7 @@
                 assertTrue(keyStore.aliases().hasMoreElements());
             } else {
                 assertEquals(Collections.EMPTY_SET,
-                             new HashSet(Collections.list(keyStore.aliases())));
+                        new HashSet(Collections.list(keyStore.aliases())));
             }
         }
 
@@ -1396,14 +1400,14 @@
             assertFalse(keyStore.isCertificateEntry(ALIAS_PRIVATE));
             assertFalse(keyStore.isCertificateEntry(ALIAS_SECRET));
             assertEquals(isCertificateEnabled(keyStore) && !isReadOnly(keyStore),
-                         keyStore.isCertificateEntry(ALIAS_CERTIFICATE));
+                    keyStore.isCertificateEntry(ALIAS_CERTIFICATE));
 
             assertFalse(keyStore.isCertificateEntry(ALIAS_ALT_CASE_PRIVATE));
             assertFalse(keyStore.isCertificateEntry(ALIAS_ALT_CASE_SECRET));
             assertEquals(!isCaseSensitive(keyStore)
-                         && isCertificateEnabled(keyStore)
-                         && !isReadOnly(keyStore),
-                         keyStore.isCertificateEntry(ALIAS_ALT_CASE_CERTIFICATE));
+                    && isCertificateEnabled(keyStore)
+                    && !isReadOnly(keyStore),
+                    keyStore.isCertificateEntry(ALIAS_ALT_CASE_CERTIFICATE));
         }
     }
 
@@ -1832,6 +1836,20 @@
                 } catch (KeyStoreException expected) {
                 }
             }
+            keyStore.setEntry(ALIAS_UNICODE_PRIVATE, getPrivateKey(), PARAM_KEY);
+            assertPrivateKey(keyStore.getKey(ALIAS_UNICODE_PRIVATE, PASSWORD_KEY));
+            assertCertificateChain(keyStore.getCertificateChain(ALIAS_UNICODE_PRIVATE));
+            if (isSecretKeyEnabled(keyStore)) {
+                assertNull(keyStore.getKey(ALIAS_UNICODE_SECRET, PASSWORD_KEY));
+                keyStore.setEntry(ALIAS_UNICODE_SECRET, new SecretKeyEntry(getSecretKey()), PARAM_KEY);
+                assertSecretKey(keyStore.getKey(ALIAS_UNICODE_SECRET, PASSWORD_KEY));
+            } else {
+                try {
+                    keyStore.setKeyEntry(ALIAS_UNICODE_SECRET, getSecretKey(), PASSWORD_KEY, null);
+                    fail();
+                } catch (KeyStoreException expected) {
+                }
+            }
         }
 
         for (KeyStore keyStore : keyStores()) {
@@ -1868,6 +1886,11 @@
                                       null);
                     assertCertificate(keyStore.getCertificate(ALIAS_CERTIFICATE));
                     assertCertificate2(keyStore.getCertificate(ALIAS_ALT_CASE_CERTIFICATE));
+                    keyStore.setEntry(ALIAS_UNICODE_CERTIFICATE,
+                                      new TrustedCertificateEntry(
+                                              getPrivateKey().getCertificate()),
+                                      null);
+                    assertCertificate(keyStore.getCertificate(ALIAS_UNICODE_CERTIFICATE));
                 }
             } else {
                 assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY));
@@ -1895,6 +1918,11 @@
                                       null);
                     assertCertificate2(keyStore.getCertificate(ALIAS_CERTIFICATE));
                     assertCertificate2(keyStore.getCertificate(ALIAS_ALT_CASE_CERTIFICATE));
+                    keyStore.setEntry(ALIAS_UNICODE_CERTIFICATE,
+                                      new TrustedCertificateEntry(
+                                              getPrivateKey().getCertificate()),
+                                      null);
+                    assertCertificate(keyStore.getCertificate(ALIAS_UNICODE_CERTIFICATE));
                 }
             }
         }
@@ -2183,4 +2211,24 @@
             }
         }
     }
+
+    // http://b/857840: want JKS key store
+    public void testDefaultKeystore() {
+        String type = KeyStore.getDefaultType();
+        assertEquals("Default keystore type must be Bouncy Castle", "BKS", type);
+
+        try {
+            KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
+            assertNotNull("Keystore must not be null", store);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+
+        try {
+            KeyStore store = KeyStore.getInstance("BKS");
+            assertNotNull("Keystore must not be null", store);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 75aada1..7364c93 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -16,14 +16,24 @@
 
 package libcore.java.security;
 
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
 import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
 import java.security.Provider;
 import java.security.PublicKey;
 import java.security.Security;
 import java.security.Signature;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
 import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -159,4 +169,860 @@
         sig.update(CONTENT);
         assertTrue(sig.verify(SIGNATURE));
     }
+
+    /*
+     * Test vectors generated with this private key:
+     *
+     * -----BEGIN RSA PRIVATE KEY-----
+     * MIIEpAIBAAKCAQEA4Ec+irjyKE/rnnQv+XSPoRjtmGM8kvUq63ouvg075gMpvnZq
+     * 0Q62pRXQ0s/ZvqeTDwwwZTeJn3lYzT6FsB+IGFJNMSWEqUslHjYltUFB7b/uGYgI
+     * 4buX/Hy0m56qr2jpyY19DtxTu8D6ADQ1bWMF+7zDxwAUBThqu8hzyw8+90JfPTPf
+     * ezFa4DbSoLZq/UdQOxab8247UWJRW3Ff2oPeryxYrrmr+zCXw8yd2dvl7ylsF2E5
+     * Ao6KZx5jBW1F9AGI0sQTNJCEXeUsJTTpxrJHjAe9rpKII7YtBmx3cPn2Pz26JH9T
+     * CER0e+eqqF2FO4vSRKzsPePImrRkU6tNJMOsaQIDAQABAoIBADd4R3al8XaY9ayW
+     * DfuDobZ1ZOZIvQWXz4q4CHGG8macJ6nsvdSA8Bl6gNBzCebGqW+SUzHlf4tKxvTU
+     * XtpFojJpwJ/EKMB6Tm7fc4oV3sl/q9Lyu0ehTyDqcvz+TDbgGtp3vRN82NTaELsW
+     * LpSkZilx8XX5hfoYjwVsuX7igW9Dq503R2Ekhs2owWGWwwgYqZXshdOEZ3kSZ7O/
+     * IfJzcQppJYYldoQcW2cSwS1L0govMpmtt8E12l6VFavadufK8qO+gFUdBzt4vxFi
+     * xIrSt/R0OgI47k0lL31efmUzzK5kzLOTYAdaL9HgNOw65c6cQIzL8OJeQRQCFoez
+     * 3UdUroECgYEA9UGIS8Nzeyki1BGe9F4t7izUy7dfRVBaFXqlAJ+Zxzot8HJKxGAk
+     * MGMy6omBd2NFRl3G3x4KbxQK/ztzluaomUrF2qloc0cv43dJ0U6z4HXmKdvrNYMz
+     * im82SdCiZUp6Qv2atr+krE1IHTkLsimwZL3DEcwb4bYxidp8QM3s8rECgYEA6hp0
+     * LduIHO23KIyH442GjdekCdFaQ/RF1Td6C1cx3b/KLa8oqOE81cCvzsM0fXSjniNa
+     * PNljPydN4rlPkt9DgzkR2enxz1jyfeLgj/RZZMcg0+whOdx8r8kSlTzeyy81Wi4s
+     * NaUPrXVMs7IxZkJLo7bjESoriYw4xcFe2yOGkzkCgYBRgo8exv2ZYCmQG68dfjN7
+     * pfCvJ+mE6tiVrOYr199O5FoiQInyzBUa880XP84EdLywTzhqLNzA4ANrokGfVFeS
+     * YtRxAL6TGYSj76Bb7PFBV03AebOpXEqD5sQ/MhTW3zLVEt4ZgIXlMeYWuD/X3Z0f
+     * TiYHwzM9B8VdEH0dOJNYcQKBgQDbT7UPUN6O21P/NMgJMYigUShn2izKBIl3WeWH
+     * wkQBDa+GZNWegIPRbBZHiTAfZ6nweAYNg0oq29NnV1toqKhCwrAqibPzH8zsiiL+
+     * OVeVxcbHQitOXXSh6ajzDndZufwtY5wfFWc+hOk6XvFQb0MVODw41Fy9GxQEj0ch
+     * 3IIyYQKBgQDYEUWTr0FfthLb8ZI3ENVNB0hiBadqO0MZSWjA3/HxHvD2GkozfV/T
+     * dBu8lkDkR7i2tsR8OsEgQ1fTsMVbqShr2nP2KSlvX6kUbYl2NX08dR51FIaWpAt0
+     * aFyCzjCQLWOdck/yTV4ulAfuNO3tLjtN9lqpvP623yjQe6aQPxZXaA==
+     * -----END RSA PRIVATE KEY-----
+     *
+     */
+
+    private static final BigInteger RSA_2048_modulus = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xe0, (byte) 0x47, (byte) 0x3e, (byte) 0x8a, (byte) 0xb8, (byte) 0xf2, (byte) 0x28,
+        (byte) 0x4f, (byte) 0xeb, (byte) 0x9e, (byte) 0x74, (byte) 0x2f, (byte) 0xf9, (byte) 0x74, (byte) 0x8f,
+        (byte) 0xa1, (byte) 0x18, (byte) 0xed, (byte) 0x98, (byte) 0x63, (byte) 0x3c, (byte) 0x92, (byte) 0xf5,
+        (byte) 0x2a, (byte) 0xeb, (byte) 0x7a, (byte) 0x2e, (byte) 0xbe, (byte) 0x0d, (byte) 0x3b, (byte) 0xe6,
+        (byte) 0x03, (byte) 0x29, (byte) 0xbe, (byte) 0x76, (byte) 0x6a, (byte) 0xd1, (byte) 0x0e, (byte) 0xb6,
+        (byte) 0xa5, (byte) 0x15, (byte) 0xd0, (byte) 0xd2, (byte) 0xcf, (byte) 0xd9, (byte) 0xbe, (byte) 0xa7,
+        (byte) 0x93, (byte) 0x0f, (byte) 0x0c, (byte) 0x30, (byte) 0x65, (byte) 0x37, (byte) 0x89, (byte) 0x9f,
+        (byte) 0x79, (byte) 0x58, (byte) 0xcd, (byte) 0x3e, (byte) 0x85, (byte) 0xb0, (byte) 0x1f, (byte) 0x88,
+        (byte) 0x18, (byte) 0x52, (byte) 0x4d, (byte) 0x31, (byte) 0x25, (byte) 0x84, (byte) 0xa9, (byte) 0x4b,
+        (byte) 0x25, (byte) 0x1e, (byte) 0x36, (byte) 0x25, (byte) 0xb5, (byte) 0x41, (byte) 0x41, (byte) 0xed,
+        (byte) 0xbf, (byte) 0xee, (byte) 0x19, (byte) 0x88, (byte) 0x08, (byte) 0xe1, (byte) 0xbb, (byte) 0x97,
+        (byte) 0xfc, (byte) 0x7c, (byte) 0xb4, (byte) 0x9b, (byte) 0x9e, (byte) 0xaa, (byte) 0xaf, (byte) 0x68,
+        (byte) 0xe9, (byte) 0xc9, (byte) 0x8d, (byte) 0x7d, (byte) 0x0e, (byte) 0xdc, (byte) 0x53, (byte) 0xbb,
+        (byte) 0xc0, (byte) 0xfa, (byte) 0x00, (byte) 0x34, (byte) 0x35, (byte) 0x6d, (byte) 0x63, (byte) 0x05,
+        (byte) 0xfb, (byte) 0xbc, (byte) 0xc3, (byte) 0xc7, (byte) 0x00, (byte) 0x14, (byte) 0x05, (byte) 0x38,
+        (byte) 0x6a, (byte) 0xbb, (byte) 0xc8, (byte) 0x73, (byte) 0xcb, (byte) 0x0f, (byte) 0x3e, (byte) 0xf7,
+        (byte) 0x42, (byte) 0x5f, (byte) 0x3d, (byte) 0x33, (byte) 0xdf, (byte) 0x7b, (byte) 0x31, (byte) 0x5a,
+        (byte) 0xe0, (byte) 0x36, (byte) 0xd2, (byte) 0xa0, (byte) 0xb6, (byte) 0x6a, (byte) 0xfd, (byte) 0x47,
+        (byte) 0x50, (byte) 0x3b, (byte) 0x16, (byte) 0x9b, (byte) 0xf3, (byte) 0x6e, (byte) 0x3b, (byte) 0x51,
+        (byte) 0x62, (byte) 0x51, (byte) 0x5b, (byte) 0x71, (byte) 0x5f, (byte) 0xda, (byte) 0x83, (byte) 0xde,
+        (byte) 0xaf, (byte) 0x2c, (byte) 0x58, (byte) 0xae, (byte) 0xb9, (byte) 0xab, (byte) 0xfb, (byte) 0x30,
+        (byte) 0x97, (byte) 0xc3, (byte) 0xcc, (byte) 0x9d, (byte) 0xd9, (byte) 0xdb, (byte) 0xe5, (byte) 0xef,
+        (byte) 0x29, (byte) 0x6c, (byte) 0x17, (byte) 0x61, (byte) 0x39, (byte) 0x02, (byte) 0x8e, (byte) 0x8a,
+        (byte) 0x67, (byte) 0x1e, (byte) 0x63, (byte) 0x05, (byte) 0x6d, (byte) 0x45, (byte) 0xf4, (byte) 0x01,
+        (byte) 0x88, (byte) 0xd2, (byte) 0xc4, (byte) 0x13, (byte) 0x34, (byte) 0x90, (byte) 0x84, (byte) 0x5d,
+        (byte) 0xe5, (byte) 0x2c, (byte) 0x25, (byte) 0x34, (byte) 0xe9, (byte) 0xc6, (byte) 0xb2, (byte) 0x47,
+        (byte) 0x8c, (byte) 0x07, (byte) 0xbd, (byte) 0xae, (byte) 0x92, (byte) 0x88, (byte) 0x23, (byte) 0xb6,
+        (byte) 0x2d, (byte) 0x06, (byte) 0x6c, (byte) 0x77, (byte) 0x70, (byte) 0xf9, (byte) 0xf6, (byte) 0x3f,
+        (byte) 0x3d, (byte) 0xba, (byte) 0x24, (byte) 0x7f, (byte) 0x53, (byte) 0x08, (byte) 0x44, (byte) 0x74,
+        (byte) 0x7b, (byte) 0xe7, (byte) 0xaa, (byte) 0xa8, (byte) 0x5d, (byte) 0x85, (byte) 0x3b, (byte) 0x8b,
+        (byte) 0xd2, (byte) 0x44, (byte) 0xac, (byte) 0xec, (byte) 0x3d, (byte) 0xe3, (byte) 0xc8, (byte) 0x9a,
+        (byte) 0xb4, (byte) 0x64, (byte) 0x53, (byte) 0xab, (byte) 0x4d, (byte) 0x24, (byte) 0xc3, (byte) 0xac,
+        (byte) 0x69,
+    });
+
+    private static final BigInteger RSA_2048_privateExponent = new BigInteger(new byte[] {
+        (byte) 0x37, (byte) 0x78, (byte) 0x47, (byte) 0x76, (byte) 0xa5, (byte) 0xf1, (byte) 0x76, (byte) 0x98,
+        (byte) 0xf5, (byte) 0xac, (byte) 0x96, (byte) 0x0d, (byte) 0xfb, (byte) 0x83, (byte) 0xa1, (byte) 0xb6,
+        (byte) 0x75, (byte) 0x64, (byte) 0xe6, (byte) 0x48, (byte) 0xbd, (byte) 0x05, (byte) 0x97, (byte) 0xcf,
+        (byte) 0x8a, (byte) 0xb8, (byte) 0x08, (byte) 0x71, (byte) 0x86, (byte) 0xf2, (byte) 0x66, (byte) 0x9c,
+        (byte) 0x27, (byte) 0xa9, (byte) 0xec, (byte) 0xbd, (byte) 0xd4, (byte) 0x80, (byte) 0xf0, (byte) 0x19,
+        (byte) 0x7a, (byte) 0x80, (byte) 0xd0, (byte) 0x73, (byte) 0x09, (byte) 0xe6, (byte) 0xc6, (byte) 0xa9,
+        (byte) 0x6f, (byte) 0x92, (byte) 0x53, (byte) 0x31, (byte) 0xe5, (byte) 0x7f, (byte) 0x8b, (byte) 0x4a,
+        (byte) 0xc6, (byte) 0xf4, (byte) 0xd4, (byte) 0x5e, (byte) 0xda, (byte) 0x45, (byte) 0xa2, (byte) 0x32,
+        (byte) 0x69, (byte) 0xc0, (byte) 0x9f, (byte) 0xc4, (byte) 0x28, (byte) 0xc0, (byte) 0x7a, (byte) 0x4e,
+        (byte) 0x6e, (byte) 0xdf, (byte) 0x73, (byte) 0x8a, (byte) 0x15, (byte) 0xde, (byte) 0xc9, (byte) 0x7f,
+        (byte) 0xab, (byte) 0xd2, (byte) 0xf2, (byte) 0xbb, (byte) 0x47, (byte) 0xa1, (byte) 0x4f, (byte) 0x20,
+        (byte) 0xea, (byte) 0x72, (byte) 0xfc, (byte) 0xfe, (byte) 0x4c, (byte) 0x36, (byte) 0xe0, (byte) 0x1a,
+        (byte) 0xda, (byte) 0x77, (byte) 0xbd, (byte) 0x13, (byte) 0x7c, (byte) 0xd8, (byte) 0xd4, (byte) 0xda,
+        (byte) 0x10, (byte) 0xbb, (byte) 0x16, (byte) 0x2e, (byte) 0x94, (byte) 0xa4, (byte) 0x66, (byte) 0x29,
+        (byte) 0x71, (byte) 0xf1, (byte) 0x75, (byte) 0xf9, (byte) 0x85, (byte) 0xfa, (byte) 0x18, (byte) 0x8f,
+        (byte) 0x05, (byte) 0x6c, (byte) 0xb9, (byte) 0x7e, (byte) 0xe2, (byte) 0x81, (byte) 0x6f, (byte) 0x43,
+        (byte) 0xab, (byte) 0x9d, (byte) 0x37, (byte) 0x47, (byte) 0x61, (byte) 0x24, (byte) 0x86, (byte) 0xcd,
+        (byte) 0xa8, (byte) 0xc1, (byte) 0x61, (byte) 0x96, (byte) 0xc3, (byte) 0x08, (byte) 0x18, (byte) 0xa9,
+        (byte) 0x95, (byte) 0xec, (byte) 0x85, (byte) 0xd3, (byte) 0x84, (byte) 0x67, (byte) 0x79, (byte) 0x12,
+        (byte) 0x67, (byte) 0xb3, (byte) 0xbf, (byte) 0x21, (byte) 0xf2, (byte) 0x73, (byte) 0x71, (byte) 0x0a,
+        (byte) 0x69, (byte) 0x25, (byte) 0x86, (byte) 0x25, (byte) 0x76, (byte) 0x84, (byte) 0x1c, (byte) 0x5b,
+        (byte) 0x67, (byte) 0x12, (byte) 0xc1, (byte) 0x2d, (byte) 0x4b, (byte) 0xd2, (byte) 0x0a, (byte) 0x2f,
+        (byte) 0x32, (byte) 0x99, (byte) 0xad, (byte) 0xb7, (byte) 0xc1, (byte) 0x35, (byte) 0xda, (byte) 0x5e,
+        (byte) 0x95, (byte) 0x15, (byte) 0xab, (byte) 0xda, (byte) 0x76, (byte) 0xe7, (byte) 0xca, (byte) 0xf2,
+        (byte) 0xa3, (byte) 0xbe, (byte) 0x80, (byte) 0x55, (byte) 0x1d, (byte) 0x07, (byte) 0x3b, (byte) 0x78,
+        (byte) 0xbf, (byte) 0x11, (byte) 0x62, (byte) 0xc4, (byte) 0x8a, (byte) 0xd2, (byte) 0xb7, (byte) 0xf4,
+        (byte) 0x74, (byte) 0x3a, (byte) 0x02, (byte) 0x38, (byte) 0xee, (byte) 0x4d, (byte) 0x25, (byte) 0x2f,
+        (byte) 0x7d, (byte) 0x5e, (byte) 0x7e, (byte) 0x65, (byte) 0x33, (byte) 0xcc, (byte) 0xae, (byte) 0x64,
+        (byte) 0xcc, (byte) 0xb3, (byte) 0x93, (byte) 0x60, (byte) 0x07, (byte) 0x5a, (byte) 0x2f, (byte) 0xd1,
+        (byte) 0xe0, (byte) 0x34, (byte) 0xec, (byte) 0x3a, (byte) 0xe5, (byte) 0xce, (byte) 0x9c, (byte) 0x40,
+        (byte) 0x8c, (byte) 0xcb, (byte) 0xf0, (byte) 0xe2, (byte) 0x5e, (byte) 0x41, (byte) 0x14, (byte) 0x02,
+        (byte) 0x16, (byte) 0x87, (byte) 0xb3, (byte) 0xdd, (byte) 0x47, (byte) 0x54, (byte) 0xae, (byte) 0x81,
+    });
+
+    private static final BigInteger RSA_2048_publicExponent = new BigInteger(new byte[] {
+        (byte) 0x01, (byte) 0x00, (byte) 0x01,
+    });
+
+    private static final BigInteger RSA_2048_primeP = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xf5, (byte) 0x41, (byte) 0x88, (byte) 0x4b, (byte) 0xc3, (byte) 0x73, (byte) 0x7b,
+        (byte) 0x29, (byte) 0x22, (byte) 0xd4, (byte) 0x11, (byte) 0x9e, (byte) 0xf4, (byte) 0x5e, (byte) 0x2d,
+        (byte) 0xee, (byte) 0x2c, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x5f, (byte) 0x45, (byte) 0x50,
+        (byte) 0x5a, (byte) 0x15, (byte) 0x7a, (byte) 0xa5, (byte) 0x00, (byte) 0x9f, (byte) 0x99, (byte) 0xc7,
+        (byte) 0x3a, (byte) 0x2d, (byte) 0xf0, (byte) 0x72, (byte) 0x4a, (byte) 0xc4, (byte) 0x60, (byte) 0x24,
+        (byte) 0x30, (byte) 0x63, (byte) 0x32, (byte) 0xea, (byte) 0x89, (byte) 0x81, (byte) 0x77, (byte) 0x63,
+        (byte) 0x45, (byte) 0x46, (byte) 0x5d, (byte) 0xc6, (byte) 0xdf, (byte) 0x1e, (byte) 0x0a, (byte) 0x6f,
+        (byte) 0x14, (byte) 0x0a, (byte) 0xff, (byte) 0x3b, (byte) 0x73, (byte) 0x96, (byte) 0xe6, (byte) 0xa8,
+        (byte) 0x99, (byte) 0x4a, (byte) 0xc5, (byte) 0xda, (byte) 0xa9, (byte) 0x68, (byte) 0x73, (byte) 0x47,
+        (byte) 0x2f, (byte) 0xe3, (byte) 0x77, (byte) 0x49, (byte) 0xd1, (byte) 0x4e, (byte) 0xb3, (byte) 0xe0,
+        (byte) 0x75, (byte) 0xe6, (byte) 0x29, (byte) 0xdb, (byte) 0xeb, (byte) 0x35, (byte) 0x83, (byte) 0x33,
+        (byte) 0x8a, (byte) 0x6f, (byte) 0x36, (byte) 0x49, (byte) 0xd0, (byte) 0xa2, (byte) 0x65, (byte) 0x4a,
+        (byte) 0x7a, (byte) 0x42, (byte) 0xfd, (byte) 0x9a, (byte) 0xb6, (byte) 0xbf, (byte) 0xa4, (byte) 0xac,
+        (byte) 0x4d, (byte) 0x48, (byte) 0x1d, (byte) 0x39, (byte) 0x0b, (byte) 0xb2, (byte) 0x29, (byte) 0xb0,
+        (byte) 0x64, (byte) 0xbd, (byte) 0xc3, (byte) 0x11, (byte) 0xcc, (byte) 0x1b, (byte) 0xe1, (byte) 0xb6,
+        (byte) 0x31, (byte) 0x89, (byte) 0xda, (byte) 0x7c, (byte) 0x40, (byte) 0xcd, (byte) 0xec, (byte) 0xf2,
+        (byte) 0xb1,
+    });
+
+    private static final BigInteger RSA_2048_primeQ = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xea, (byte) 0x1a, (byte) 0x74, (byte) 0x2d, (byte) 0xdb, (byte) 0x88, (byte) 0x1c,
+        (byte) 0xed, (byte) 0xb7, (byte) 0x28, (byte) 0x8c, (byte) 0x87, (byte) 0xe3, (byte) 0x8d, (byte) 0x86,
+        (byte) 0x8d, (byte) 0xd7, (byte) 0xa4, (byte) 0x09, (byte) 0xd1, (byte) 0x5a, (byte) 0x43, (byte) 0xf4,
+        (byte) 0x45, (byte) 0xd5, (byte) 0x37, (byte) 0x7a, (byte) 0x0b, (byte) 0x57, (byte) 0x31, (byte) 0xdd,
+        (byte) 0xbf, (byte) 0xca, (byte) 0x2d, (byte) 0xaf, (byte) 0x28, (byte) 0xa8, (byte) 0xe1, (byte) 0x3c,
+        (byte) 0xd5, (byte) 0xc0, (byte) 0xaf, (byte) 0xce, (byte) 0xc3, (byte) 0x34, (byte) 0x7d, (byte) 0x74,
+        (byte) 0xa3, (byte) 0x9e, (byte) 0x23, (byte) 0x5a, (byte) 0x3c, (byte) 0xd9, (byte) 0x63, (byte) 0x3f,
+        (byte) 0x27, (byte) 0x4d, (byte) 0xe2, (byte) 0xb9, (byte) 0x4f, (byte) 0x92, (byte) 0xdf, (byte) 0x43,
+        (byte) 0x83, (byte) 0x39, (byte) 0x11, (byte) 0xd9, (byte) 0xe9, (byte) 0xf1, (byte) 0xcf, (byte) 0x58,
+        (byte) 0xf2, (byte) 0x7d, (byte) 0xe2, (byte) 0xe0, (byte) 0x8f, (byte) 0xf4, (byte) 0x59, (byte) 0x64,
+        (byte) 0xc7, (byte) 0x20, (byte) 0xd3, (byte) 0xec, (byte) 0x21, (byte) 0x39, (byte) 0xdc, (byte) 0x7c,
+        (byte) 0xaf, (byte) 0xc9, (byte) 0x12, (byte) 0x95, (byte) 0x3c, (byte) 0xde, (byte) 0xcb, (byte) 0x2f,
+        (byte) 0x35, (byte) 0x5a, (byte) 0x2e, (byte) 0x2c, (byte) 0x35, (byte) 0xa5, (byte) 0x0f, (byte) 0xad,
+        (byte) 0x75, (byte) 0x4c, (byte) 0xb3, (byte) 0xb2, (byte) 0x31, (byte) 0x66, (byte) 0x42, (byte) 0x4b,
+        (byte) 0xa3, (byte) 0xb6, (byte) 0xe3, (byte) 0x11, (byte) 0x2a, (byte) 0x2b, (byte) 0x89, (byte) 0x8c,
+        (byte) 0x38, (byte) 0xc5, (byte) 0xc1, (byte) 0x5e, (byte) 0xdb, (byte) 0x23, (byte) 0x86, (byte) 0x93,
+        (byte) 0x39,
+    });
+
+    private static final byte[] Vector1Data = new byte[] {
+        (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x2e,
+        (byte) 0x0a,
+    };
+
+    private static final byte[] SHA1withRSA_Vector1Signature = {
+        (byte) 0x6d, (byte) 0x5b, (byte) 0xff, (byte) 0x68, (byte) 0xda, (byte) 0x18, (byte) 0x98, (byte) 0x72,
+        (byte) 0x5c, (byte) 0x1f, (byte) 0x46, (byte) 0x51, (byte) 0x77, (byte) 0x15, (byte) 0x11, (byte) 0xcb,
+        (byte) 0xe0, (byte) 0xb9, (byte) 0x3b, (byte) 0x7d, (byte) 0xf5, (byte) 0x96, (byte) 0x98, (byte) 0x24,
+        (byte) 0x85, (byte) 0x9d, (byte) 0x3e, (byte) 0xed, (byte) 0x9b, (byte) 0xb2, (byte) 0x8a, (byte) 0x91,
+        (byte) 0xfb, (byte) 0xf6, (byte) 0x85, (byte) 0x64, (byte) 0x74, (byte) 0x18, (byte) 0xb5, (byte) 0x1c,
+        (byte) 0xb3, (byte) 0x8d, (byte) 0x99, (byte) 0x0d, (byte) 0xdf, (byte) 0xaa, (byte) 0xa6, (byte) 0xa1,
+        (byte) 0xc3, (byte) 0xb6, (byte) 0x25, (byte) 0xb3, (byte) 0x06, (byte) 0xe0, (byte) 0xef, (byte) 0x28,
+        (byte) 0xb0, (byte) 0x4d, (byte) 0x50, (byte) 0xc7, (byte) 0x75, (byte) 0x39, (byte) 0xb9, (byte) 0x2c,
+        (byte) 0x47, (byte) 0xb5, (byte) 0xe2, (byte) 0x96, (byte) 0xf8, (byte) 0xf6, (byte) 0xcb, (byte) 0xa0,
+        (byte) 0x58, (byte) 0xc9, (byte) 0x3e, (byte) 0xd5, (byte) 0xfc, (byte) 0x26, (byte) 0xd9, (byte) 0x55,
+        (byte) 0x73, (byte) 0x39, (byte) 0x75, (byte) 0xb3, (byte) 0xb0, (byte) 0x0a, (byte) 0x5f, (byte) 0x5e,
+        (byte) 0x3b, (byte) 0x4a, (byte) 0x2e, (byte) 0xb1, (byte) 0x0e, (byte) 0x7d, (byte) 0xe5, (byte) 0xcc,
+        (byte) 0x04, (byte) 0x2c, (byte) 0xd1, (byte) 0x0a, (byte) 0x32, (byte) 0xaa, (byte) 0xd9, (byte) 0x8d,
+        (byte) 0x1f, (byte) 0xcb, (byte) 0xe3, (byte) 0x7f, (byte) 0x63, (byte) 0x12, (byte) 0xb1, (byte) 0x98,
+        (byte) 0x46, (byte) 0x46, (byte) 0x07, (byte) 0xd9, (byte) 0x49, (byte) 0xd2, (byte) 0xbf, (byte) 0xb5,
+        (byte) 0xbc, (byte) 0xbb, (byte) 0xfd, (byte) 0x1c, (byte) 0xd7, (byte) 0x11, (byte) 0x94, (byte) 0xaa,
+        (byte) 0x5f, (byte) 0x7b, (byte) 0xb2, (byte) 0x0c, (byte) 0x5d, (byte) 0x94, (byte) 0x53, (byte) 0x5e,
+        (byte) 0x81, (byte) 0x5c, (byte) 0xbb, (byte) 0x1d, (byte) 0x4f, (byte) 0x30, (byte) 0xcd, (byte) 0xf8,
+        (byte) 0xd7, (byte) 0xa5, (byte) 0xfa, (byte) 0x5e, (byte) 0xe0, (byte) 0x19, (byte) 0x3f, (byte) 0xa4,
+        (byte) 0xaa, (byte) 0x56, (byte) 0x4e, (byte) 0xec, (byte) 0xeb, (byte) 0xee, (byte) 0xa2, (byte) 0x6c,
+        (byte) 0xc9, (byte) 0x4f, (byte) 0xc2, (byte) 0xcc, (byte) 0x2a, (byte) 0xbc, (byte) 0x5b, (byte) 0x09,
+        (byte) 0x10, (byte) 0x73, (byte) 0x61, (byte) 0x0c, (byte) 0x04, (byte) 0xb6, (byte) 0xb7, (byte) 0x2c,
+        (byte) 0x37, (byte) 0xd2, (byte) 0xca, (byte) 0x2d, (byte) 0x54, (byte) 0xf2, (byte) 0xf7, (byte) 0x77,
+        (byte) 0xe1, (byte) 0xba, (byte) 0x9f, (byte) 0x29, (byte) 0x07, (byte) 0xa2, (byte) 0x74, (byte) 0xc6,
+        (byte) 0xe9, (byte) 0x1e, (byte) 0xde, (byte) 0xd7, (byte) 0x9c, (byte) 0x4b, (byte) 0xb7, (byte) 0x66,
+        (byte) 0x52, (byte) 0xe8, (byte) 0xac, (byte) 0xf6, (byte) 0x76, (byte) 0xab, (byte) 0x16, (byte) 0x82,
+        (byte) 0x96, (byte) 0x87, (byte) 0x40, (byte) 0x0f, (byte) 0xad, (byte) 0x2d, (byte) 0x46, (byte) 0xa6,
+        (byte) 0x28, (byte) 0x04, (byte) 0x13, (byte) 0xc2, (byte) 0xce, (byte) 0x50, (byte) 0x56, (byte) 0x6d,
+        (byte) 0xbe, (byte) 0x0c, (byte) 0x91, (byte) 0xd0, (byte) 0x8e, (byte) 0x80, (byte) 0x9e, (byte) 0x91,
+        (byte) 0x8f, (byte) 0x62, (byte) 0xb3, (byte) 0x57, (byte) 0xd6, (byte) 0xae, (byte) 0x53, (byte) 0x91,
+        (byte) 0x83, (byte) 0xe9, (byte) 0x38, (byte) 0x77, (byte) 0x8f, (byte) 0x20, (byte) 0xdd, (byte) 0x13,
+        (byte) 0x7d, (byte) 0x15, (byte) 0x44, (byte) 0x7e, (byte) 0xb5, (byte) 0x00, (byte) 0xd6, (byte) 0x45,
+    };
+
+    private static final byte[] Vector2Data = new byte[] {
+        (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20,
+        (byte) 0x61, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x67, (byte) 0x6e, (byte) 0x65, (byte) 0x64,
+        (byte) 0x20, (byte) 0x6d, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x61, (byte) 0x67, (byte) 0x65,
+        (byte) 0x20, (byte) 0x66, (byte) 0x72, (byte) 0x6f, (byte) 0x6d, (byte) 0x20, (byte) 0x4b, (byte) 0x65,
+        (byte) 0x6e, (byte) 0x6e, (byte) 0x79, (byte) 0x20, (byte) 0x52, (byte) 0x6f, (byte) 0x6f, (byte) 0x74,
+        (byte) 0x2e, (byte) 0x0a,
+    };
+
+    private static final byte[] SHA1withRSA_Vector2Signature = new byte[] {
+        (byte) 0x2e, (byte) 0xa6, (byte) 0x33, (byte) 0xd1, (byte) 0x9d, (byte) 0xfc, (byte) 0x4e, (byte) 0x27,
+        (byte) 0xb3, (byte) 0xa8, (byte) 0x9a, (byte) 0xf2, (byte) 0x48, (byte) 0x62, (byte) 0x15, (byte) 0xa2,
+        (byte) 0xce, (byte) 0x5f, (byte) 0x2b, (byte) 0x0e, (byte) 0xc5, (byte) 0x26, (byte) 0xba, (byte) 0xd9,
+        (byte) 0x0f, (byte) 0x60, (byte) 0xeb, (byte) 0xf0, (byte) 0xd5, (byte) 0x5c, (byte) 0x6b, (byte) 0x23,
+        (byte) 0x11, (byte) 0x95, (byte) 0xa4, (byte) 0xbd, (byte) 0x11, (byte) 0x68, (byte) 0xe7, (byte) 0x3a,
+        (byte) 0x37, (byte) 0x3d, (byte) 0x79, (byte) 0xb8, (byte) 0x4f, (byte) 0xe9, (byte) 0xa1, (byte) 0x88,
+        (byte) 0xfb, (byte) 0xa9, (byte) 0x8b, (byte) 0x34, (byte) 0xa1, (byte) 0xe0, (byte) 0xca, (byte) 0x11,
+        (byte) 0xdd, (byte) 0xd0, (byte) 0x83, (byte) 0x7f, (byte) 0xc1, (byte) 0x0b, (byte) 0x16, (byte) 0x61,
+        (byte) 0xac, (byte) 0x09, (byte) 0xa2, (byte) 0xdd, (byte) 0x40, (byte) 0x5b, (byte) 0x8c, (byte) 0x7a,
+        (byte) 0xb2, (byte) 0xb4, (byte) 0x02, (byte) 0x7c, (byte) 0xd4, (byte) 0x9a, (byte) 0xe6, (byte) 0xa5,
+        (byte) 0x1a, (byte) 0x27, (byte) 0x77, (byte) 0x70, (byte) 0xe3, (byte) 0xe3, (byte) 0x71, (byte) 0xc7,
+        (byte) 0x59, (byte) 0xc7, (byte) 0x9f, (byte) 0xb8, (byte) 0xef, (byte) 0xe7, (byte) 0x15, (byte) 0x02,
+        (byte) 0x0d, (byte) 0x70, (byte) 0xdc, (byte) 0x2c, (byte) 0xe9, (byte) 0xf7, (byte) 0x63, (byte) 0x2a,
+        (byte) 0xb5, (byte) 0xee, (byte) 0x9f, (byte) 0x29, (byte) 0x56, (byte) 0x86, (byte) 0x99, (byte) 0xb3,
+        (byte) 0x0f, (byte) 0xe5, (byte) 0x1f, (byte) 0x76, (byte) 0x22, (byte) 0x3b, (byte) 0x7f, (byte) 0xa9,
+        (byte) 0x9e, (byte) 0xd4, (byte) 0xc4, (byte) 0x83, (byte) 0x5d, (byte) 0x57, (byte) 0xcc, (byte) 0x37,
+        (byte) 0xcb, (byte) 0x9a, (byte) 0x9e, (byte) 0x73, (byte) 0x44, (byte) 0x93, (byte) 0xb4, (byte) 0xf1,
+        (byte) 0x6b, (byte) 0x98, (byte) 0xa0, (byte) 0x57, (byte) 0xbb, (byte) 0x5e, (byte) 0x8f, (byte) 0x89,
+        (byte) 0x5b, (byte) 0x97, (byte) 0x26, (byte) 0xe4, (byte) 0xd0, (byte) 0x51, (byte) 0x0a, (byte) 0x5a,
+        (byte) 0xb7, (byte) 0x12, (byte) 0x1a, (byte) 0x6d, (byte) 0xb0, (byte) 0x79, (byte) 0x30, (byte) 0x51,
+        (byte) 0x83, (byte) 0x2e, (byte) 0xe2, (byte) 0x7a, (byte) 0x67, (byte) 0x66, (byte) 0xd3, (byte) 0x95,
+        (byte) 0xca, (byte) 0xfc, (byte) 0xcb, (byte) 0x92, (byte) 0x79, (byte) 0x32, (byte) 0x26, (byte) 0x86,
+        (byte) 0xe1, (byte) 0x0d, (byte) 0xd8, (byte) 0x19, (byte) 0xfa, (byte) 0x65, (byte) 0x37, (byte) 0xc9,
+        (byte) 0x4c, (byte) 0x2a, (byte) 0xe1, (byte) 0x42, (byte) 0xc7, (byte) 0xd4, (byte) 0xb7, (byte) 0xeb,
+        (byte) 0x1f, (byte) 0xc3, (byte) 0x53, (byte) 0x64, (byte) 0x6f, (byte) 0x2b, (byte) 0x78, (byte) 0x18,
+        (byte) 0x03, (byte) 0xda, (byte) 0x8d, (byte) 0x62, (byte) 0x24, (byte) 0x70, (byte) 0xab, (byte) 0xe6,
+        (byte) 0x16, (byte) 0x13, (byte) 0x24, (byte) 0x6b, (byte) 0x5f, (byte) 0xd3, (byte) 0xec, (byte) 0xc1,
+        (byte) 0x58, (byte) 0x64, (byte) 0xbd, (byte) 0x30, (byte) 0x98, (byte) 0x5e, (byte) 0x33, (byte) 0xce,
+        (byte) 0x87, (byte) 0x64, (byte) 0x14, (byte) 0x07, (byte) 0x85, (byte) 0x43, (byte) 0x3e, (byte) 0x9f,
+        (byte) 0x27, (byte) 0x9f, (byte) 0x63, (byte) 0x66, (byte) 0x9d, (byte) 0x26, (byte) 0x19, (byte) 0xc0,
+        (byte) 0x02, (byte) 0x08, (byte) 0x15, (byte) 0xcb, (byte) 0xb4, (byte) 0xaa, (byte) 0x4a, (byte) 0xc8,
+        (byte) 0xc0, (byte) 0x09, (byte) 0x15, (byte) 0x7d, (byte) 0x8a, (byte) 0x21, (byte) 0xbc, (byte) 0xa3,
+    };
+
+    private static final byte[] SHA256withRSA_Vector2Signature = new byte[] {
+        (byte) 0x18, (byte) 0x6e, (byte) 0x31, (byte) 0x1f, (byte) 0x1d, (byte) 0x44, (byte) 0x09, (byte) 0x3e,
+        (byte) 0xa0, (byte) 0xc4, (byte) 0x3d, (byte) 0xb4, (byte) 0x1b, (byte) 0xf2, (byte) 0xd8, (byte) 0xa4,
+        (byte) 0x59, (byte) 0xab, (byte) 0xb5, (byte) 0x37, (byte) 0x28, (byte) 0xb8, (byte) 0x94, (byte) 0x6b,
+        (byte) 0x6f, (byte) 0x13, (byte) 0x54, (byte) 0xff, (byte) 0xac, (byte) 0x15, (byte) 0x84, (byte) 0xd0,
+        (byte) 0xc9, (byte) 0x15, (byte) 0x5b, (byte) 0x69, (byte) 0x05, (byte) 0xf1, (byte) 0x44, (byte) 0xfd,
+        (byte) 0xde, (byte) 0xe8, (byte) 0xb4, (byte) 0x12, (byte) 0x59, (byte) 0x9e, (byte) 0x4c, (byte) 0x0b,
+        (byte) 0xd5, (byte) 0x49, (byte) 0x33, (byte) 0x28, (byte) 0xe0, (byte) 0xcb, (byte) 0x87, (byte) 0x85,
+        (byte) 0xd8, (byte) 0x18, (byte) 0x6f, (byte) 0xfe, (byte) 0xa2, (byte) 0x23, (byte) 0x82, (byte) 0xf0,
+        (byte) 0xe5, (byte) 0x39, (byte) 0x1b, (byte) 0x8c, (byte) 0x93, (byte) 0x11, (byte) 0x49, (byte) 0x72,
+        (byte) 0x2a, (byte) 0x5b, (byte) 0x25, (byte) 0xff, (byte) 0x4e, (byte) 0x88, (byte) 0x70, (byte) 0x9d,
+        (byte) 0x9d, (byte) 0xff, (byte) 0xe2, (byte) 0xc0, (byte) 0x7e, (byte) 0xc8, (byte) 0x03, (byte) 0x40,
+        (byte) 0xbe, (byte) 0x44, (byte) 0x09, (byte) 0xeb, (byte) 0x9e, (byte) 0x8e, (byte) 0x88, (byte) 0xe4,
+        (byte) 0x98, (byte) 0x82, (byte) 0x06, (byte) 0xa4, (byte) 0x9d, (byte) 0x63, (byte) 0x88, (byte) 0x65,
+        (byte) 0xa3, (byte) 0x8e, (byte) 0x0d, (byte) 0x22, (byte) 0xf3, (byte) 0x33, (byte) 0xf2, (byte) 0x40,
+        (byte) 0xe8, (byte) 0x91, (byte) 0x67, (byte) 0x72, (byte) 0x29, (byte) 0x1c, (byte) 0x08, (byte) 0xff,
+        (byte) 0x54, (byte) 0xa0, (byte) 0xcc, (byte) 0xad, (byte) 0x84, (byte) 0x88, (byte) 0x4b, (byte) 0x3b,
+        (byte) 0xef, (byte) 0xf9, (byte) 0x5e, (byte) 0xb3, (byte) 0x41, (byte) 0x6a, (byte) 0xbd, (byte) 0x94,
+        (byte) 0x16, (byte) 0x7d, (byte) 0x9d, (byte) 0x53, (byte) 0x77, (byte) 0xf1, (byte) 0x6a, (byte) 0x95,
+        (byte) 0x57, (byte) 0xad, (byte) 0x65, (byte) 0x9d, (byte) 0x75, (byte) 0x95, (byte) 0xf6, (byte) 0x6a,
+        (byte) 0xd2, (byte) 0x88, (byte) 0xea, (byte) 0x5b, (byte) 0xa2, (byte) 0x94, (byte) 0x8f, (byte) 0x5e,
+        (byte) 0x84, (byte) 0x18, (byte) 0x19, (byte) 0x46, (byte) 0x83, (byte) 0x0b, (byte) 0x6d, (byte) 0x5b,
+        (byte) 0xb9, (byte) 0xdb, (byte) 0xa4, (byte) 0xe5, (byte) 0x17, (byte) 0x02, (byte) 0x9e, (byte) 0x11,
+        (byte) 0xed, (byte) 0xd9, (byte) 0x7b, (byte) 0x83, (byte) 0x87, (byte) 0x89, (byte) 0xf3, (byte) 0xe4,
+        (byte) 0xbf, (byte) 0x0e, (byte) 0xe8, (byte) 0xdc, (byte) 0x55, (byte) 0x9c, (byte) 0xf7, (byte) 0xc9,
+        (byte) 0xc3, (byte) 0xe2, (byte) 0x2c, (byte) 0xf7, (byte) 0x8c, (byte) 0xaa, (byte) 0x17, (byte) 0x1f,
+        (byte) 0xd1, (byte) 0xc7, (byte) 0x74, (byte) 0xc7, (byte) 0x8e, (byte) 0x1c, (byte) 0x5b, (byte) 0xd2,
+        (byte) 0x31, (byte) 0x74, (byte) 0x43, (byte) 0x9a, (byte) 0x52, (byte) 0xbf, (byte) 0x89, (byte) 0xc5,
+        (byte) 0xb4, (byte) 0x80, (byte) 0x6a, (byte) 0x9e, (byte) 0x05, (byte) 0xdb, (byte) 0xbb, (byte) 0x07,
+        (byte) 0x8c, (byte) 0x08, (byte) 0x61, (byte) 0xba, (byte) 0xa4, (byte) 0xbc, (byte) 0x80, (byte) 0x3a,
+        (byte) 0xdd, (byte) 0x3b, (byte) 0x1a, (byte) 0x8c, (byte) 0x21, (byte) 0xd8, (byte) 0xa3, (byte) 0xc0,
+        (byte) 0xc7, (byte) 0xd1, (byte) 0x08, (byte) 0xe1, (byte) 0x34, (byte) 0x99, (byte) 0xc0, (byte) 0xcf,
+        (byte) 0x80, (byte) 0xff, (byte) 0xfa, (byte) 0x07, (byte) 0xef, (byte) 0x5c, (byte) 0x45, (byte) 0xe5,
+    };
+
+    private static final byte[] SHA384withRSA_Vector2Signature = new byte[] {
+        (byte) 0xaf, (byte) 0xf7, (byte) 0x7a, (byte) 0xc2, (byte) 0xbb, (byte) 0xb8, (byte) 0xbd, (byte) 0xe3,
+        (byte) 0x42, (byte) 0xaa, (byte) 0x16, (byte) 0x8a, (byte) 0x52, (byte) 0x6c, (byte) 0x99, (byte) 0x66,
+        (byte) 0x08, (byte) 0xbe, (byte) 0x15, (byte) 0xd9, (byte) 0x7c, (byte) 0x60, (byte) 0x2c, (byte) 0xac,
+        (byte) 0x4d, (byte) 0x4c, (byte) 0xf4, (byte) 0xdf, (byte) 0xbc, (byte) 0x16, (byte) 0x58, (byte) 0x0a,
+        (byte) 0x4e, (byte) 0xde, (byte) 0x8d, (byte) 0xb3, (byte) 0xbd, (byte) 0x03, (byte) 0x4e, (byte) 0x23,
+        (byte) 0x40, (byte) 0xa5, (byte) 0x80, (byte) 0xae, (byte) 0x83, (byte) 0xb4, (byte) 0x0f, (byte) 0x99,
+        (byte) 0x44, (byte) 0xc3, (byte) 0x5e, (byte) 0xdb, (byte) 0x59, (byte) 0x1d, (byte) 0xea, (byte) 0x7b,
+        (byte) 0x4d, (byte) 0xf3, (byte) 0xd2, (byte) 0xad, (byte) 0xbd, (byte) 0x21, (byte) 0x9f, (byte) 0x8e,
+        (byte) 0x87, (byte) 0x8f, (byte) 0x12, (byte) 0x13, (byte) 0x33, (byte) 0xf1, (byte) 0xc0, (byte) 0x9d,
+        (byte) 0xe7, (byte) 0xec, (byte) 0x6e, (byte) 0xad, (byte) 0xea, (byte) 0x5d, (byte) 0x69, (byte) 0xbb,
+        (byte) 0xab, (byte) 0x5b, (byte) 0xd8, (byte) 0x55, (byte) 0x56, (byte) 0xc8, (byte) 0xda, (byte) 0x81,
+        (byte) 0x41, (byte) 0xfb, (byte) 0xd3, (byte) 0x11, (byte) 0x6c, (byte) 0x97, (byte) 0xa7, (byte) 0xc3,
+        (byte) 0xf1, (byte) 0x31, (byte) 0xbf, (byte) 0xbe, (byte) 0x3f, (byte) 0xdb, (byte) 0x35, (byte) 0x85,
+        (byte) 0xb7, (byte) 0xb0, (byte) 0x75, (byte) 0x7f, (byte) 0xaf, (byte) 0xfb, (byte) 0x65, (byte) 0x61,
+        (byte) 0xc7, (byte) 0x0e, (byte) 0x63, (byte) 0xb5, (byte) 0x7d, (byte) 0x95, (byte) 0xe9, (byte) 0x16,
+        (byte) 0x9d, (byte) 0x6a, (byte) 0x00, (byte) 0x9f, (byte) 0x5e, (byte) 0xcd, (byte) 0xff, (byte) 0xa6,
+        (byte) 0xbc, (byte) 0x71, (byte) 0xf2, (byte) 0x2c, (byte) 0xd3, (byte) 0x68, (byte) 0xb9, (byte) 0x3f,
+        (byte) 0xaa, (byte) 0x06, (byte) 0xf1, (byte) 0x9c, (byte) 0x7e, (byte) 0xca, (byte) 0x4a, (byte) 0xfe,
+        (byte) 0xb1, (byte) 0x73, (byte) 0x19, (byte) 0x80, (byte) 0x05, (byte) 0xa6, (byte) 0x85, (byte) 0x14,
+        (byte) 0xda, (byte) 0x7a, (byte) 0x16, (byte) 0x7a, (byte) 0xc2, (byte) 0x46, (byte) 0x57, (byte) 0xa7,
+        (byte) 0xc0, (byte) 0xbf, (byte) 0xcd, (byte) 0xdc, (byte) 0x2f, (byte) 0x64, (byte) 0xf6, (byte) 0x6d,
+        (byte) 0xdc, (byte) 0xcb, (byte) 0x5a, (byte) 0x29, (byte) 0x95, (byte) 0x1c, (byte) 0xfe, (byte) 0xf2,
+        (byte) 0xda, (byte) 0x7e, (byte) 0xcb, (byte) 0x26, (byte) 0x12, (byte) 0xc6, (byte) 0xb0, (byte) 0xba,
+        (byte) 0x84, (byte) 0x9b, (byte) 0x4f, (byte) 0xba, (byte) 0x1b, (byte) 0x78, (byte) 0x25, (byte) 0xb8,
+        (byte) 0x8f, (byte) 0x2e, (byte) 0x51, (byte) 0x5f, (byte) 0x9e, (byte) 0xfc, (byte) 0x40, (byte) 0xbc,
+        (byte) 0x85, (byte) 0xcd, (byte) 0x86, (byte) 0x7f, (byte) 0x88, (byte) 0xc5, (byte) 0xaa, (byte) 0x2b,
+        (byte) 0x78, (byte) 0xb1, (byte) 0x9c, (byte) 0x51, (byte) 0x9a, (byte) 0xe1, (byte) 0xe1, (byte) 0xc0,
+        (byte) 0x40, (byte) 0x47, (byte) 0xcb, (byte) 0xa4, (byte) 0xb7, (byte) 0x6c, (byte) 0x31, (byte) 0xf2,
+        (byte) 0xc8, (byte) 0x9a, (byte) 0xad, (byte) 0x0b, (byte) 0xd3, (byte) 0xf6, (byte) 0x85, (byte) 0x9a,
+        (byte) 0x8f, (byte) 0x4f, (byte) 0xc9, (byte) 0xd8, (byte) 0x33, (byte) 0x7c, (byte) 0x45, (byte) 0x30,
+        (byte) 0xea, (byte) 0x17, (byte) 0xd3, (byte) 0xe3, (byte) 0x90, (byte) 0x2c, (byte) 0xda, (byte) 0xde,
+        (byte) 0x41, (byte) 0x17, (byte) 0x3f, (byte) 0x08, (byte) 0xb9, (byte) 0x34, (byte) 0xc0, (byte) 0xd1,
+    };
+
+    private static final byte[] SHA512withRSA_Vector2Signature = new byte[] {
+        (byte) 0x19, (byte) 0xe2, (byte) 0xe5, (byte) 0xf3, (byte) 0x18, (byte) 0x83, (byte) 0xec, (byte) 0xf0,
+        (byte) 0xab, (byte) 0x50, (byte) 0x05, (byte) 0x4b, (byte) 0x5f, (byte) 0x22, (byte) 0xfc, (byte) 0x82,
+        (byte) 0x6d, (byte) 0xca, (byte) 0xe7, (byte) 0xbe, (byte) 0x23, (byte) 0x94, (byte) 0xfa, (byte) 0xf9,
+        (byte) 0xa4, (byte) 0x8a, (byte) 0x95, (byte) 0x4d, (byte) 0x14, (byte) 0x08, (byte) 0x8b, (byte) 0x5e,
+        (byte) 0x03, (byte) 0x1b, (byte) 0x74, (byte) 0xde, (byte) 0xc1, (byte) 0x45, (byte) 0x9c, (byte) 0xce,
+        (byte) 0x1d, (byte) 0xac, (byte) 0xab, (byte) 0xd3, (byte) 0xa8, (byte) 0xc3, (byte) 0xca, (byte) 0x67,
+        (byte) 0x80, (byte) 0xf6, (byte) 0x03, (byte) 0x46, (byte) 0x65, (byte) 0x77, (byte) 0x59, (byte) 0xbb,
+        (byte) 0xb8, (byte) 0x83, (byte) 0xee, (byte) 0xc2, (byte) 0x3e, (byte) 0x78, (byte) 0xdd, (byte) 0x89,
+        (byte) 0xcd, (byte) 0x9b, (byte) 0x78, (byte) 0x35, (byte) 0xa9, (byte) 0x09, (byte) 0xc8, (byte) 0x77,
+        (byte) 0xdd, (byte) 0xd3, (byte) 0xa0, (byte) 0x64, (byte) 0xb0, (byte) 0x74, (byte) 0x48, (byte) 0x51,
+        (byte) 0x4f, (byte) 0xa0, (byte) 0xae, (byte) 0x33, (byte) 0xb3, (byte) 0x28, (byte) 0xb0, (byte) 0xa8,
+        (byte) 0x78, (byte) 0x8f, (byte) 0xa2, (byte) 0x32, (byte) 0xa6, (byte) 0x0a, (byte) 0xaa, (byte) 0x09,
+        (byte) 0xb5, (byte) 0x8d, (byte) 0x4c, (byte) 0x44, (byte) 0x46, (byte) 0xb4, (byte) 0xd2, (byte) 0x06,
+        (byte) 0x6b, (byte) 0x8c, (byte) 0x51, (byte) 0x6e, (byte) 0x9c, (byte) 0xfa, (byte) 0x1f, (byte) 0x94,
+        (byte) 0x3e, (byte) 0x19, (byte) 0x9c, (byte) 0x63, (byte) 0xfe, (byte) 0xa9, (byte) 0x9a, (byte) 0xe3,
+        (byte) 0x6c, (byte) 0x82, (byte) 0x64, (byte) 0x5f, (byte) 0xca, (byte) 0xc2, (byte) 0x8d, (byte) 0x66,
+        (byte) 0xbe, (byte) 0x12, (byte) 0x6e, (byte) 0xb6, (byte) 0x35, (byte) 0x6d, (byte) 0xaa, (byte) 0xed,
+        (byte) 0x4b, (byte) 0x50, (byte) 0x08, (byte) 0x1c, (byte) 0xbf, (byte) 0x07, (byte) 0x70, (byte) 0x78,
+        (byte) 0xc0, (byte) 0xbb, (byte) 0xc5, (byte) 0x8d, (byte) 0x6c, (byte) 0x8d, (byte) 0x35, (byte) 0xff,
+        (byte) 0x04, (byte) 0x81, (byte) 0xd8, (byte) 0xf4, (byte) 0xd2, (byte) 0x4a, (byte) 0xc3, (byte) 0x05,
+        (byte) 0x23, (byte) 0xcb, (byte) 0xeb, (byte) 0x20, (byte) 0xb1, (byte) 0xd4, (byte) 0x2d, (byte) 0xd8,
+        (byte) 0x7a, (byte) 0xd4, (byte) 0x7e, (byte) 0xf6, (byte) 0xa9, (byte) 0xe8, (byte) 0x72, (byte) 0x69,
+        (byte) 0xfe, (byte) 0xab, (byte) 0x54, (byte) 0x4d, (byte) 0xd1, (byte) 0xf4, (byte) 0x6b, (byte) 0x83,
+        (byte) 0x31, (byte) 0x17, (byte) 0xed, (byte) 0x26, (byte) 0xe9, (byte) 0xd2, (byte) 0x5b, (byte) 0xad,
+        (byte) 0x42, (byte) 0x42, (byte) 0xa5, (byte) 0x8f, (byte) 0x98, (byte) 0x7c, (byte) 0x1b, (byte) 0x5c,
+        (byte) 0x8e, (byte) 0x88, (byte) 0x56, (byte) 0x20, (byte) 0x8e, (byte) 0x48, (byte) 0xf9, (byte) 0x4d,
+        (byte) 0x82, (byte) 0x91, (byte) 0xcb, (byte) 0xc8, (byte) 0x1c, (byte) 0x7c, (byte) 0xa5, (byte) 0x69,
+        (byte) 0x1b, (byte) 0x40, (byte) 0xc2, (byte) 0x4c, (byte) 0x25, (byte) 0x16, (byte) 0x4f, (byte) 0xfa,
+        (byte) 0x09, (byte) 0xeb, (byte) 0xf5, (byte) 0x6c, (byte) 0x55, (byte) 0x3c, (byte) 0x6e, (byte) 0xf7,
+        (byte) 0xc0, (byte) 0xc1, (byte) 0x34, (byte) 0xd1, (byte) 0x53, (byte) 0xa3, (byte) 0x69, (byte) 0x64,
+        (byte) 0xee, (byte) 0xf4, (byte) 0xf9, (byte) 0xc7, (byte) 0x96, (byte) 0x60, (byte) 0x84, (byte) 0x87,
+        (byte) 0xb4, (byte) 0xc7, (byte) 0x3c, (byte) 0x26, (byte) 0xa7, (byte) 0x3a, (byte) 0xbf, (byte) 0x95,
+    };
+
+    private static final byte[] MD5withRSA_Vector2Signature = new byte[] {
+        (byte) 0x04, (byte) 0x17, (byte) 0x83, (byte) 0x10, (byte) 0xe2, (byte) 0x6e, (byte) 0xdf, (byte) 0xa9,
+        (byte) 0xae, (byte) 0xd2, (byte) 0xdc, (byte) 0x5f, (byte) 0x70, (byte) 0x1d, (byte) 0xaf, (byte) 0x54,
+        (byte) 0xc0, (byte) 0x5f, (byte) 0x0b, (byte) 0x2c, (byte) 0xe6, (byte) 0xd0, (byte) 0x00, (byte) 0x18,
+        (byte) 0x4c, (byte) 0xf6, (byte) 0x8f, (byte) 0x18, (byte) 0x10, (byte) 0x74, (byte) 0x90, (byte) 0x99,
+        (byte) 0xa9, (byte) 0x90, (byte) 0x3c, (byte) 0x5a, (byte) 0x38, (byte) 0xd3, (byte) 0x3d, (byte) 0x48,
+        (byte) 0xcf, (byte) 0x31, (byte) 0xaf, (byte) 0x12, (byte) 0x98, (byte) 0xfb, (byte) 0x66, (byte) 0xe8,
+        (byte) 0x58, (byte) 0xec, (byte) 0xca, (byte) 0xe1, (byte) 0x42, (byte) 0xf9, (byte) 0x84, (byte) 0x17,
+        (byte) 0x6f, (byte) 0x4c, (byte) 0x3e, (byte) 0xc4, (byte) 0x40, (byte) 0xc6, (byte) 0x70, (byte) 0xb0,
+        (byte) 0x38, (byte) 0xf3, (byte) 0x47, (byte) 0xeb, (byte) 0x6f, (byte) 0xcb, (byte) 0xea, (byte) 0x21,
+        (byte) 0x41, (byte) 0xf3, (byte) 0xa0, (byte) 0x3e, (byte) 0x42, (byte) 0xad, (byte) 0xa5, (byte) 0xad,
+        (byte) 0x5d, (byte) 0x2c, (byte) 0x1a, (byte) 0x8e, (byte) 0x3e, (byte) 0xb3, (byte) 0xa5, (byte) 0x78,
+        (byte) 0x3d, (byte) 0x56, (byte) 0x09, (byte) 0x93, (byte) 0xc9, (byte) 0x93, (byte) 0xd3, (byte) 0xd2,
+        (byte) 0x9a, (byte) 0xc5, (byte) 0xa5, (byte) 0x2e, (byte) 0xb2, (byte) 0xd8, (byte) 0x37, (byte) 0xc7,
+        (byte) 0x13, (byte) 0x1a, (byte) 0x0b, (byte) 0xda, (byte) 0x50, (byte) 0x28, (byte) 0x6d, (byte) 0x47,
+        (byte) 0x65, (byte) 0x52, (byte) 0xcd, (byte) 0xe7, (byte) 0xec, (byte) 0x57, (byte) 0x00, (byte) 0x41,
+        (byte) 0x34, (byte) 0x28, (byte) 0xb9, (byte) 0x8b, (byte) 0x03, (byte) 0x41, (byte) 0xb6, (byte) 0xd5,
+        (byte) 0xa8, (byte) 0xef, (byte) 0xd3, (byte) 0xdd, (byte) 0x80, (byte) 0xd5, (byte) 0x69, (byte) 0xe4,
+        (byte) 0xf0, (byte) 0x4d, (byte) 0xa4, (byte) 0x7d, (byte) 0x60, (byte) 0x2f, (byte) 0xef, (byte) 0x79,
+        (byte) 0x07, (byte) 0x75, (byte) 0xeb, (byte) 0xf7, (byte) 0x4b, (byte) 0x43, (byte) 0x41, (byte) 0xdb,
+        (byte) 0x33, (byte) 0xad, (byte) 0x9c, (byte) 0x7b, (byte) 0x78, (byte) 0x83, (byte) 0x34, (byte) 0x77,
+        (byte) 0xe4, (byte) 0x80, (byte) 0xbe, (byte) 0xe6, (byte) 0x6f, (byte) 0xdd, (byte) 0xac, (byte) 0xa5,
+        (byte) 0x37, (byte) 0xcf, (byte) 0xb5, (byte) 0x44, (byte) 0x11, (byte) 0x77, (byte) 0x96, (byte) 0x45,
+        (byte) 0xf9, (byte) 0xae, (byte) 0x48, (byte) 0xa6, (byte) 0xbe, (byte) 0x30, (byte) 0x32, (byte) 0xeb,
+        (byte) 0x43, (byte) 0x6f, (byte) 0x66, (byte) 0x39, (byte) 0x57, (byte) 0xf8, (byte) 0xe6, (byte) 0x60,
+        (byte) 0x31, (byte) 0xd0, (byte) 0xfc, (byte) 0xcf, (byte) 0x9f, (byte) 0xe5, (byte) 0x3d, (byte) 0xcf,
+        (byte) 0xbd, (byte) 0x7b, (byte) 0x13, (byte) 0x20, (byte) 0xce, (byte) 0x11, (byte) 0xfd, (byte) 0xe5,
+        (byte) 0xff, (byte) 0x90, (byte) 0x85, (byte) 0xdf, (byte) 0xca, (byte) 0x3d, (byte) 0xd9, (byte) 0x44,
+        (byte) 0x16, (byte) 0xc2, (byte) 0x32, (byte) 0x28, (byte) 0xc7, (byte) 0x01, (byte) 0x6d, (byte) 0xea,
+        (byte) 0xcb, (byte) 0x0d, (byte) 0x85, (byte) 0x08, (byte) 0x6f, (byte) 0xcb, (byte) 0x41, (byte) 0x6a,
+        (byte) 0x3c, (byte) 0x0f, (byte) 0x3d, (byte) 0x38, (byte) 0xb5, (byte) 0x61, (byte) 0xc5, (byte) 0x64,
+        (byte) 0x64, (byte) 0x81, (byte) 0x4c, (byte) 0xcd, (byte) 0xd1, (byte) 0x6a, (byte) 0x87, (byte) 0x28,
+        (byte) 0x02, (byte) 0xaf, (byte) 0x8f, (byte) 0x59, (byte) 0xe5, (byte) 0x67, (byte) 0x25, (byte) 0x00,
+    };
+
+    public void testGetCommonInstances_Success() throws Exception {
+        assertNotNull(Signature.getInstance("SHA1withRSA"));
+        assertNotNull(Signature.getInstance("SHA256withRSA"));
+        assertNotNull(Signature.getInstance("SHA384withRSA"));
+        assertNotNull(Signature.getInstance("SHA512withRSA"));
+        assertNotNull(Signature.getInstance("MD5withRSA"));
+        assertNotNull(Signature.getInstance("SHA1withDSA"));
+    }
+
+    public void testVerify_SHA1withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector1Signature));
+    }
+
+    public void testVerify_SHA256withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA256withRSA_Vector2Signature));
+    }
+
+    public void testVerify_SHA384withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA384withRSA_Vector2Signature));
+    }
+
+    public void testVerify_SHA512withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA512withRSA_Vector2Signature));
+    }
+
+    public void testVerify_MD5withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("MD5withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(MD5withRSA_Vector2Signature));
+    }
+
+    public void testVerify_SHA1withRSA_Key_InitSignThenInitVerify_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(privKeySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        // Start a signing operation
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        // Switch to verify
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector1Signature));
+    }
+
+    public void testVerify_SHA1withRSA_Key_TwoMessages_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initVerify(pubKey);
+
+        sig.update(Vector1Data);
+        assertTrue("First signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector1Signature));
+
+        sig.update(Vector2Data);
+        assertTrue("Second signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector2Signature));
+    }
+
+    public void testVerify_SHA1withRSA_Key_WrongExpectedSignature_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertFalse("Signature should fail to verify", sig.verify(SHA1withRSA_Vector2Signature));
+    }
+
+    public void testSign_SHA1withRSA_CrtKeyWithPublicExponent_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent, RSA_2048_privateExponent, null, null, null, null, null);
+
+        // The RI fails on this key which is totally unreasonable.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            if (StandardNames.IS_RI) {
+                return;
+            } else {
+                fail("Private key should be created");
+                return;
+            }
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector1Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA1withRSA_Vector1Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    public void testSign_SHA1withRSA_CrtKey_NoPrivateExponent_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent, null, RSA_2048_primeP, RSA_2048_primeQ, null, null, null);
+
+        // Failing on this key early is okay.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            return;
+        } catch (InvalidKeySpecException e) {
+            return;
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        try {
+            sig.initSign(privKey);
+            fail("Should throw error when private exponent is not available");
+        } catch (InvalidKeyException e) {
+            // success
+        }
+    }
+
+    public void testSign_SHA1withRSA_CrtKey_NoModulus_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(null, RSA_2048_publicExponent,
+                RSA_2048_privateExponent, RSA_2048_primeP, RSA_2048_primeQ, null, null, null);
+
+        // Failing on this key early is okay.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            return;
+        } catch (InvalidKeySpecException e) {
+            return;
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        try {
+            sig.initSign(privKey);
+            fail("Should throw error when modulus is not available");
+        } catch (InvalidKeyException e) {
+            // success
+        }
+    }
+
+    public void testSign_SHA1withRSA_Key_EmptyKey_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(null, null);
+
+        // Failing on this key early is okay.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            return;
+        } catch (InvalidKeySpecException e) {
+            return;
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        try {
+            sig.initSign(privKey);
+            fail("Should throw error when key is empty");
+        } catch (InvalidKeyException e) {
+            // success
+        }
+    }
+
+    public void testSign_SHA1withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector1Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA1withRSA_Vector1Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    public void testSign_SHA256withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+
+        final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA256withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    public void testSign_SHA384withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA384withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    public void testSign_SHA512withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA512withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    public void testSign_MD5withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("MD5withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, MD5withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    /*
+     * These tests were generated with this DSA private key:
+     *
+     * -----BEGIN DSA PRIVATE KEY-----
+     * MIIBugIBAAKBgQCeYcKJ73epThNnZB8JAf4kE1Pgt5CoTnb+iYJ/esU8TgwgVTCV
+     * QoXhQH0njwcN6NyZ77MHlDTWfP+cvmnT60Q3UO9J+OJb2NEQhJfq46UcwE5pynA9
+     * eLkW5f5hXYpasyxhtgE70AF8Mo3h82kOi1jGzwCU+EkqS+raAP9L0L5AIwIVAL/u
+     * qg8SNFBy+GAT2PFBARClL1dfAoGAd9R6EsyBfn7rOvvmhm1aEB2tqU+5A10hGuQw
+     * lXWOzV7RvQpF7uf3a2UCYNAurz28B90rjjPAk4DZK6dxV3a8jrng1/QjjUEal08s
+     * G9VLZuj60lANF6s0MT2kiNiOqKduFwO3D2h8ZHuSuGPkmmcYgSfUCxNI031O9qiP
+     * VhctCFECgYAz7i1DhjRGUkCdYQd5tVaI42lhXOV71MTYPbuFOIxTL/hny7Z0PZWR
+     * A1blmYE6vrArDEhzpmRvDJZSIMzMfJjUIGu1KO73zpo9siK0xY0/sw5r3QC9txP2
+     * 2Mv3BUIl5TLrs9outQJ0VMwldY2fElgCLWcSVkH44qZwWir1cq+cIwIUEGPDardb
+     * pNvWlWgTDD6a6ZTby+M=
+     * -----END DSA PRIVATE KEY-----
+     *
+     */
+
+    private static final BigInteger DSA_priv = new BigInteger(new byte[] {
+        (byte) 0x10, (byte) 0x63, (byte) 0xc3, (byte) 0x6a, (byte) 0xb7, (byte) 0x5b, (byte) 0xa4, (byte) 0xdb,
+        (byte) 0xd6, (byte) 0x95, (byte) 0x68, (byte) 0x13, (byte) 0x0c, (byte) 0x3e, (byte) 0x9a, (byte) 0xe9,
+        (byte) 0x94, (byte) 0xdb, (byte) 0xcb, (byte) 0xe3,
+    });
+
+    private static final BigInteger DSA_pub = new BigInteger(new byte[] {
+        (byte) 0x33, (byte) 0xee, (byte) 0x2d, (byte) 0x43, (byte) 0x86, (byte) 0x34, (byte) 0x46, (byte) 0x52,
+        (byte) 0x40, (byte) 0x9d, (byte) 0x61, (byte) 0x07, (byte) 0x79, (byte) 0xb5, (byte) 0x56, (byte) 0x88,
+        (byte) 0xe3, (byte) 0x69, (byte) 0x61, (byte) 0x5c, (byte) 0xe5, (byte) 0x7b, (byte) 0xd4, (byte) 0xc4,
+        (byte) 0xd8, (byte) 0x3d, (byte) 0xbb, (byte) 0x85, (byte) 0x38, (byte) 0x8c, (byte) 0x53, (byte) 0x2f,
+        (byte) 0xf8, (byte) 0x67, (byte) 0xcb, (byte) 0xb6, (byte) 0x74, (byte) 0x3d, (byte) 0x95, (byte) 0x91,
+        (byte) 0x03, (byte) 0x56, (byte) 0xe5, (byte) 0x99, (byte) 0x81, (byte) 0x3a, (byte) 0xbe, (byte) 0xb0,
+        (byte) 0x2b, (byte) 0x0c, (byte) 0x48, (byte) 0x73, (byte) 0xa6, (byte) 0x64, (byte) 0x6f, (byte) 0x0c,
+        (byte) 0x96, (byte) 0x52, (byte) 0x20, (byte) 0xcc, (byte) 0xcc, (byte) 0x7c, (byte) 0x98, (byte) 0xd4,
+        (byte) 0x20, (byte) 0x6b, (byte) 0xb5, (byte) 0x28, (byte) 0xee, (byte) 0xf7, (byte) 0xce, (byte) 0x9a,
+        (byte) 0x3d, (byte) 0xb2, (byte) 0x22, (byte) 0xb4, (byte) 0xc5, (byte) 0x8d, (byte) 0x3f, (byte) 0xb3,
+        (byte) 0x0e, (byte) 0x6b, (byte) 0xdd, (byte) 0x00, (byte) 0xbd, (byte) 0xb7, (byte) 0x13, (byte) 0xf6,
+        (byte) 0xd8, (byte) 0xcb, (byte) 0xf7, (byte) 0x05, (byte) 0x42, (byte) 0x25, (byte) 0xe5, (byte) 0x32,
+        (byte) 0xeb, (byte) 0xb3, (byte) 0xda, (byte) 0x2e, (byte) 0xb5, (byte) 0x02, (byte) 0x74, (byte) 0x54,
+        (byte) 0xcc, (byte) 0x25, (byte) 0x75, (byte) 0x8d, (byte) 0x9f, (byte) 0x12, (byte) 0x58, (byte) 0x02,
+        (byte) 0x2d, (byte) 0x67, (byte) 0x12, (byte) 0x56, (byte) 0x41, (byte) 0xf8, (byte) 0xe2, (byte) 0xa6,
+        (byte) 0x70, (byte) 0x5a, (byte) 0x2a, (byte) 0xf5, (byte) 0x72, (byte) 0xaf, (byte) 0x9c, (byte) 0x23,
+    });
+
+    private static final BigInteger DSA_P = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0x9e, (byte) 0x61, (byte) 0xc2, (byte) 0x89, (byte) 0xef, (byte) 0x77, (byte) 0xa9,
+        (byte) 0x4e, (byte) 0x13, (byte) 0x67, (byte) 0x64, (byte) 0x1f, (byte) 0x09, (byte) 0x01, (byte) 0xfe,
+        (byte) 0x24, (byte) 0x13, (byte) 0x53, (byte) 0xe0, (byte) 0xb7, (byte) 0x90, (byte) 0xa8, (byte) 0x4e,
+        (byte) 0x76, (byte) 0xfe, (byte) 0x89, (byte) 0x82, (byte) 0x7f, (byte) 0x7a, (byte) 0xc5, (byte) 0x3c,
+        (byte) 0x4e, (byte) 0x0c, (byte) 0x20, (byte) 0x55, (byte) 0x30, (byte) 0x95, (byte) 0x42, (byte) 0x85,
+        (byte) 0xe1, (byte) 0x40, (byte) 0x7d, (byte) 0x27, (byte) 0x8f, (byte) 0x07, (byte) 0x0d, (byte) 0xe8,
+        (byte) 0xdc, (byte) 0x99, (byte) 0xef, (byte) 0xb3, (byte) 0x07, (byte) 0x94, (byte) 0x34, (byte) 0xd6,
+        (byte) 0x7c, (byte) 0xff, (byte) 0x9c, (byte) 0xbe, (byte) 0x69, (byte) 0xd3, (byte) 0xeb, (byte) 0x44,
+        (byte) 0x37, (byte) 0x50, (byte) 0xef, (byte) 0x49, (byte) 0xf8, (byte) 0xe2, (byte) 0x5b, (byte) 0xd8,
+        (byte) 0xd1, (byte) 0x10, (byte) 0x84, (byte) 0x97, (byte) 0xea, (byte) 0xe3, (byte) 0xa5, (byte) 0x1c,
+        (byte) 0xc0, (byte) 0x4e, (byte) 0x69, (byte) 0xca, (byte) 0x70, (byte) 0x3d, (byte) 0x78, (byte) 0xb9,
+        (byte) 0x16, (byte) 0xe5, (byte) 0xfe, (byte) 0x61, (byte) 0x5d, (byte) 0x8a, (byte) 0x5a, (byte) 0xb3,
+        (byte) 0x2c, (byte) 0x61, (byte) 0xb6, (byte) 0x01, (byte) 0x3b, (byte) 0xd0, (byte) 0x01, (byte) 0x7c,
+        (byte) 0x32, (byte) 0x8d, (byte) 0xe1, (byte) 0xf3, (byte) 0x69, (byte) 0x0e, (byte) 0x8b, (byte) 0x58,
+        (byte) 0xc6, (byte) 0xcf, (byte) 0x00, (byte) 0x94, (byte) 0xf8, (byte) 0x49, (byte) 0x2a, (byte) 0x4b,
+        (byte) 0xea, (byte) 0xda, (byte) 0x00, (byte) 0xff, (byte) 0x4b, (byte) 0xd0, (byte) 0xbe, (byte) 0x40,
+        (byte) 0x23,
+    });
+
+    private static final BigInteger DSA_Q = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xbf, (byte) 0xee, (byte) 0xaa, (byte) 0x0f, (byte) 0x12, (byte) 0x34, (byte) 0x50,
+        (byte) 0x72, (byte) 0xf8, (byte) 0x60, (byte) 0x13, (byte) 0xd8, (byte) 0xf1, (byte) 0x41, (byte) 0x01,
+        (byte) 0x10, (byte) 0xa5, (byte) 0x2f, (byte) 0x57, (byte) 0x5f,
+    });
+
+    private static final BigInteger DSA_G = new BigInteger(new byte[] {
+        (byte) 0x77, (byte) 0xd4, (byte) 0x7a, (byte) 0x12, (byte) 0xcc, (byte) 0x81, (byte) 0x7e, (byte) 0x7e,
+        (byte) 0xeb, (byte) 0x3a, (byte) 0xfb, (byte) 0xe6, (byte) 0x86, (byte) 0x6d, (byte) 0x5a, (byte) 0x10,
+        (byte) 0x1d, (byte) 0xad, (byte) 0xa9, (byte) 0x4f, (byte) 0xb9, (byte) 0x03, (byte) 0x5d, (byte) 0x21,
+        (byte) 0x1a, (byte) 0xe4, (byte) 0x30, (byte) 0x95, (byte) 0x75, (byte) 0x8e, (byte) 0xcd, (byte) 0x5e,
+        (byte) 0xd1, (byte) 0xbd, (byte) 0x0a, (byte) 0x45, (byte) 0xee, (byte) 0xe7, (byte) 0xf7, (byte) 0x6b,
+        (byte) 0x65, (byte) 0x02, (byte) 0x60, (byte) 0xd0, (byte) 0x2e, (byte) 0xaf, (byte) 0x3d, (byte) 0xbc,
+        (byte) 0x07, (byte) 0xdd, (byte) 0x2b, (byte) 0x8e, (byte) 0x33, (byte) 0xc0, (byte) 0x93, (byte) 0x80,
+        (byte) 0xd9, (byte) 0x2b, (byte) 0xa7, (byte) 0x71, (byte) 0x57, (byte) 0x76, (byte) 0xbc, (byte) 0x8e,
+        (byte) 0xb9, (byte) 0xe0, (byte) 0xd7, (byte) 0xf4, (byte) 0x23, (byte) 0x8d, (byte) 0x41, (byte) 0x1a,
+        (byte) 0x97, (byte) 0x4f, (byte) 0x2c, (byte) 0x1b, (byte) 0xd5, (byte) 0x4b, (byte) 0x66, (byte) 0xe8,
+        (byte) 0xfa, (byte) 0xd2, (byte) 0x50, (byte) 0x0d, (byte) 0x17, (byte) 0xab, (byte) 0x34, (byte) 0x31,
+        (byte) 0x3d, (byte) 0xa4, (byte) 0x88, (byte) 0xd8, (byte) 0x8e, (byte) 0xa8, (byte) 0xa7, (byte) 0x6e,
+        (byte) 0x17, (byte) 0x03, (byte) 0xb7, (byte) 0x0f, (byte) 0x68, (byte) 0x7c, (byte) 0x64, (byte) 0x7b,
+        (byte) 0x92, (byte) 0xb8, (byte) 0x63, (byte) 0xe4, (byte) 0x9a, (byte) 0x67, (byte) 0x18, (byte) 0x81,
+        (byte) 0x27, (byte) 0xd4, (byte) 0x0b, (byte) 0x13, (byte) 0x48, (byte) 0xd3, (byte) 0x7d, (byte) 0x4e,
+        (byte) 0xf6, (byte) 0xa8, (byte) 0x8f, (byte) 0x56, (byte) 0x17, (byte) 0x2d, (byte) 0x08, (byte) 0x51,
+    });
+
+    /**
+     * A possible signature using SHA1withDSA of Vector2Data. Note that DSS is
+     * randomized, so this won't be the exact signature you'll get out of
+     * another signing operation unless you use a fixed RNG.
+     */
+    public static final byte[] SHA1withDSA_Vector2Signature = new byte[] {
+        (byte) 0x30, (byte) 0x2d, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x88, (byte) 0xef, (byte) 0xac,
+        (byte) 0x2b, (byte) 0x8b, (byte) 0xe2, (byte) 0x61, (byte) 0xc6, (byte) 0x2b, (byte) 0xea, (byte) 0xd5,
+        (byte) 0x96, (byte) 0xbc, (byte) 0xb0, (byte) 0xa1, (byte) 0x30, (byte) 0x0c, (byte) 0x1f, (byte) 0xed,
+        (byte) 0x11, (byte) 0x02, (byte) 0x14, (byte) 0x15, (byte) 0xc4, (byte) 0xfc, (byte) 0x82, (byte) 0x6f,
+        (byte) 0x17, (byte) 0xdc, (byte) 0x87, (byte) 0x82, (byte) 0x75, (byte) 0x23, (byte) 0xd4, (byte) 0x58,
+        (byte) 0xdc, (byte) 0x73, (byte) 0x3d, (byte) 0xf3, (byte) 0x51, (byte) 0xc0, (byte) 0x57,
+    };
+
+    public void testSign_SHA1withDSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(DSA_priv, DSA_P, DSA_Q, DSA_G);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withDSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    public void testVerify_SHA1withDSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("SHA1withDSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(SHA1withDSA_Vector2Signature));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/text/AttributedCharacterIteratorAttributeTest.java b/luni/src/test/java/libcore/java/text/AttributedCharacterIteratorAttributeTest.java
index cca00f5..560a6b6 100644
--- a/luni/src/test/java/libcore/java/text/AttributedCharacterIteratorAttributeTest.java
+++ b/luni/src/test/java/libcore/java/text/AttributedCharacterIteratorAttributeTest.java
@@ -16,18 +16,13 @@
 
 package libcore.java.text;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
 import java.text.AttributedCharacterIterator;
 import java.text.DateFormat;
 import java.text.NumberFormat;
 import junit.framework.TestCase;
-import tests.util.SerializationTester;
+import libcore.util.SerializationTester;
 
 /**
  * AttributedCharacterIterator.Attribute is used like the base enum type and
@@ -45,14 +40,14 @@
     public void testSerializingSubclass() throws IOException, ClassNotFoundException {
         AttributedCharacterIterator.Attribute a = new CustomAttribute();
         try {
-            SerializationTester.getDeserializedObject(a);
+            SerializationTester.reserialize(a);
             fail();
         } catch (InvalidObjectException expected) {
         }
     }
 
     private void assertSameReserialized(Object o) throws ClassNotFoundException, IOException {
-        assertSame(o, SerializationTester.getDeserializedObject(o));
+        assertSame(o, SerializationTester.reserialize(o));
     }
 
     private static class CustomAttribute extends AttributedCharacterIterator.Attribute {
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index 96c9aef..5c36fe2 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -20,6 +20,7 @@
 import java.util.GregorianCalendar;
 import java.util.Locale;
 import java.util.TimeZone;
+import libcore.util.SerializationTester;
 
 public class CalendarTest extends junit.framework.TestCase {
 
@@ -186,9 +187,9 @@
                 + "10000000500000001000000200000000178";
         Calendar calendar = new GregorianCalendar(1970, 1, 1, 0, 0, 0);
         calendar.setTimeZone(TimeZone.getTimeZone("GMT-08:00"));
-        calendar.setFirstDayOfWeek(1);
+        // Starting from ICU4.8 release, the default minimalDaysInFirstWeek changed from 4 to 1.
         calendar.setMinimalDaysInFirstWeek(4);
-        new SerializableTester<Calendar>(calendar, s).test();
+        new SerializationTester<Calendar>(calendar, s).test();
     }
 
     private void assertCalendarEquals(Calendar calendar,
diff --git a/luni/src/test/java/libcore/java/util/EnumSetTest.java b/luni/src/test/java/libcore/java/util/EnumSetTest.java
new file mode 100644
index 0000000..a74e9fc
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/EnumSetTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011 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.util;
+
+import java.io.InvalidObjectException;
+import java.util.EnumSet;
+import junit.framework.TestCase;
+import libcore.util.SerializationTester;
+
+public final class EnumSetTest extends TestCase {
+    public void testSmallEnumSetSerialization() {
+        assertTrue(Roshambo.values().length <= 64);
+        String s = "aced0005737200246a6176612e7574696c2e456e756d5365742453657269616c"
+                + "697a6174696f6e50726f78790507d3db7654cad10200024c000b656c656d656e7"
+                + "4547970657400114c6a6176612f6c616e672f436c6173733b5b0008656c656d65"
+                + "6e74737400115b4c6a6176612f6c616e672f456e756d3b7870767200266c69626"
+                + "36f72652e6a6176612e7574696c2e456e756d5365745465737424526f7368616d"
+                + "626f00000000000000001200007872000e6a6176612e6c616e672e456e756d000"
+                + "00000000000001200007870757200115b4c6a6176612e6c616e672e456e756d3b"
+                + "a88dea2d33d22f980200007870000000027e71007e0004740004524f434b7e710"
+                + "07e000474000853434953534f5253";
+        EnumSet<Roshambo> set = EnumSet.of(Roshambo.ROCK, Roshambo.SCISSORS);
+        new SerializationTester<EnumSet<Roshambo>>(set, s).test();
+    }
+
+    public void testLargeEnumSetSerialization() {
+        assertTrue(Element.values().length > 64);
+        String s = "aced0005737200246a6176612e7574696c2e456e756d5365742453657269616c"
+                + "697a6174696f6e50726f78790507d3db7654cad10200024c000b656c656d656e7"
+                + "4547970657400114c6a6176612f6c616e672f436c6173733b5b0008656c656d65"
+                + "6e74737400115b4c6a6176612f6c616e672f456e756d3b7870767200256c69626"
+                + "36f72652e6a6176612e7574696c2e456e756d5365745465737424456c656d656e"
+                + "7400000000000000001200007872000e6a6176612e6c616e672e456e756d00000"
+                + "000000000001200007870757200115b4c6a6176612e6c616e672e456e756d3ba8"
+                + "8dea2d33d22f980200007870000000047e71007e0004740001487e71007e00047"
+                + "4000254427e71007e000474000244597e71007e000474000355554f";
+        EnumSet<Element> set = EnumSet.of(Element.H, Element.TB, Element.DY, Element.UUO);
+        new SerializationTester<EnumSet<Element>>(set, s).test();
+    }
+
+    public void testDeserializeRemovedValue() throws Exception {
+        /*
+         * enum Roshambo {
+         *     ROCK, PAPER, SCISSORS, LIZARD, SPOCK
+         * }
+         * EnumSet<Roshambo> set = EnumSet.of(Roshambo.SPOCK);
+         */
+        String s = "aced0005737200246a6176612e7574696c2e456e756d5365742453657269616c"
+                + "697a6174696f6e50726f78790507d3db7654cad10200024c000b656c656d656e7"
+                + "4547970657400114c6a6176612f6c616e672f436c6173733b5b0008656c656d65"
+                + "6e74737400115b4c6a6176612f6c616e672f456e756d3b7870767200266c69626"
+                + "36f72652e6a6176612e7574696c2e456e756d5365745465737424526f7368616d"
+                + "626f00000000000000001200007872000e6a6176612e6c616e672e456e756d000"
+                + "00000000000001200007870757200115b4c6a6176612e6c616e672e456e756d3b"
+                + "a88dea2d33d22f980200007870000000017e71007e000474000553504f434b";
+        try {
+            SerializationTester.deserializeHex(s);
+            fail();
+        } catch (InvalidObjectException expected) {
+        }
+    }
+
+    public void testDeserializeShuffledOrdinals() throws Exception {
+        /*
+         * enum Roshambo {
+         *     SCISSORS, ROCK, PAPER
+         * }
+         * EnumSet<Roshambo> set = EnumSet.of(Roshambo.SCISSORS, Roshambo.ROCK)
+         */
+        String s = "aced0005737200246a6176612e7574696c2e456e756d5365742453657269616c"
+                + "697a6174696f6e50726f78790507d3db7654cad10200024c000b656c656d656e7"
+                + "4547970657400114c6a6176612f6c616e672f436c6173733b5b0008656c656d65"
+                + "6e74737400115b4c6a6176612f6c616e672f456e756d3b7870767200266c69626"
+                + "36f72652e6a6176612e7574696c2e456e756d5365745465737424526f7368616d"
+                + "626f00000000000000001200007872000e6a6176612e6c616e672e456e756d000"
+                + "00000000000001200007870757200115b4c6a6176612e6c616e672e456e756d3b"
+                + "a88dea2d33d22f980200007870000000027e71007e000474000853434953534f5"
+                + "2537e71007e0004740004524f434b";
+        assertEquals(EnumSet.of(Roshambo.ROCK, Roshambo.SCISSORS),
+                SerializationTester.deserializeHex(s));
+    }
+
+    enum Roshambo {
+        ROCK, PAPER, SCISSORS
+    }
+
+    enum Element {
+        H, HE, LI, BE, B, C, N, O, F, NE, NA, MG, AL, SI, P, S, CL, AR, K, CA, SC, TI, V, CR, MN,
+        FE, CO, NI, CU, ZN, GA, GE, AS, SE, BR, KR, RB, SR, Y, ZR, NB, MO, TC, RU, RH, PD, AG, CD,
+        IN, SN, SB, TE, I, XE, CS, BA, LA, CE, PR, ND, PM, SM, EU, GD, TB, DY, HO, ER, TM, YB, LU,
+        HF, TA, W, RE, OS, IR, PT, AU, HG, TL, PB, BI, PO, AT, RN, FR, RA, AC, TH, PA, U, NP, PU,
+        AM, CM, BK, CF, ES, FM, MD, NO, LR, RF, DB, SG, BH, HS, MT, DS, RG, CN, UUT, UUQ, UUP, UUH,
+        UUS, UUO
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/EventObjectTest.java b/luni/src/test/java/libcore/java/util/EventObjectTest.java
index a80e8c7..dc87d61 100644
--- a/luni/src/test/java/libcore/java/util/EventObjectTest.java
+++ b/luni/src/test/java/libcore/java/util/EventObjectTest.java
@@ -19,6 +19,7 @@
 
 import java.util.EventObject;
 import junit.framework.TestCase;
+import libcore.util.SerializationTester;
 
 public final class EventObjectTest extends TestCase {
 
@@ -44,7 +45,7 @@
                 + "c8d094e186d7da80200007870";
         Object source = new Object();
         EventObject eventObject = new EventObject(source);
-        new SerializableTester<EventObject>(eventObject, s) {
+        new SerializationTester<EventObject>(eventObject, s) {
             @Override protected boolean equals(EventObject a, EventObject b) {
                 return a.getSource() == null
                         || b.getSource() == null
diff --git a/luni/src/test/java/libcore/java/util/OldCollectionsTest.java b/luni/src/test/java/libcore/java/util/OldCollectionsTest.java
index f54fc56..8a31ba7 100644
--- a/luni/src/test/java/libcore/java/util/OldCollectionsTest.java
+++ b/luni/src/test/java/libcore/java/util/OldCollectionsTest.java
@@ -17,8 +17,6 @@
 
 package libcore.java.util;
 
-import junit.framework.TestCase;
-
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -38,12 +36,11 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.Vector;
-
+import junit.framework.TestCase;
+import libcore.util.SerializationTester;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert;
 
-import tests.util.SerializationTester;
-
 public class OldCollectionsTest extends TestCase {
 
     private static final SerializableAssert comparator = new SerializableAssert() {
@@ -603,44 +600,147 @@
         SerializationTest.verifySelf(Collections.EMPTY_SET, comparator);
     }
 
-    public void test_checkedCollectionSerializationCompatability() throws Exception {
-        Collection<String> c = Collections.emptySet();
-        c = Collections.checkedCollection(c, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedCollection.golden.ser");
+    public void test_checkedCollectionSerializationCompatibility() throws Exception {
+        String s = "aced0005737200276a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b6564436f6c6c656374696f6e15e96dfd18e6cc6f0200034c00016374001"
+                + "64c6a6176612f7574696c2f436f6c6c656374696f6e3b4c000474797065740011"
+                + "4c6a6176612f6c616e672f436c6173733b5b00167a65726f4c656e677468456c6"
+                + "56d656e7441727261797400135b4c6a6176612f6c616e672f4f626a6563743b78"
+                + "707372001e6a6176612e7574696c2e436f6c6c656374696f6e7324456d7074795"
+                + "3657415f5721db403cb280200007870767200106a6176612e6c616e672e537472"
+                + "696e67a0f0a4387a3bb342020000787070";
+        assertSerialized(Collections.checkedCollection(
+                Collections.<String>emptySet(), String.class), s, false);
     }
-    public void test_checkedListRandomAccessSerializationCompatability() throws Exception {
-        List<String> c = new ArrayList<String>();
-        assertTrue(c instanceof RandomAccess);
-        c = Collections.checkedList(c, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedListRandomAccess.golden.ser");
+    public void test_checkedListRandomAccessSerializationCompatibility() throws Exception {
+        String s = "aced00057372002d6a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b656452616e646f6d4163636573734c69737416bc0e55a2d7f2f10200007"
+                + "87200216a6176612e7574696c2e436f6c6c656374696f6e7324436865636b6564"
+                + "4c69737400e7ce7692c45f7c0200014c00046c6973747400104c6a6176612f757"
+                + "4696c2f4c6973743b787200276a6176612e7574696c2e436f6c6c656374696f6e"
+                + "7324436865636b6564436f6c6c656374696f6e15e96dfd18e6cc6f0200034c000"
+                + "1637400164c6a6176612f7574696c2f436f6c6c656374696f6e3b4c0004747970"
+                + "657400114c6a6176612f6c616e672f436c6173733b5b00167a65726f4c656e677"
+                + "468456c656d656e7441727261797400135b4c6a6176612f6c616e672f4f626a65"
+                + "63743b7870737200136a6176612e7574696c2e41727261794c6973747881d21d9"
+                + "9c7619d03000149000473697a6578700000000077040000000a78767200106a61"
+                + "76612e6c616e672e537472696e67a0f0a4387a3bb34202000078707071007e0009";
+        assertSerialized(Collections.checkedList(new ArrayList<String>(), String.class), s, true);
     }
-    public void test_checkedListSerializationCompatability() throws Exception {
-        List<String> c = new LinkedList<String>();
-        assertFalse(c instanceof RandomAccess);
-        c = Collections.checkedList(c, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedList.golden.ser");
+    public void test_checkedListSerializationCompatibility() throws Exception {
+        String s = "aced0005737200216a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b65644c69737400e7ce7692c45f7c0200014c00046c6973747400104c6a6"
+                + "176612f7574696c2f4c6973743b787200276a6176612e7574696c2e436f6c6c65"
+                + "6374696f6e7324436865636b6564436f6c6c656374696f6e15e96dfd18e6cc6f0"
+                + "200034c0001637400164c6a6176612f7574696c2f436f6c6c656374696f6e3b4c"
+                + "0004747970657400114c6a6176612f6c616e672f436c6173733b5b00167a65726"
+                + "f4c656e677468456c656d656e7441727261797400135b4c6a6176612f6c616e67"
+                + "2f4f626a6563743b7870737200146a6176612e7574696c2e4c696e6b65644c697"
+                + "3740c29535d4a608822030000787077040000000078767200106a6176612e6c61"
+                + "6e672e537472696e67a0f0a4387a3bb34202000078707071007e0008";
+        assertSerialized(Collections.checkedList(new LinkedList<String>(), String.class), s, true);
     }
-    public void test_checkedSetSerializationCompatability() throws Exception {
-        Set<String> c = new HashSet<String>();
-        assertFalse(c instanceof SortedSet);
-        c = Collections.checkedSet(c, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSet.golden.ser");
+    public void test_checkedSetSerializationCompatibility() throws Exception {
+        String s = "aced0005737200206a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b656453657441249ba27ad9ffab020000787200276a6176612e7574696c2"
+                + "e436f6c6c656374696f6e7324436865636b6564436f6c6c656374696f6e15e96d"
+                + "fd18e6cc6f0200034c0001637400164c6a6176612f7574696c2f436f6c6c65637"
+                + "4696f6e3b4c0004747970657400114c6a6176612f6c616e672f436c6173733b5b"
+                + "00167a65726f4c656e677468456c656d656e7441727261797400135b4c6a61766"
+                + "12f6c616e672f4f626a6563743b7870737200116a6176612e7574696c2e486173"
+                + "68536574ba44859596b8b7340300007870770c000000103f40000000000000787"
+                + "67200106a6176612e6c616e672e537472696e67a0f0a4387a3bb3420200007870"
+                + "70";
+        assertSerialized(Collections.checkedSet(new HashSet<String>(), String.class), s, true);
     }
-    public void test_checkedMapSerializationCompatability() throws Exception {
-        Map<String, String> c = new HashMap<String, String>();
-        assertFalse(c instanceof SortedMap);
-        c = Collections.checkedMap(c, String.class, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedMap.golden.ser");
+    public void test_checkedMapSerializationCompatibility() throws Exception {
+        String s = "aced0005737200206a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b65644d61704fb2bcdf0d1863680200054c00076b6579547970657400114"
+                + "c6a6176612f6c616e672f436c6173733b4c00016d74000f4c6a6176612f757469"
+                + "6c2f4d61703b4c000976616c75655479706571007e00015b00127a65726f4c656"
+                + "e6774684b657941727261797400135b4c6a6176612f6c616e672f4f626a656374"
+                + "3b5b00147a65726f4c656e67746856616c7565417272617971007e00037870767"
+                + "200106a6176612e6c616e672e537472696e67a0f0a4387a3bb342020000787073"
+                + "7200116a6176612e7574696c2e486173684d61700507dac1c31660d1030002460"
+                + "00a6c6f6164466163746f724900097468726573686f6c6478703f400000000000"
+                + "0c770800000010000000007871007e00067070";
+        assertSerialized(Collections.checkedMap(
+                new HashMap<String, String>(), String.class, String.class), s);
     }
-    public void test_checkedSortedSetSerializationCompatability() throws Exception {
-        SortedSet<String> c = new TreeSet<String>();
-        c = Collections.checkedSortedSet(c, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedSet.golden.ser");
+    public void test_checkedSortedSetSerializationCompatibility() throws Exception {
+        String s = "aced0005737200266a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b6564536f72746564536574163406ba7362eb0f0200014c0002737374001"
+                + "54c6a6176612f7574696c2f536f727465645365743b787200206a6176612e7574"
+                + "696c2e436f6c6c656374696f6e7324436865636b656453657441249ba27ad9ffa"
+                + "b020000787200276a6176612e7574696c2e436f6c6c656374696f6e7324436865"
+                + "636b6564436f6c6c656374696f6e15e96dfd18e6cc6f0200034c0001637400164"
+                + "c6a6176612f7574696c2f436f6c6c656374696f6e3b4c0004747970657400114c"
+                + "6a6176612f6c616e672f436c6173733b5b00167a65726f4c656e677468456c656"
+                + "d656e7441727261797400135b4c6a6176612f6c616e672f4f626a6563743b7870"
+                + "737200116a6176612e7574696c2e54726565536574dd98509395ed875b0300007"
+                + "8707077040000000078767200106a6176612e6c616e672e537472696e67a0f0a4"
+                + "387a3bb34202000078707071007e0009";
+        assertSerialized(Collections.checkedSortedSet(new TreeSet<String>(), String.class), s, true);
     }
-    public void test_checkedSortedMapSerializationCompatability() throws Exception {
-        SortedMap<String, String> c = new TreeMap<String, String>();
-        c = Collections.checkedSortedMap(c, String.class, String.class);
-        SerializationTester.assertCompatibilityEquals(c, "/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedMap.golden.ser");
+    public void test_checkedSortedMapSerializationCompatibility() throws Exception {
+        String s = "aced0005737200266a6176612e7574696c2e436f6c6c656374696f6e73244368"
+                + "65636b6564536f727465644d617016332c973afe036e0200014c0002736d74001"
+                + "54c6a6176612f7574696c2f536f727465644d61703b787200206a6176612e7574"
+                + "696c2e436f6c6c656374696f6e7324436865636b65644d61704fb2bcdf0d18636"
+                + "80200054c00076b6579547970657400114c6a6176612f6c616e672f436c617373"
+                + "3b4c00016d74000f4c6a6176612f7574696c2f4d61703b4c000976616c7565547"
+                + "9706571007e00035b00127a65726f4c656e6774684b657941727261797400135b"
+                + "4c6a6176612f6c616e672f4f626a6563743b5b00147a65726f4c656e677468566"
+                + "16c7565417272617971007e00057870767200106a6176612e6c616e672e537472"
+                + "696e67a0f0a4387a3bb3420200007870737200116a6176612e7574696c2e54726"
+                + "5654d61700cc1f63e2d256ae60300014c000a636f6d70617261746f727400164c"
+                + "6a6176612f7574696c2f436f6d70617261746f723b78707077040000000078710"
+                + "07e0008707071007e000b";
+        assertSerialized(Collections.checkedSortedMap(
+                new TreeMap<String, String>(), String.class, String.class), s);
+    }
+
+    private void assertSerialized(Collection<?> collection, String s, final boolean definesEquals) {
+        new SerializationTester<Collection<?>>(collection, s) {
+            @SuppressWarnings("unchecked")
+            @Override protected void verify(Collection<?> deserialized) throws Exception {
+                try {
+                    ((Collection) deserialized).add(Boolean.TRUE);
+                    fail();
+                } catch (ClassCastException expected) {
+                }
+            }
+            @Override protected boolean equals(Collection<?> a, Collection<?> b) {
+                boolean equal = definesEquals
+                        ? a.equals(b)
+                        : Arrays.equals(a.toArray(), b.toArray());
+                return equal
+                        && (a instanceof SortedSet == b instanceof SortedSet)
+                        && (a instanceof RandomAccess == b instanceof RandomAccess);
+            }
+        }.test();
+    }
+
+    private void assertSerialized(Map<?, ?> map, String s) {
+        new SerializationTester<Map<?, ?>>(map, s) {
+            @SuppressWarnings("unchecked")
+            @Override protected void verify(Map<?, ?> deserialized) throws Exception {
+                try {
+                    ((Map) deserialized).put(Boolean.TRUE, "a");
+                    fail();
+                } catch (ClassCastException expected) {
+                }
+                try {
+                    ((Map) deserialized).put("a", Boolean.TRUE);
+                    fail();
+                } catch (ClassCastException expected) {
+                }
+            }
+            @Override protected boolean equals(Map<?, ?> a, Map<?, ?> b) {
+                return super.equals(a, b)
+                        && (a instanceof SortedMap == b instanceof SortedMap);
+            }
+        }.test();
     }
 
     public void test_checkedCollectionLjava_util_CollectionLjava_lang_Class() {
diff --git a/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java b/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java
index c2e2a03..de24cf9 100644
--- a/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java
+++ b/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java
@@ -21,12 +21,9 @@
 import java.util.List;
 import java.util.PriorityQueue;
 import junit.framework.TestCase;
-import tests.util.SerializationTester;
+import libcore.util.SerializationTester;
 
 public class OldPriorityQueueTest extends TestCase {
-
-    private static final String SERIALIZATION_FILE_NAME = "/serialization/tests/api/java/util/PriorityQueue.golden.ser";
-
     public void test_ConstructorI() {
         PriorityQueue<Object> queue = new PriorityQueue<Object>(100);
         assertNotNull(queue);
@@ -65,43 +62,22 @@
     }
 
     public void test_Serialization() throws Exception {
-        Integer[] array = { 2, 45, 7, -12, 9, 23, 17, 1118, 10, 16, 39 };
-        List<Integer> list = Arrays.asList(array);
-        PriorityQueue<Integer> srcIntegerQueue = new PriorityQueue<Integer>(list);
-        PriorityQueue<Integer> destIntegerQueue = SerializationTester.getDeserializedObject(srcIntegerQueue);
-        Arrays.sort(array);
-        for (int i = 0; i < array.length; i++) {
-            assertEquals(array[i], destIntegerQueue.poll());
-        }
-        assertEquals(0, destIntegerQueue.size());
-    }
-
-    public void test_Serialization_casting() throws Exception {
-        Integer[] array = { 2, 45, 7, -12, 9, 23, 17, 1118, 10, 16, 39 };
-        List<Integer> list = Arrays.asList(array);
-        PriorityQueue<Integer> srcIntegerQueue = new PriorityQueue<Integer>(list);
-        Object dodgy = SerializationTester.getDeserializedObject((Object) srcIntegerQueue);
-        PriorityQueue<String> destStringQueue = (PriorityQueue<String>) dodgy;
-        // will not incur class cast exception.
-        Object o = destStringQueue.peek();
-        Arrays.sort(array);
-        Integer I = (Integer) o;
-        assertEquals(array[0], I);
-    }
-
-    public void test_SerializationCompatibility_cast() throws Exception {
-        Integer[] array = { 2, 45, 7, -12, 9, 23, 17, 1118, 10, 16, 39 };
-        List<Integer> list = Arrays.asList(array);
+        String s = "aced0005737200176a6176612e7574696c2e5072696f72697479517565756594"
+                + "da30b4fb3f82b103000249000473697a654c000a636f6d70617261746f7274001"
+                + "64c6a6176612f7574696c2f436f6d70617261746f723b78700000000b70770400"
+                + "00000c737200116a6176612e6c616e672e496e746567657212e2a0a4f78187380"
+                + "2000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac"
+                + "951d0b94e08b0200007870fffffff47371007e0003000000027371007e0003000"
+                + "000077371007e00030000000a7371007e0003000000097371007e000300000017"
+                + "7371007e0003000000117371007e00030000045e7371007e00030000002d73710"
+                + "07e0003000000107371007e00030000002778";
         PriorityQueue<Integer> srcIntegerQueue = new PriorityQueue<Integer>(
-                list);
-        PriorityQueue<String> destStringQueue = (PriorityQueue<String>) SerializationTester
-                .readObject(srcIntegerQueue, SERIALIZATION_FILE_NAME);
-
-        // will not incur class cast exception.
-        Object o = destStringQueue.peek();
-        Arrays.sort(array);
-        Integer I = (Integer) o;
-        assertEquals(array[0], I);
+                Arrays.asList(2, 45, 7, -12, 9, 23, 17, 1118, 10, 16, 39));
+        new SerializationTester<PriorityQueue<Integer>>(srcIntegerQueue, s) {
+            @Override protected boolean equals(PriorityQueue<Integer> a, PriorityQueue<Integer> b) {
+                return Arrays.equals(a.toArray(), b.toArray());
+            }
+        }.test();
     }
 
     private static class MockComparatorStringByLength implements
diff --git a/luni/src/test/java/libcore/java/util/SerializableTester.java b/luni/src/test/java/libcore/java/util/SerializableTester.java
deleted file mode 100644
index 50b6435..0000000
--- a/luni/src/test/java/libcore/java/util/SerializableTester.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import static junit.framework.Assert.*;
-import junit.framework.AssertionFailedError;
-
-public class SerializableTester<T> {
-
-    private final String golden;
-    private final T value;
-
-    public SerializableTester(T value, String golden) {
-        this.golden = golden;
-        this.value = value;
-    }
-
-    /**
-     * Returns true if {@code a} and {@code b} are equal. Override this if
-     * {@link Object#equals} isn't appropriate or sufficient for this tester's
-     * value type.
-     */
-    protected boolean equals(T a, T b) {
-        return a.equals(b);
-    }
-
-    /**
-     * Verifies that {@code deserialized} is valid. Implementations of this
-     * method may mutate {@code deserialized}.
-     */
-    protected void verify(T deserialized) {}
-
-    public void test() {
-        try {
-            if (golden == null || golden.length() == 0) {
-                fail("No golden value supplied! Consider using this: "
-                        + hexEncode(serialize(value)));
-            }
-
-            @SuppressWarnings("unchecked") // deserialize should return the proper type
-            T deserialized = (T) deserialize(hexDecode(golden));
-            assertTrue("User-constructed value doesn't equal deserialized golden value",
-                    equals(value, deserialized));
-
-            @SuppressWarnings("unchecked") // deserialize should return the proper type
-            T reserialized = (T) deserialize(serialize(value));
-            assertTrue("User-constructed value doesn't equal itself, reserialized",
-                    equals(value, reserialized));
-
-            // just a sanity check! if this fails, verify() is probably broken
-            verify(value);
-            verify(deserialized);
-            verify(reserialized);
-
-        } catch (Exception e) {
-            Error failure = new AssertionFailedError();
-            failure.initCause(e);
-            throw failure;
-        }
-    }
-
-    private static byte[] serialize(Object object) throws IOException {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        new ObjectOutputStream(out).writeObject(object);
-        return out.toByteArray();
-    }
-
-    private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
-        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
-        Object result = in.readObject();
-        assertEquals(-1, in.read());
-        return result;
-    }
-
-    private static String hexEncode(byte[] bytes) {
-        StringBuilder result = new StringBuilder(bytes.length * 2);
-        for (byte b : bytes) {
-            result.append(String.format("%02x", b));
-        }
-        return result.toString();
-    }
-
-    private static byte[] hexDecode(String s) {
-        byte[] result = new byte[s.length() / 2];
-        for (int i = 0; i < result.length; i++) {
-            result[i] = (byte) Integer.parseInt(s.substring(i*2, i*2 + 2), 16);
-        }
-        return result;
-    }
-
-    public static String serializeHex(Object object) throws IOException {
-        return hexEncode(serialize(object));
-    }
-
-    public static Object deserializeHex(String hex) throws IOException, ClassNotFoundException {
-        return deserialize(hexDecode(hex));
-    }
-}
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 9708c2a..484668b 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -149,6 +149,15 @@
         assertFalse(denver.hasSameRules(phoenix));
     }
 
+    // http://code.google.com/p/android/issues/detail?id=24036
+    public void testNullId() throws Exception {
+        try {
+            TimeZone.getTimeZone(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
     // http://b.corp.google.com/issue?id=6556561
     public void testCustomZoneIds() throws Exception {
         // These are all okay (and equivalent).
diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java
index 4a048f5..32fcba4 100644
--- a/luni/src/test/java/libcore/java/util/TreeMapTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java
@@ -26,11 +26,12 @@
 import java.util.SortedMap;
 import java.util.TreeMap;
 import junit.framework.TestCase;
+import libcore.util.SerializationTester;
 
 public class TreeMapTest extends TestCase {
 
     /**
-     * Test that the entrySet() method produces correctly mutable Entrys.
+     * Test that the entrySet() method produces correctly mutable entries.
      */
     public void testEntrySetSetValue() {
         TreeMap<String, String> map = new TreeMap<String, String>();
@@ -38,7 +39,7 @@
         map.put("B", "b");
         map.put("C", "c");
 
-        Iterator<Entry<String,String>> iterator = map.entrySet().iterator();
+        Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
         Entry<String, String> entryA = iterator.next();
         assertEquals("a", entryA.setValue("x"));
         assertEquals("x", entryA.getValue());
@@ -54,8 +55,8 @@
     }
 
     /**
-     * Test that the entrySet() method of a submap produces correctly mutable Entrys that
-     * propagate changes to the original map.
+     * Test that the entrySet() method of a sub map produces correctly mutable
+     * entries that propagate changes to the original map.
      */
     public void testSubMapEntrySetSetValue() {
         TreeMap<String, String> map = new TreeMap<String, String>();
@@ -65,7 +66,7 @@
         map.put("D", "d");
         NavigableMap<String, String> subMap = map.subMap("A", true, "C", true);
 
-        Iterator<Entry<String,String>> iterator = subMap.entrySet().iterator();
+        Iterator<Entry<String, String>> iterator = subMap.entrySet().iterator();
         Entry<String, String> entryA = iterator.next();
         assertEquals("a", entryA.setValue("x"));
         assertEquals("x", entryA.getValue());
@@ -96,7 +97,7 @@
     }
 
     /**
-     * Test that an Entry given by any method except entrySet() of a submap is immutable.
+     * Test that an Entry given by any method except entrySet() of a sub map is immutable.
      */
     public void testExceptionsOnSubMapSetValue() {
         TreeMap<String, String> map = new TreeMap<String, String>();
@@ -270,7 +271,7 @@
                 + "e60300014c000a636f6d70617261746f727400164c6a6176612f7574696c2f436"
                 + "f6d70617261746f723b78707077040000000078";
         TreeMap<String, String> map = new TreeMap<String, String>();
-        new SerializableTester<TreeMap<String, String>>(map, s).test();
+        new SerializationTester<TreeMap<String, String>>(map, s).test();
     }
 
     public void testSerializationWithComparator() {
@@ -279,18 +280,18 @@
                 + "f6d70617261746f723b78707372002a6a6176612e6c616e672e537472696e6724"
                 + "43617365496e73656e736974697665436f6d70617261746f7277035c7d5c50e5c"
                 + "e02000078707704000000027400016171007e00057400016271007e000678";
-        TreeMap<String,String> map = new TreeMap<String, String>(
+        TreeMap<String, String> map = new TreeMap<String, String>(
                 String.CASE_INSENSITIVE_ORDER);
         map.put("a", "a");
         map.put("b", "b");
-        new SerializableTester<NavigableMap<String, String>>(map, s) {
+        new SerializationTester<NavigableMap<String, String>>(map, s) {
             @Override protected void verify(NavigableMap<String, String> deserialized) {
                 assertEquals(0, deserialized.comparator().compare("X", "x"));
             }
         }.test();
     }
 
-    public void testSubmapSerialization() {
+    public void testSubMapSerialization() {
         String s = "aced0005737200216a6176612e7574696c2e547265654d617024417363656e646"
                 + "96e675375624d61700cab946d1f0fab1c020000787200216a6176612e7574696c2"
                 + "e547265654d6170244e6176696761626c655375624d6170e2d0a70e64210e08020"
@@ -304,25 +305,25 @@
                 + "97665436f6d70617261746f7277035c7d5c50e5ce0200007870770400000004710"
                 + "07e000671007e00067400016271007e000c71007e000571007e000574000164710"
                 + "07e000d78";
-        TreeMap<String,String> map = new TreeMap<String, String>(
+        TreeMap<String, String> map = new TreeMap<String, String>(
                 String.CASE_INSENSITIVE_ORDER);
         map.put("a", "a");
         map.put("b", "b");
         map.put("c", "c");
         map.put("d", "d");
-        SortedMap<String, String> submap = map.subMap("a", "c");
-        new SerializableTester<SortedMap<String, String>>(submap, s) {
+        SortedMap<String, String> subMap = map.subMap("a", "c");
+        new SerializationTester<SortedMap<String, String>>(subMap, s) {
             @Override protected void verify(SortedMap<String, String> deserialized) {
                 try {
                     deserialized.put("e", "e");
                     fail();
-                } catch (IllegalArgumentException e) {
+                } catch (IllegalArgumentException expected) {
                 }
             }
         }.test();
     }
 
-    public void testNavigableSubmapSerialization() {
+    public void testNavigableSubMapSerialization() {
         String s = "aced0005737200216a6176612e7574696c2e547265654d617024417363656e646"
                 + "96e675375624d61700cab946d1f0fab1c020000787200216a6176612e7574696c2"
                 + "e547265654d6170244e6176696761626c655375624d6170e2d0a70e64210e08020"
@@ -336,19 +337,19 @@
                 + "97665436f6d70617261746f7277035c7d5c50e5ce0200007870770400000004710"
                 + "07e000671007e00067400016271007e000c71007e000571007e000574000164710"
                 + "07e000d78";
-        TreeMap<String,String> map = new TreeMap<String, String>(
+        TreeMap<String, String> map = new TreeMap<String, String>(
                 String.CASE_INSENSITIVE_ORDER);
         map.put("a", "a");
         map.put("b", "b");
         map.put("c", "c");
         map.put("d", "d");
-        SortedMap<String, String> submap = map.subMap("a", false, "c", true);
-        new SerializableTester<SortedMap<String, String>>(submap, s) {
+        SortedMap<String, String> subMap = map.subMap("a", false, "c", true);
+        new SerializationTester<SortedMap<String, String>>(subMap, s) {
             @Override protected void verify(SortedMap<String, String> deserialized) {
                 try {
                     deserialized.put("e", "e");
                     fail();
-                } catch (IllegalArgumentException e) {
+                } catch (IllegalArgumentException expected) {
                 }
             }
         }.test();
@@ -370,12 +371,12 @@
                 + "07e000b78737200286a6176612e7574696c2e436f6c6c656374696f6e732452657"
                 + "665727365436f6d70617261746f7232000003fa6c354d510200014c0003636d707"
                 + "1007e0001787071007e0009";
-        TreeMap<String,String> map = new TreeMap<String, String>(
+        TreeMap<String, String> map = new TreeMap<String, String>(
                 String.CASE_INSENSITIVE_ORDER);
         map.put("a", "a");
         map.put("b", "b");
         NavigableMap<String, String> descendingMap = map.descendingMap();
-        new SerializableTester<NavigableMap<String, String>>(descendingMap, s) {
+        new SerializationTester<NavigableMap<String, String>>(descendingMap, s) {
             @Override protected void verify(NavigableMap<String, String> deserialized) {
                 assertEquals("b", deserialized.navigableKeySet().first());
             }
@@ -388,11 +389,11 @@
                 + "f6d70617261746f723b78707372002a6a6176612e6c616e672e537472696e6724"
                 + "43617365496e73656e736974697665436f6d70617261746f7277035c7d5c50e5c"
                 + "e02000078707704000000027400016171007e00057400016271007e000678";
-        TreeMap<String,String> map = new TreeMap<String, String>(
+        TreeMap<String, String> map = new TreeMap<String, String>(
                 String.CASE_INSENSITIVE_ORDER);
         map.put("a", "a");
         map.put("b", "b");
-        new SerializableTester<TreeMap<String, String>>(map, s) {
+        new SerializationTester<TreeMap<String, String>>(map, s) {
             @Override protected void verify(TreeMap<String, String> deserialized) {
                 assertEquals(0, deserialized.comparator().compare("X", "x"));
             }
@@ -402,7 +403,7 @@
     /**
      * On JDK5, this fails with a NullPointerException after deserialization!
      */
-    public void testJava5SubmapSerialization() {
+    public void testJava5SubMapSerialization() {
         String s = "aced0005737200186a6176612e7574696c2e547265654d6170245375624d6170"
                 + "a5818343a213c27f0200055a000966726f6d53746172745a0005746f456e644c0"
                 + "00766726f6d4b65797400124c6a6176612f6c616e672f4f626a6563743b4c0006"
@@ -414,18 +415,18 @@
                 + "261746f7277035c7d5c50e5ce020000787077040000000471007e000471007e00"
                 + "047400016271007e000a7400016371007e000b7400016471007e000c7871007e0"
                 + "00b";
-        TreeMap<String,String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+        TreeMap<String, String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
         map.put("a", "a");
         map.put("b", "b");
         map.put("c", "c");
         map.put("d", "d");
-        SortedMap<String, String> submap = map.subMap("a", "c");
-        new SerializableTester<SortedMap<String, String>>(submap, s) {
+        SortedMap<String, String> subMap = map.subMap("a", "c");
+        new SerializationTester<SortedMap<String, String>>(subMap, s) {
             @Override protected void verify(SortedMap<String, String> deserialized) {
                 try {
                     deserialized.put("e", "e");
                     fail();
-                } catch (IllegalArgumentException e) {
+                } catch (IllegalArgumentException expected) {
                 }
             }
         }.test();
diff --git a/luni/src/test/java/libcore/java/util/TreeSetTest.java b/luni/src/test/java/libcore/java/util/TreeSetTest.java
new file mode 100644
index 0000000..93b4982
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/TreeSetTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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.util;
+
+import java.util.NavigableSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import junit.framework.TestCase;
+import libcore.util.SerializationTester;
+
+public final class TreeSetTest extends TestCase {
+
+    public void testEmptySetSerialization() {
+        String s = "aced0005737200116a6176612e7574696c2e54726565536574dd98509395ed87"
+                + "5b03000078707077040000000078";
+        TreeSet<String> set = new TreeSet<String>();
+        new SerializationTester<TreeSet<String>>(set, s).test();
+    }
+
+    public void testSerializationWithComparator() {
+        String s = "aced0005737200116a6176612e7574696c2e54726565536574dd98509395ed87"
+                + "5b03000078707372002a6a6176612e6c616e672e537472696e672443617365496"
+                + "e73656e736974697665436f6d70617261746f7277035c7d5c50e5ce0200007870"
+                + "770400000002740001617400016278";
+        TreeSet<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+        set.add("a");
+        set.add("b");
+        new SerializationTester<NavigableSet<String>>(set, s) {
+            @Override protected void verify(NavigableSet<String> deserialized) {
+                assertEquals(0, deserialized.comparator().compare("X", "x"));
+            }
+        }.test();
+    }
+
+    public void testSubSetSerialization() {
+        String s = "aced0005737200116a6176612e7574696c2e54726565536574dd98509395ed87"
+                + "5b03000078707372002a6a6176612e6c616e672e537472696e672443617365496"
+                + "e73656e736974697665436f6d70617261746f7277035c7d5c50e5ce0200007870"
+                + "770400000002740001617400016278";
+        TreeSet<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+        set.add("a");
+        set.add("b");
+        set.add("c");
+        set.add("d");
+        final SortedSet<String> subSet = set.subSet("a", "c");
+        new SerializationTester<SortedSet<String>>(subSet, s) {
+            @Override protected void verify(SortedSet<String> deserialized) {
+                assertBounded(deserialized, deserialized == subSet);
+            }
+        }.test();
+    }
+
+    public void testNavigableSubSetSerialization() {
+        String s = "aced0005737200116a6176612e7574696c2e54726565536574dd98509395ed87"
+                + "5b03000078707372002a6a6176612e6c616e672e537472696e672443617365496"
+                + "e73656e736974697665436f6d70617261746f7277035c7d5c50e5ce0200007870"
+                + "770400000002740001627400016378";
+        TreeSet<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+        set.add("a");
+        set.add("b");
+        set.add("c");
+        set.add("d");
+        final SortedSet<String> subSet = set.subSet("a", false, "c", true);
+        new SerializationTester<SortedSet<String>>(subSet, s) {
+            @Override protected void verify(SortedSet<String> deserialized) {
+                assertBounded(deserialized, deserialized == subSet);
+            }
+        }.test();
+    }
+
+    /**
+     * Regrettably, serializing a TreeSet causes it to forget its bounds. This
+     * is unlike a serialized TreeMap which retains its bounds when serialized.
+     */
+    private void assertBounded(SortedSet<String> deserialized, boolean bounded) {
+        if (bounded) {
+            try {
+                deserialized.add("e");
+                fail();
+            } catch (IllegalArgumentException expected) {
+            }
+        } else {
+            assertTrue(deserialized.add("e"));
+            assertTrue(deserialized.remove("e"));
+        }
+    }
+
+    /**
+     * Test that TreeSet never attempts to serialize a non-serializable
+     * comparator. http://b/5552608
+     */
+    public void testDescendingSetSerialization() {
+        String s = "aced0005737200116a6176612e7574696c2e54726565536574dd98509395ed87"
+                + "5b0300007870737200276a6176612e7574696c2e436f6c6c656374696f6e73245"
+                + "2657665727365436f6d70617261746f7264048af0534e4ad00200007870770400"
+                + "000002740001627400016178";
+        TreeSet<String> set = new TreeSet<String>();
+        set.add("a");
+        set.add("b");
+        NavigableSet<String> descendingSet = set.descendingSet();
+        new SerializationTester<NavigableSet<String>>(descendingSet, s) {
+            @Override protected void verify(NavigableSet<String> deserialized) {
+                assertEquals("b", deserialized.first());
+            }
+        }.test();
+    }
+
+    public void testJava5SerializationWithComparator() {
+        String s = "aced0005737200116a6176612e7574696c2e54726565536574dd98509395ed87"
+                + "5b03000078707372002a6a6176612e6c616e672e537472696e672443617365496"
+                + "e73656e736974697665436f6d70617261746f7277035c7d5c50e5ce0200007870"
+                + "770400000002740001617400016278";
+        TreeSet<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+        set.add("a");
+        set.add("b");
+        new SerializationTester<TreeSet<String>>(set, s) {
+            @Override protected void verify(TreeSet<String> deserialized) {
+                assertEquals(0, deserialized.comparator().compare("X", "x"));
+            }
+        }.test();
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java b/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java
index 4c9d88d..3bfecc2 100644
--- a/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java
+++ b/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java
@@ -27,7 +27,7 @@
 import java.util.EventListener;
 import java.util.List;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public final class PropertyChangeSupportTest extends TestCase {
 
@@ -243,7 +243,7 @@
         support.addPropertyChangeListener("a", listenerToA);
         support.addPropertyChangeListener("a", listenerToA);
 
-        new SerializableTester<PropertyChangeSupport>(support, s) {
+        new SerializationTester<PropertyChangeSupport>(support, s) {
             @Override protected boolean equals(PropertyChangeSupport a, PropertyChangeSupport b) {
                 return describe(a.getPropertyChangeListeners())
                         .equals(describe(b.getPropertyChangeListeners()));
diff --git a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
index b10be33..a5ca2a2 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
@@ -28,7 +28,7 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public final class CopyOnWriteArrayListTest extends TestCase {
 
@@ -265,7 +265,7 @@
         List<String> contents = Arrays.asList("a", "b", "c", null, "e");
         CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(contents);
 
-        new SerializableTester<CopyOnWriteArrayList<String>>(list, s).test();
+        new SerializationTester<CopyOnWriteArrayList<String>>(list, s).test();
     }
 
     /**
diff --git a/luni/src/test/java/libcore/java/util/regex/OldAndroidRegexTest.java b/luni/src/test/java/libcore/java/util/regex/OldAndroidRegexTest.java
new file mode 100644
index 0000000..9d4c22d
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/regex/OldAndroidRegexTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2008 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.util.regex;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import junit.framework.TestCase;
+
+public final class OldAndroidRegexTest extends TestCase {
+    public void testMatches() throws Exception {
+        /* Tests class Matcher */
+
+        Pattern p = Pattern.compile("bcd");
+        Matcher m = p.matcher("bcd");
+        assertTrue("Should match.", m.matches());
+
+        /* Pattern in the middle */
+        p = Pattern.compile("bcd");
+        m = p.matcher("abcdefg");
+        assertFalse("Should not match.", m.matches());
+
+        /* Pattern at the head */
+        m = p.matcher("bcdefg");
+        assertFalse("Should not match.", m.matches());
+
+        /* Pattern at the tail */
+        m = p.matcher("abcd");
+        assertFalse("Should not match.", m.matches());
+
+        /* Make sure matches() doesn't change after calls to find() */
+        p = Pattern.compile(".*");
+        m = p.matcher("abc");
+        assertTrue(m.matches());
+        assertTrue(m.find());
+        assertTrue(m.matches());
+
+        p = Pattern.compile(".");
+        m = p.matcher("abc");
+        assertFalse(m.matches());
+        assertTrue(m.find());
+        assertFalse(m.matches());
+
+        /* Make sure matches() agrees after a reset() */
+        m.reset("z");
+        assertTrue(m.matches());
+
+        m.reset("xyz");
+        assertFalse(m.matches());
+
+        /* Tests class Pattern */
+
+        assertFalse("Erroneously matched partial string.  " +
+                "See http://b/issue?id=754601", Pattern.matches("er", "xer"));
+        assertFalse("Erroneously matched partial string.  " +
+                "See http://b/issue?id=754601", Pattern.matches("xe", "xer"));
+        assertTrue("Generic regex should match.",
+                Pattern.matches(".*", "bcd"));
+        assertTrue("Grouped regex should match.",
+                Pattern.matches("(b(c(d)))", "bcd"));
+        assertTrue("Grouped regex should match.",
+                Pattern.matches("(b)(c)(d)", "bcd"));
+    }
+
+    public void testGroupCount() throws Exception {
+        Pattern p = Pattern.compile(
+                "\\b(?:\\+?1)?"
+                        + "(?:[ -\\.])?"
+                        + "\\(?(\\d{3})?\\)?"
+                        + "(?:[ -\\.\\/])?"
+                        + "(\\d{3})"
+                        + "(?:[ -\\.])?"
+                        + "(\\d{4})\\b"
+        );
+
+        Matcher m = p.matcher("1 (919) 555-1212");
+
+        assertEquals("groupCount is incorrect, see http://b/issue?id=759412",
+                3, m.groupCount());
+    }
+
+    public void testGroups() throws Exception {
+        Pattern p = Pattern.compile("(b)([c|d])(z*)");
+        Matcher m = p.matcher("abcdefg");
+
+        /* Must call find() first, otherwise group*() are undefined. */
+        assertTrue(m.find());
+
+        assertEquals(3, m.groupCount());
+
+        assertEquals("bc", m.group(0));
+        assertEquals("b", m.group(1));
+        assertEquals("c", m.group(2));
+        assertEquals("", m.group(3));
+    }
+
+    public void testFind() throws Exception {
+        Pattern p = Pattern.compile(".");
+        Matcher m = p.matcher("abc");
+
+        assertTrue(m.find());
+        assertEquals("a", m.group(0));
+
+        assertTrue(m.find());
+        assertEquals("b", m.group(0));
+
+        assertTrue(m.find());
+        assertEquals("c", m.group(0));
+
+        assertFalse(m.find());
+    }
+
+    public void testReplaceAll() throws Exception {
+        // Begins with non-matching text, ends with matching text
+        Pattern p = Pattern.compile("a*b");
+        Matcher m = p.matcher("fooaabfooaabfooabfoob");
+
+        String r = m.replaceAll("-");
+        assertEquals("foo-foo-foo-foo-", r);
+
+        // Begins with matching text, ends with non-matching text
+        p = Pattern.compile("a*b");
+        m = p.matcher("aabfooaabfooabfoobfoo");
+
+        r = m.replaceAll("-");
+        assertEquals("-foo-foo-foo-foo", r);
+    }
+
+    public void testReplaceFirst() throws Exception {
+        // Begins with non-matching text, ends with matching text
+        Pattern p = Pattern.compile("a*b");
+        Matcher m = p.matcher("fooaabfooaabfooabfoob");
+
+        String r = m.replaceFirst("-");
+        assertEquals("foo-fooaabfooabfoob", r);
+
+        // Begins with matching text, ends with non-matching text
+        p = Pattern.compile("a*b");
+        m = p.matcher("aabfooaabfooabfoobfoo");
+
+        r = m.replaceFirst("-");
+        assertEquals("-fooaabfooabfoobfoo", r);
+    }
+
+    public void testSplit() throws Exception {
+        Pattern p = Pattern.compile(":");
+        String[] strings;
+
+        strings = p.split("boo:and:foo");
+        assertEquals(3, strings.length);
+        assertEquals("boo", strings[0]);
+        assertEquals("and", strings[1]);
+        assertEquals("foo", strings[2]);
+
+        strings = p.split("boo:and:foo", 2);
+        assertEquals(2, strings.length);
+        assertEquals("boo", strings[0]);
+        assertEquals("and:foo", strings[1]);
+
+        strings = p.split("boo:and:foo", 5);
+        assertEquals(3, strings.length);
+        assertEquals("boo", strings[0]);
+        assertEquals("and", strings[1]);
+        assertEquals("foo", strings[2]);
+
+        strings = p.split("boo:and:foo", -2);
+        assertEquals(3, strings.length);
+        assertEquals("boo", strings[0]);
+        assertEquals("and", strings[1]);
+        assertEquals("foo", strings[2]);
+
+        p = Pattern.compile("o");
+
+        strings = p.split("boo:and:foo");
+        assertEquals(3, strings.length);
+        assertEquals("b", strings[0]);
+        assertEquals("", strings[1]);
+        assertEquals(":and:f", strings[2]);
+
+        strings = p.split("boo:and:foo", 5);
+        assertEquals(5, strings.length);
+        assertEquals("b", strings[0]);
+        assertEquals("", strings[1]);
+        assertEquals(":and:f", strings[2]);
+        assertEquals("", strings[3]);
+        assertEquals("", strings[4]);
+
+        strings = p.split("boo:and:foo", -2);
+        assertEquals(5, strings.length);
+        assertEquals("b", strings[0]);
+        assertEquals("", strings[1]);
+        assertEquals(":and:f", strings[2]);
+        assertEquals("", strings[3]);
+        assertEquals("", strings[4]);
+
+        strings = p.split("boo:and:foo", 0);
+        assertEquals(3, strings.length);
+        assertEquals("b", strings[0]);
+        assertEquals("", strings[1]);
+        assertEquals(":and:f", strings[2]);
+    }
+
+    // -------------------------------------------------------------------
+    // Regression test for #1172774: Bug in Regex.java
+    // Regression test for #1216887: Regular expression match is very slow
+    public static final Pattern TOP_LEVEL_DOMAIN_PATTERN = Pattern.compile(
+            "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+            + "|(biz|b[abdefghijmnorstvwyz])"
+            + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
+            + "|d[ejkmoz]"
+            + "|(edu|e[cegrstu])"
+            + "|f[ijkmor]"
+            + "|(gov|g[abdefghilmnpqrstuwy])"
+            + "|h[kmnrtu]"
+            + "|(info|int|i[delmnoqrst])"
+            + "|(jobs|j[emop])"
+            + "|k[eghimnrwyz]"
+            + "|l[abcikrstuvy]"
+            + "|(mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
+            + "|(name|net|n[acefgilopruz])"
+            + "|(org|om)"
+            + "|(pro|p[aefghklmnrstwy])"
+            + "|qa"
+            + "|r[eouw]"
+            + "|s[abcdeghijklmnortuvyz]"
+            + "|(tel|travel|t[cdfghjklmnoprtvwz])"
+            + "|u[agkmsyz]"
+            + "|v[aceginu]"
+            + "|w[fs]"
+            + "|y[etu]"
+            + "|z[amw])");
+
+    public static final Pattern EMAIL_ADDRESS_PATTERN = Pattern.compile(
+            "[\\+a-zA-Z0-9\\.\\_\\%\\-]+\\@"
+            + "(("
+            + "[a-zA-Z0-9]\\.|"
+            + "([a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9]\\.)+)"
+            + TOP_LEVEL_DOMAIN_PATTERN
+            + ")");
+
+    public void testMonsterRegexCorrectness() {
+        assertTrue(EMAIL_ADDRESS_PATTERN.matcher("a+b@gmail.com").matches());
+    }
+
+    public void testMonsterRegexPerformance() {
+        long t0 = System.currentTimeMillis();
+        Matcher m = EMAIL_ADDRESS_PATTERN.matcher("donot repeate@RC8jjjjjjjjjjjjjjj");
+        assertFalse(m.find());
+        long t1 = System.currentTimeMillis();
+        System.out.println("RegEx performance test finished, took " + (t1 - t0) + " ms.");
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/OldAndroidZipStressTest.java b/luni/src/test/java/libcore/java/util/zip/OldAndroidZipStressTest.java
new file mode 100644
index 0000000..9196dc9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/OldAndroidZipStressTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 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.util.zip;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import junit.framework.TestCase;
+
+public final class OldAndroidZipStressTest extends TestCase {
+    /**
+     * JarEntry.getCertificates() is really slow. http://b/1046174
+     */
+    public void checkJarCertificates(File file) throws Exception {
+        JarFile jarFile = new JarFile(file);
+        JarEntry je = jarFile.getJarEntry("AndroidManifest.xml");
+        byte[] readBuffer = new byte[1024];
+
+        long t0 = System.currentTimeMillis();
+
+        // We must read the stream for the JarEntry to retrieve its certificates.
+        InputStream is = jarFile.getInputStream(je);
+        while (is.read(readBuffer, 0, readBuffer.length) != -1) {
+        }
+        is.close();
+        Certificate[] certs = je != null ? je.getCertificates() : null;
+
+        long t1 = System.currentTimeMillis();
+        System.out.println("loadCertificates() took " + (t1 - t0) + " ms");
+        if (certs == null) {
+            System.out.println("We have no certificates");
+        } else {
+            System.out.println("We have " + certs.length + " certificates");
+        }
+    }
+
+    private File[] getFiles() {
+        File[] result = new File("/system/app").listFiles(new FilenameFilter() {
+            public boolean accept(File dir, String name) {
+                return name.endsWith(".apk");
+            }
+        });
+        return result != null ? result : new File[0];
+    }
+
+    public void testJarCertificates() throws Exception {
+        for (File file : getFiles()) {
+            checkJarCertificates(file);
+        }
+    }
+
+    // Boot-time package scan is slow. Not expected to fail. Please see log if
+    // you are interested in the results. http://b/1212257
+    public void testZipStressManifest() throws Exception {
+        long time0 = System.currentTimeMillis();
+        byte[] buffer = new byte[512];
+        for (File file : getFiles()) {
+            System.out.println("ZIP stress test processing " + file + "...");
+
+            ZipFile zip = new ZipFile(file);
+            ZipEntry entry = zip.getEntry("AndroidManifest.xml");
+            InputStream stream = zip.getInputStream(entry);
+            int j = stream.read(buffer);
+            while (j != -1) {
+                j = stream.read(buffer);
+            }
+            stream.close();
+        }
+        long time1 = System.currentTimeMillis();
+        System.out.println("ZIP stress test finished, time was " + (time1- time0) + "ms");
+    }
+
+    public void testZipStressAllFiles() throws Exception {
+        long time0 = System.currentTimeMillis();
+        byte[] buffer = new byte[512];
+        for (File file : getFiles()) {
+            System.out.println("ZIP stress test processing " + file + "...");
+            ZipFile zip = new ZipFile(file);
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            while (entries.hasMoreElements()) {
+                InputStream stream = zip.getInputStream(entries.nextElement());
+                int j = stream.read(buffer);
+                while (j != -1) {
+                    j = stream.read(buffer);
+                }
+                stream.close();
+            }
+        }
+        long time1 = System.currentTimeMillis();
+        System.out.println("ZIP stress test finished, time was " + (time1- time0) + "ms");
+    }
+
+    private void assertEquals(byte[] a, byte[] b) {
+        assertTrue(Arrays.equals(a, b));
+    }
+
+    /**
+     * Native memory allocated by Deflater in system_server. The fix reduced
+     * some internal ZLIB buffers in size, so this test is trying to execute a
+     * lot of deflating to ensure that things are still working properly.
+     * http://b/1185084
+     */
+    public void testZipDeflateInflateStress() throws Exception {
+        final int DATA_SIZE = 16384;
+        Random random = new Random(42); // Seed makes test reproducible
+        // Outer loop selects "mode" of test.
+        for (int j = 1; j <= 2; j++) {
+            byte[] input = new byte[DATA_SIZE];
+
+            if (j == 1) {
+                // Totally random content
+                random.nextBytes(input);
+            } else {
+                // Random contents with longer repetitions
+                int pos = 0;
+                while (pos < input.length) {
+                    byte what = (byte)random.nextInt(256);
+                    int howMany = random.nextInt(32);
+                    if (pos + howMany >= input.length) {
+                        howMany = input.length - pos;
+                    }
+                    Arrays.fill(input, pos, pos + howMany, what);
+                    pos += howMany;
+                }
+            }
+
+            // Inner loop tries all 9 compression levels.
+            for (int i = 1; i <= 9; i++) {
+                System.out.println("ZipDeflateInflateStress test (" + j + "," + i + ")...");
+                byte[] zipped = new byte[2 * DATA_SIZE]; // Just to make sure...
+
+                Deflater deflater = new Deflater(i);
+                deflater.setInput(input);
+                deflater.finish();
+                deflater.deflate(zipped);
+                deflater.end();
+
+                byte[] output = new byte[DATA_SIZE];
+
+                Inflater inflater = new Inflater();
+                inflater.setInput(zipped);
+                inflater.finished();
+                inflater.inflate(output);
+                inflater.end();
+                assertEquals(input, output);
+            }
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index 89b430f..b7a6050 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -24,6 +24,7 @@
 import java.util.Enumeration;
 import java.util.Random;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
@@ -49,6 +50,7 @@
             while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
             is.close();
         }
+        zipFile.close();
     }
 
     public void testInflatingStreamsRequiringZipRefill() throws IOException {
@@ -58,13 +60,14 @@
         while (in.getNextEntry() != null) {
             while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
         }
+        in.close();
     }
 
     /**
      * Compresses a single random file into a .zip archive.
      */
     private File createZipFile(int uncompressedSize) throws IOException {
-        File result = File.createTempFile("OldZipFileTest", "zip");
+        File result = File.createTempFile("ZipFileTest", "zip");
         result.deleteOnExit();
 
         ZipOutputStream out = new ZipOutputStream(new FileOutputStream(result));
@@ -81,5 +84,44 @@
         out.closeEntry();
         out.close();
         return result;
-    }
+      }
+
+      public void testHugeZipFile() throws IOException {
+          int expectedEntryCount = 64*1024 - 1;
+          File f = createHugeZipFile(expectedEntryCount);
+          ZipFile zipFile = new ZipFile(f);
+          int entryCount = 0;
+          for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+              ZipEntry zipEntry = e.nextElement();
+              ++entryCount;
+          }
+          assertEquals(expectedEntryCount, entryCount);
+          zipFile.close();
+      }
+
+      public void testZip64Support() throws IOException {
+          try {
+              createHugeZipFile(64*1024);
+              fail(); // Make this test more like testHugeZipFile when we have Zip64 support.
+          } catch (ZipException expected) {
+          }
+      }
+
+      /**
+       * Compresses the given number of empty files into a .zip archive.
+       */
+      private File createHugeZipFile(int count) throws IOException {
+          File result = File.createTempFile("ZipFileTest", "zip");
+          result.deleteOnExit();
+
+          ZipOutputStream out = new ZipOutputStream(new FileOutputStream(result));
+          for (int i = 0; i < count; ++i) {
+              ZipEntry entry = new ZipEntry(Integer.toString(i));
+              out.putNextEntry(entry);
+              out.closeEntry();
+          }
+
+          out.close();
+          return result;
+      }
 }
diff --git a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
new file mode 100644
index 0000000..7cb7792
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2010 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.net.ssl;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.charset.Charsets;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import javax.net.ssl.DefaultHostnameVerifier;
+import javax.net.ssl.DistinguishedNameParser;
+import javax.security.auth.x500.X500Principal;
+import junit.framework.TestCase;
+
+public final class DefaultHostnameVerifierTest extends TestCase {
+    private static final int ALT_UNKNOWN = 0;
+    private static final int ALT_DNS_NAME = 2;
+    private static final int ALT_IPA_NAME = 7;
+
+    private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
+
+    public void testGetFirstCn() {
+        assertFirstCn("", null);
+        assertFirstCn("ou=xxx", null);
+        assertFirstCn("ou=xxx,cn=xxx", "xxx");
+        assertFirstCn("ou=xxx+cn=yyy,cn=zzz+cn=abc", "yyy");
+        assertFirstCn("cn=a,cn=b", "a");
+        assertFirstCn("cn=Cc,cn=Bb,cn=Aa", "Cc");
+        assertFirstCn("cn=imap.gmail.com", "imap.gmail.com");
+    }
+
+    public void testGetFirstCnWithOid() {
+        assertFirstCn("2.5.4.3=a,ou=xxx", "a");
+    }
+
+    public void testGetFirstCnWithQuotedStrings() {
+        assertFirstCn("cn=\"\\\" a ,=<>#;\"", "\" a ,=<>#;");
+        assertFirstCn("cn=abc\\,def", "abc,def");
+    }
+
+    public void testGetFirstCnWithUtf8() {
+        assertFirstCn("cn=Lu\\C4\\8Di\\C4\\87", "\u004c\u0075\u010d\u0069\u0107");
+    }
+
+    public void testGetFirstCnWithWhitespace() {
+        assertFirstCn("ou=a, cn=  a  b  ,o=x", "a  b");
+        assertFirstCn("cn=\"  a  b  \" ,o=x", "  a  b  ");
+    }
+
+    private void assertFirstCn(String dn, String expected) {
+        X500Principal principal = new X500Principal(dn);
+        assertEquals("dn:" + dn, expected, new DistinguishedNameParser(principal).find("cn"));
+    }
+
+    public void testVerify() {
+        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")));
+        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
+        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
+    }
+
+    /**
+     * If a subjectAltName extension of type ALT_DNS_NAME is present, that MUST
+     * be used as the identity and the CN should be ignored.
+     */
+    public void testSubjectAltNameAndCn() {
+        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
+        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
+        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")));
+    }
+
+    public void testSubjectAltNameWithWildcard() {
+        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")));
+    }
+
+    public void testSubjectAltNameWithIpAddress() {
+        assertTrue(verifier.verify("1.2.3.4", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
+        assertFalse(verifier.verify("1.2.3.5", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
+        assertTrue(verifier.verify("192.168.100.1", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")));
+    }
+
+    public void testUnknownSubjectAltName() {
+        // Has unknown subject alternative names
+        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
+        assertTrue(verifier.verify("2.33.44.55", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
+        assertFalse(verifier.verify("g.com", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
+        assertFalse(verifier.verify("2.33.44.1", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
+    }
+
+    public void testWildcardMatchesWildcardSuffix() {
+        assertTrue(verifier.verifyHostName("b.c.d", "*.b.c.d"));
+        assertTrue(verifier.verifyHostName("imap.google.com", "*.imap.google.com"));
+    }
+
+    public void testWildcardMatchingSubstring() {
+        assertTrue(verifier.verifyHostName("b.c.d", "b*.c.d"));
+        assertTrue(verifier.verifyHostName("imap.google.com", "ima*.google.com"));
+    }
+
+    public void testWildcardMatchingEmptySubstring() {
+        assertTrue(verifier.verifyHostName("imap.google.com", "imap*.google.com"));
+    }
+
+    public void testWildcardMatchesChildDomain() {
+        assertFalse(verifier.verifyHostName("a.b.c.d", "*.c.d"));
+    }
+
+    public void testVerifyHostName() {
+        assertTrue(verifier.verifyHostName("a.b.c.d", "a.b.c.d"));
+        assertTrue(verifier.verifyHostName("a.b.c.d", "*.b.c.d"));
+        assertFalse(verifier.verifyHostName("a.b.c.d", "*.*.c.d"));
+        assertTrue(verifier.verifyHostName("imap.google.com", "imap.google.com"));
+        assertFalse(verifier.verifyHostName("imap2.google.com", "imap.google.com"));
+        assertTrue(verifier.verifyHostName("imap.google.com", "*.google.com"));
+        assertTrue(verifier.verifyHostName("imap2.google.com", "*.google.com"));
+        assertFalse(verifier.verifyHostName("imap.google.com", "*.googl.com"));
+        assertFalse(verifier.verifyHostName("imap2.google2.com", "*.google3.com"));
+        assertFalse(verifier.verifyHostName("imap.google.com", "a*.google.com"));
+        assertFalse(verifier.verifyHostName("imap.google.com", "ix*.google.com"));
+        assertTrue(verifier.verifyHostName("imap.google.com", "iMap.Google.Com"));
+    }
+
+    public void testSubjectOnlyCert() throws Exception {
+        // subject: C=JP, CN=www.example.com
+        // subject alt names: n/a
+        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
+                + "MIIC0TCCAbmgAwIBAgIJANCQbJPPw31SMA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV\n"
+                + "BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjA1ODE4\n"
+                + "WhgPMjA2NDEwMTUyMDU4MThaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu\n"
+                + "ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsdUJk\n"
+                + "4KxADA3vlDHxNbyC27Ozw4yiSVzPTHUct471YmdDRW3orO2P5a5hRnUGV70gjH9X\n"
+                + "MU4oeOdWYAgXB9pxfLyr6621k1+uNrmaZtzp0ECH9twcwxNJJFDZsN7o9vt7V6Ej\n"
+                + "NN9weeqDr/aeQXo07a12vyVfR6jWO8jHB0e4aemwZNoYjNvM69fivQTse2ZoRVfj\n"
+                + "eSHhjRTX6I8ry4a31Hwt+fT1QiWWNN6o7+WOtpJAhX3eg4smhSD1svi2kOT8tdUe\n"
+                + "NS4hWlmXmumU9G4tI8PBurcLNTm7PB2lUlbn/IV18WavqKE/Uy/1WgAx+a1EJNdp\n"
+                + "i07AG1PsqaONKkf1AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAJrNsuL7fZZNC8gL\n"
+                + "BdePJ7DYW2e7mXANU3bCBe2BZqmXKQxKwibZnEsqA+yMLqcSd8uxISlyHY2tw9wT\n"
+                + "4wB9KPIttfNLbwn/rk+MbOTHpvyF60d9WhJJVUkPBl8D4VuPSl+VnlA54kU9dtZN\n"
+                + "+ZYdxYbNtSsI/Flz9SCoOV79W9GhN+uYJhv6RwyIMIHeMpZpyX1xSUVx5dZlmerQ\n"
+                + "WAUvghDH3fFRt2ZdnA4OXoKkTAaM3Pv7PUMsnah8bux6MQi0AuLMWFWOI1H34koH\n"
+                + "rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk\n"
+                + "taXDWI4=\n"
+                + "-----END CERTIFICATE-----");
+        assertTrue(verifier.verify("www.example.com", cert));
+        assertFalse(verifier.verify("www2.example.com", cert));
+    }
+
+    public void testSubjectAltOnlyCert() throws Exception {
+        // subject: C=JP (no CN)
+        // subject alt names: DNS:www.example.com
+        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
+                + "MIICvTCCAaWgAwIBAgIJALbA0TZk2YmNMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV\n"
+                + "BAYTAkpQMCAXDTEwMDExMjIwNTg1NFoYDzIwNjQxMDE1MjA1ODU0WjANMQswCQYD\n"
+                + "VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEg6acVC9V4\n"
+                + "xNGoLNVLPbqBc8IvMvcsc88dF6MW3d9VagX3aeWU8c79tI/KOV/1AOakH7WYxw/w\n"
+                + "yD8aOX7+9BK1Hu0qKKKbSM+ycqaMthXd6xytrNDsIx5WiGUz8zTko0Gk3orIR7p7\n"
+                + "rPcNzB/zwtESkscqPv85aEn7S/yClNkzLfEzm3CtaYOc0tfhBMyzi/ipXzGMxUmx\n"
+                + "PvOLr3v/Oz5pZEQw7Kxlm4+tAtn7bJlHziQ1UW4WPIy+T3hySBEpODFiqZi7Ok3X\n"
+                + "Zjxdii62fgo5B2Ee7q5Amo0mUIwcQTDjJ2CLAqzYnSh3tpiPJGjEIjmRyCoMQ1bx\n"
+                + "7D+y7nSPIq8CAwEAAaMeMBwwGgYDVR0RBBMwEYIPd3d3LmV4YW1wbGUuY29tMA0G\n"
+                + "CSqGSIb3DQEBBQUAA4IBAQBsGEh+nHc0l9FJTzWqvG3qs7i6XoJZdtThCDx4HjKJ\n"
+                + "8GMrJtreNN4JvIxn7KC+alVbnILjzCRO+c3rsnpxKBi5cp2imjuw5Kf/x2Seimb9\n"
+                + "UvZbaJvBVOzy4Q1IGef9bLy3wZzy2/WfBFyvPTAkgkRaX7LN2jnYOYVhNoNFrwqe\n"
+                + "EWxkA6fzrpyseUEFeGFFjGxRSRCDcQ25Eq6d9rkC1x21zNtt4QwZBO0wHrTy155M\n"
+                + "JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT\n"
+                + "9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj\n"
+                + "-----END CERTIFICATE-----");
+        assertTrue(verifier.verify("www.example.com", cert));
+        assertFalse(verifier.verify("www2.example.com", cert));
+    }
+
+    public void testSubjectWithAltNamesCert() throws Exception {
+        // subject: C=JP, CN=www.example.com
+        // subject alt names: DNS:www2.example.com, DNS:www3.example.com
+        // * Subject should be ignored, because it has subject alt names.
+        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
+                + "MIIDBDCCAeygAwIBAgIJALv14qjcuhw9MA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV\n"
+                + "BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjA1OTM4\n"
+                + "WhgPMjA2NDEwMTUyMDU5MzhaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu\n"
+                + "ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiTVgU\n"
+                + "kBO9KNYZZLmiPR0eBrk8u61CLnm35BGKW8EFpDaINLbbIFIQvqOMekURON/N+xFY\n"
+                + "D8roo7aFZVuHWAUqFcOJ4e6NmviK5qocLihtzAexsw4f4AzZxM3A8kcLlWLyAt7e\n"
+                + "EVLxhcMHogY7GaF6q+33Z8p+zp6x3tj07mwyPrriCLse2PeRsRunZl/fp/VvRlr6\n"
+                + "YbC7CbRrhnIv5nqohs8BsbBiiFpxQftsMQmiXhY2LUzqY2RXUIOw24fHjoQkHTL2\n"
+                + "4z5nUM3b6ueQe+CBnobUS6fzK/36Nct4dRpev9i/ORdRLuIDKJ+QR16G1V/BJYBR\n"
+                + "dAK+3iXvg6z8vP1XAgMBAAGjMTAvMC0GA1UdEQQmMCSCEHd3dzIuZXhhbXBsZS5j\n"
+                + "b22CEHd3dzMuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAJQNf38uXm3h\n"
+                + "0vsF+Yd6/HqM48Su7tWnTDAfTXnQZZkzjzITq3JXzquMXICktAVN2cLnT9zPfRAE\n"
+                + "8V8A3BNO5zXiR5W3o/mJP5HQ3/WxpzBGM2N+YmDCJyBoQrIVaAZaXAZUaBBvn5A+\n"
+                + "kEVfGWquwIFuvA67xegbJOCRLD4eUzRdNsn5+NFiakWO1tkFqEzqyQ0PNPviRjgu\n"
+                + "z9NxdPvd1JQOhydkucsPKJzlEBbGyL5QL/Jkot3Qy+FOeuNzgQUfAGtQgzRrsZDK\n"
+                + "hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI\n"
+                + "h5G2eVGXH/Y=\n"
+                + "-----END CERTIFICATE-----");
+        assertFalse(verifier.verify("www.example.com", cert));
+        assertTrue(verifier.verify("www2.example.com", cert));
+        assertTrue(verifier.verify("www3.example.com", cert));
+        assertFalse(verifier.verify("www4.example.com", cert));
+    }
+
+    public void testSubjectWithWildAltNamesCert() throws Exception {
+        // subject: C=JP, CN=www.example.com
+        // subject alt names: DNS:*.example2.com
+        // * Subject should be ignored, because it has subject alt names.
+        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
+                + "MIIC8DCCAdigAwIBAgIJAL/oWJ64VAdXMA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV\n"
+                + "BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjEwMDAx\n"
+                + "WhgPMjA2NDEwMTUyMTAwMDFaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu\n"
+                + "ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbx1QB\n"
+                + "92iea7VybLYICA4MX4LWipYrRsgXUXQrcIQ3YLTQ9rH0VwScrHL4O4JDxgXCQnR+\n"
+                + "4VOzD42q1KXHJAqzqGUYCNPyvZEzkGCnQ4FBIUEmxZd5SNEefJVH3Z6GizYJomTh\n"
+                + "p78yDcoqymD9umxRC2cWFu8GscfFGMVyhsqLlOofu7UWOs22mkXPo43jDx+VOAoV\n"
+                + "n48YP3P57a2Eo0gcd4zVL00y62VegqBO/1LW38aTS7teiCBFc1TkNYa5I40yN9lP\n"
+                + "rB9ICHYQWyzf/7OxU9iauEK2w6DmSsQoLs9JzEhgeNZddkcc77ciSUCo2Hx0VpOJ\n"
+                + "BFyf2rbryJeAk+FDAgMBAAGjHTAbMBkGA1UdEQQSMBCCDiouZXhhbXBsZTIuY29t\n"
+                + "MA0GCSqGSIb3DQEBBQUAA4IBAQA2a14pRL+4laJ8sscQlucaDB/oSdb0cwhk4IkE\n"
+                + "kKl/ZKr6rKwPZ81sJRgzvI4imLbUAKt4AJHdpI9cIQUq1gw9bzil7LKwmFtFSPmC\n"
+                + "MYb1iadaYrvp7RE4yXrWCcSbU0hup9JQLHTrHLlqLtRuU48NHMvWYThBcS9Q/hQp\n"
+                + "nJ/JxYy3am99MHALWLAfuRxQXhE4C5utDmBwI2KD6A8SA30s+CnuegmkYScuSqBu\n"
+                + "Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT\n"
+                + "+ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ\n"
+                + "-----END CERTIFICATE-----");
+        assertFalse(verifier.verify("www.example.com", cert));
+        assertFalse(verifier.verify("www2.example.com", cert));
+        assertTrue(verifier.verify("www.example2.com", cert));
+        assertTrue(verifier.verify("abc.example2.com", cert));
+        assertFalse(verifier.verify("www.example3.com", cert));
+    }
+
+    public void testWildAltNameOnlyCert() throws Exception {
+        // subject: C=JP
+        // subject alt names: DNS:*.example.com
+        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
+                + "MIICuzCCAaOgAwIBAgIJAP82tgcvmAGxMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV\n"
+                + "BAYTAkpQMCAXDTEwMDExMjIxMDAyN1oYDzIwNjQxMDE1MjEwMDI3WjANMQswCQYD\n"
+                + "VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALs528EQbcB1\n"
+                + "x4BwxthQBZrgDJzoO7KPV3dhGYoeP8EnRjapZm+T/sj9P/O4HvfxjnB+fsjYSdmE\n"
+                + "WWUtnFrP7wtG9DUC748Ea2PMV8WFhOG58dqBNIko5XzkHB7SxkNZD5S/0KQYMGLr\n"
+                + "rchDsDlmsEf2Qb6qiqpNEU70aSkExZJcH+B9nWdeBpsVFu7wtezwSWEc2NUa2bhW\n"
+                + "gcXQ/aafwHZ4o2PyGwy0sgS/UifqO9tEllC2tPleSNJOmYsVudv5Bz4Q0GG38BSz\n"
+                + "Pc0IcOoln0ZWpXbGr03V2vlXWCwzaFAl3I1T3O7YVqDiaSWoP+d0tHZzmw8aJLXd\n"
+                + "B+KaUUGxRPsCAwEAAaMcMBowGAYDVR0RBBEwD4INKi5leGFtcGxlLmNvbTANBgkq\n"
+                + "hkiG9w0BAQUFAAOCAQEAJbVan4QgJ0cvpJnK9UWIVJNC+UbP87RC5go2fQiTnmGv\n"
+                + "prOrIuMqz1+vGcpIheLTLctJRHPoadXq0+UbQEIaU3pQbY6C4nNdfl+hcvmJeqrt\n"
+                + "kOCcvmIamO68iNvTSeszuHuu4O38PefrW2Xd0nn7bjFZrzBzHFhTudmnqNliP3ue\n"
+                + "KKQpqkUt5lCytnH8V/u/UCWdvVx5LnUa2XFGVLi3ongBIojW5fvF+yxn9ADqxdrI\n"
+                + "va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU\n"
+                + "qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==\n"
+                + "-----END CERTIFICATE-----");
+        assertTrue(verifier.verify("www.example.com", cert));
+        assertTrue(verifier.verify("www2.example.com", cert));
+        assertFalse(verifier.verify("www.example2.com", cert));
+    }
+
+    public void testAltIpOnlyCert() throws Exception {
+        // subject: C=JP
+        // subject alt names: IP Address:192.168.10.1
+        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
+                + "MIICsjCCAZqgAwIBAgIJALrC37YAXFIeMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV\n"
+                + "BAYTAkpQMCAXDTEwMDExMjIxMzk0NloYDzIwNjQxMDE1MjEzOTQ2WjANMQswCQYD\n"
+                + "VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr8s/4Abpby\n"
+                + "IYks5YCJE2nbWH7kj6XbwnRzsVP9RVC33bPoQ1M+2ZY24HqkigjQS/HEXR0s0bYh\n"
+                + "dewNUnTj1uGyGs6cYzsbu7x114vmVYqjxUo3hKjwfYiPeF6f3IE1vpLI7I2G32gq\n"
+                + "Zwm9c1/vXNHIdWQxCpFcuPA8P3YGfoApFX4pQPFplBUNAQqnjdmA68cbxxMC+1F3\n"
+                + "mX42D7iIEVwyVpah5HjyxjIZQlf3X7QBj0bCmkL+ibIHTALrkNNwNM6i4xzYLz/5\n"
+                + "14GkN9ncHY87eSOk6r53ptER6mQMhCe9qPRjSHnpWTTyj6IXTaYe+dDQw657B80w\n"
+                + "cSHL7Ed25zUCAwEAAaMTMBEwDwYDVR0RBAgwBocEwKgKATANBgkqhkiG9w0BAQUF\n"
+                + "AAOCAQEAgrwrtOWZT3fbi1AafpGaAiOBWSJqYqRhtQy0AfiZBxv1U0XaYqmZmpnq\n"
+                + "DVAqr0NkljowD28NBrxIFO5gBNum2ZOPDl2/5vjFn+IirUCJ9u9wS7zYkTCW2lQR\n"
+                + "xE7Ic3mfWv7wUbKDfjlWqP1IDHUxwkrBTAl+HnwOPiaKKk1ttwcrgS8AHlqASe03\n"
+                + "mlwnvJ+Stk54IneRaegL0L93sNAy63RZqnPCTxGz7eHcFwX8Jdr4sbxTxQqV6pIc\n"
+                + "WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik\n"
+                + "sAyifp8agkYdzaSh55fFmKXlFnRsQw==\n"
+                + "-----END CERTIFICATE-----");
+        assertTrue(verifier.verify("192.168.10.1", cert));
+        assertFalse(verifier.verify("192.168.10.2", cert));
+    }
+
+    X509Certificate parseCertificate(String encoded) throws Exception {
+        InputStream in = new ByteArrayInputStream(encoded.getBytes(Charsets.US_ASCII));
+        return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
+    }
+
+    private static class StubX509Certificate extends X509Certificate {
+        private final X500Principal subjectX500Principal;
+        private Collection<List<?>> subjectAlternativeNames;
+
+        public StubX509Certificate(String subjectDn) {
+            subjectX500Principal = new X500Principal(subjectDn);
+            subjectAlternativeNames = null;
+        }
+
+        public StubX509Certificate addSubjectAlternativeName(int type, String name) {
+            if (subjectAlternativeNames == null) {
+                subjectAlternativeNames = new ArrayList<List<?>>();
+            }
+            LinkedList<Object> entry = new LinkedList<Object>();
+            entry.add(type);
+            entry.add(name);
+            subjectAlternativeNames.add(entry);
+            return this;
+        }
+
+        @Override public Collection<List<?>> getSubjectAlternativeNames() {
+            return subjectAlternativeNames;
+        }
+
+        @Override public X500Principal getSubjectX500Principal() {
+            return subjectX500Principal;
+        }
+
+        @Override public void checkValidity() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public void checkValidity(Date date) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public int getBasicConstraints() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public Principal getIssuerDN() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public boolean[] getIssuerUniqueID() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public boolean[] getKeyUsage() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public Date getNotAfter() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public Date getNotBefore() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public BigInteger getSerialNumber() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public String getSigAlgName() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public String getSigAlgOID() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public byte[] getSigAlgParams() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public byte[] getSignature() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public Principal getSubjectDN() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public boolean[] getSubjectUniqueID() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public byte[] getTBSCertificate() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public int getVersion() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public byte[] getEncoded() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public PublicKey getPublicKey() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public String toString() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public void verify(PublicKey key) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public void verify(PublicKey key, String sigProvider) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public Set<String> getCriticalExtensionOIDs() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public byte[] getExtensionValue(String oid) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public Set<String> getNonCriticalExtensionOIDs() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override public boolean hasUnsupportedCriticalExtension() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index 5e91dc1..e3ae16f 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -166,7 +166,8 @@
         TestSSLContext c = TestSSLContext.create();
         SSLEngine e = c.clientContext.createSSLEngine();
         String[] protocols = e.getSupportedProtocols();
-        StandardNames.assertSupportedProtocols(StandardNames.SSL_SOCKET_PROTOCOLS, protocols);
+        StandardNames.assertSupportedProtocols(StandardNames.SSL_SOCKET_PROTOCOLS_SSLENGINE,
+                                               protocols);
         assertNotSame(protocols, e.getSupportedProtocols());
         c.close();
     }
diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/GeneralNameTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/GeneralNameTest.java
new file mode 100644
index 0000000..aac5e84
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/security/auth/x500/GeneralNameTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 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.security.auth.x500;
+
+import org.apache.harmony.security.x509.GeneralName;
+import junit.framework.TestCase;
+
+public final class GeneralNameTest extends TestCase {
+    // http://code.google.com/p/android/issues/detail?id=21311
+    public void testWildcardsInDnsName() throws Exception {
+        // examples of potential DNS wildcard locations from RFC 6125 section 7.2
+        new GeneralName(GeneralName.DNS_NAME, "*.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "fo*.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "f*o.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "*oo.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "www.*.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "www.foo*.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "*.co.uk");
+        new GeneralName(GeneralName.DNS_NAME, "*.com");
+        new GeneralName(GeneralName.DNS_NAME, "f*b*r.example.com");
+        new GeneralName(GeneralName.DNS_NAME, "*.*.example.com");
+    }
+}
diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
index 2687a82..e37ed1f 100644
--- a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
+++ b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
@@ -21,7 +21,7 @@
 import java.security.cert.X509Certificate;
 import javax.security.auth.x500.X500Principal;
 import junit.framework.TestCase;
-import libcore.java.util.SerializableTester;
+import libcore.util.SerializationTester;
 
 public class X500PrincipalTest extends TestCase {
 
@@ -37,7 +37,7 @@
                                                  + "L=Mountain View, "
                                                  + "O=Google Inc, "
                                                  + "CN=www.google.com");
-        new SerializableTester<X500Principal>(actual, expected).test();
+        new SerializationTester<X500Principal>(actual, expected).test();
     }
 
     /**
diff --git a/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
index a62a00e..360ca44 100644
--- a/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
+++ b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
@@ -155,12 +155,18 @@
 
     private void assertCached(boolean shouldPut, int responseCode) throws Exception {
         server = new MockWebServer();
-        server.enqueue(new MockResponse()
+        MockResponse response = new MockResponse()
                 .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
                 .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
                 .setResponseCode(responseCode)
                 .setBody("ABCDE")
-                .addHeader("WWW-Authenticate: challenge"));
+                .addHeader("WWW-Authenticate: challenge");
+        if (responseCode == HttpURLConnection.HTTP_PROXY_AUTH) {
+            response.addHeader("Proxy-Authenticate: Basic realm=\"protected area\"");
+        } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
+            response.addHeader("WWW-Authenticate: Basic realm=\"protected area\"");
+        }
+        server.enqueue(response);
         server.play();
 
         URL url = server.getUrl("/");
@@ -1503,12 +1509,12 @@
 
     public void testCachePlusCookies() throws Exception {
         server.enqueue(new MockResponse()
-                .addHeader("Set-Cookie: a=FIRST; domain=.local;")
+                .addHeader("Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
                 .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
                 .addHeader("Cache-Control: max-age=0")
                 .setBody("A"));
         server.enqueue(new MockResponse()
-                .addHeader("Set-Cookie: a=SECOND; domain=.local;")
+                .addHeader("Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
                 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
         server.play();
 
@@ -1613,6 +1619,37 @@
                 .addHeader("Cache-Control: max-age=60"));
     }
 
+    public void testConditionalHitUpdatesCache() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
+                .addHeader("Cache-Control: max-age=0")
+                .setBody("A"));
+        server.enqueue(new MockResponse()
+                .addHeader("Cache-Control: max-age=30")
+                .addHeader("Allow: GET, HEAD")
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.enqueue(new MockResponse().setBody("B"));
+        server.play();
+
+        // cache miss; seed the cache
+        HttpURLConnection connection1 = (HttpURLConnection) server.getUrl("/a").openConnection();
+        assertEquals("A", readAscii(connection1));
+        assertEquals(null, connection1.getHeaderField("Allow"));
+
+        // conditional cache hit; update the cache
+        HttpURLConnection connection2 = (HttpURLConnection) server.getUrl("/a").openConnection();
+        assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
+        assertEquals("A", readAscii(connection2));
+        assertEquals("GET, HEAD", connection2.getHeaderField("Allow"));
+
+        // full cache hit
+        HttpURLConnection connection3 = (HttpURLConnection) server.getUrl("/a").openConnection();
+        assertEquals("A", readAscii(connection3));
+        assertEquals("GET, HEAD", connection3.getHeaderField("Allow"));
+
+        assertEquals(2, server.getRequestCount());
+    }
+
     /**
      * @param delta the offset from the current date to use. Negative
      *     values yield dates in the past; positive values yield dates in the
@@ -1678,22 +1715,34 @@
      */
     private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
         // scenario 1: condition succeeds
-        server.enqueue(response.setBody("A"));
+        server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
         server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
 
         // scenario 2: condition fails
-        server.enqueue(response.setBody("B"));
-        server.enqueue(new MockResponse().setBody("C"));
+        server.enqueue(response.setBody("B").setStatus("HTTP/1.1 200 B-OK"));
+        server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 C-OK").setBody("C"));
 
         server.play();
 
         URL valid = server.getUrl("/valid");
-        assertEquals("A", readAscii(valid.openConnection()));
-        assertEquals("A", readAscii(valid.openConnection()));
+        HttpURLConnection connection1 = (HttpURLConnection) valid.openConnection();
+        assertEquals("A", readAscii(connection1));
+        assertEquals(HttpURLConnection.HTTP_OK, connection1.getResponseCode());
+        assertEquals("A-OK", connection1.getResponseMessage());
+        HttpURLConnection connection2 = (HttpURLConnection) valid.openConnection();
+        assertEquals("A", readAscii(connection2));
+        assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
+        assertEquals("A-OK", connection2.getResponseMessage());
 
         URL invalid = server.getUrl("/invalid");
-        assertEquals("B", readAscii(invalid.openConnection()));
-        assertEquals("C", readAscii(invalid.openConnection()));
+        HttpURLConnection connection3 = (HttpURLConnection) invalid.openConnection();
+        assertEquals("B", readAscii(connection3));
+        assertEquals(HttpURLConnection.HTTP_OK, connection3.getResponseCode());
+        assertEquals("B-OK", connection3.getResponseMessage());
+        HttpURLConnection connection4 = (HttpURLConnection) invalid.openConnection();
+        assertEquals("C", readAscii(connection4));
+        assertEquals(HttpURLConnection.HTTP_OK, connection4.getResponseCode());
+        assertEquals("C-OK", connection4.getResponseMessage());
 
         server.takeRequest(); // regular get
         return server.takeRequest(); // conditional get
diff --git a/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java b/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
index 0db2d4d..d7ba441 100644
--- a/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
+++ b/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
@@ -17,6 +17,8 @@
 package libcore.net.http;
 
 import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import junit.framework.TestCase;
 
@@ -184,4 +186,59 @@
         RequestHeaders parsedHeaders = new RequestHeaders(uri, headers);
         assertEquals(0, parsedHeaders.getMaxAgeSeconds());
     }
+
+    public void testParseChallengesWithCommaInRealm() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "s1 realm=\"ab,cd\", s2 realm=\"ef,gh\"");
+        assertEquals(Arrays.asList(new Challenge("s1", "ab,cd"), new Challenge("s2", "ef,gh")),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
+
+    public void testParseChallengesWithMultipleHeaders() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "s1 realm=\"abc\"");
+        headers.add("WWW-Authenticate", "s2 realm=\"def\"");
+        assertEquals(Arrays.asList(new Challenge("s1", "abc"), new Challenge("s2", "def")),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
+
+    public void testParseChallengesWithExtraWhitespace() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "  s1  realm=\"a\"  ,  s2  realm=\"b\",  ");
+        assertEquals(Arrays.asList(new Challenge("s1", "a"), new Challenge("s2", "b")),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
+
+    public void testParseChallengesWithMissingRealm() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "Basic");
+        assertEquals(Collections.<Challenge>emptyList(),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
+
+    public void testParseChallengesWithEmptyRealm() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "Basic realm=\"\"");
+        assertEquals(Arrays.asList(new Challenge("Basic", "")),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
+
+    public void testParseChallengesWithMissingScheme() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "realm=\"a\"");
+        assertEquals(Collections.<Challenge>emptyList(),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=11140
+    public void testParseChallengesWithManyParameters() {
+        RawHeaders headers = new RawHeaders();
+        headers.add("WWW-Authenticate", "Digest realm=\"abc\","
+                + " qop=\"auth,auth-int\","
+                + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+                + " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\","
+                + " Basic realm=\"def\"");
+        assertEquals(Arrays.asList(new Challenge("Digest", "abc"), new Challenge("Basic", "def")),
+                HeaderParser.parseChallenges(headers, "WWW-Authenticate"));
+    }
 }
diff --git a/luni/src/test/java/libcore/xml/DeclarationTest.java b/luni/src/test/java/libcore/xml/DeclarationTest.java
index 24cb32c..4cd049f 100644
--- a/luni/src/test/java/libcore/xml/DeclarationTest.java
+++ b/luni/src/test/java/libcore/xml/DeclarationTest.java
@@ -16,7 +16,6 @@
 
 package libcore.xml;
 
-import dalvik.annotation.KnownFailure;
 import junit.framework.TestCase;
 import org.w3c.dom.Document;
 import org.xml.sax.InputSource;
@@ -79,21 +78,18 @@
         assertEquals("ISO-8859-1", documentB.getInputEncoding());
     }
 
-    @KnownFailure("Dalvik doesn't parse the XML declaration")
     public void testGetXmlEncoding() throws Exception {
         String message = "This implementation doesn't parse the encoding from the XML declaration";
         assertEquals(message, "ISO-8859-1", documentA.getXmlEncoding());
         assertEquals(message, "US-ASCII", documentB.getXmlEncoding());
     }
 
-    @KnownFailure("Dalvik doesn't parse the XML declaration")
     public void testGetXmlVersion() throws Exception {
         String message = "This implementation doesn't parse the version from the XML declaration";
         assertEquals(message, "1.0", documentA.getXmlVersion());
         assertEquals(message, "1.1", documentB.getXmlVersion());
     }
 
-    @KnownFailure("Dalvik doesn't parse the XML declaration")
     public void testGetXmlStandalone() throws Exception {
         String message = "This implementation doesn't parse standalone from the XML declaration";
         assertEquals(message, false, documentA.getXmlStandalone());
diff --git a/luni/src/test/java/libcore/xml/DomTest.java b/luni/src/test/java/libcore/xml/DomTest.java
index 79440f5..88da565 100644
--- a/luni/src/test/java/libcore/xml/DomTest.java
+++ b/luni/src/test/java/libcore/xml/DomTest.java
@@ -16,7 +16,6 @@
 
 package libcore.xml;
 
-import dalvik.annotation.KnownFailure;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileWriter;
@@ -186,7 +185,6 @@
      * Android's parsed DOM doesn't include entity declarations. These nodes will
      * only be tested for implementations that support them.
      */
-    @KnownFailure("Dalvik doesn't parse entity declarations")
     public void testEntityDeclarations() {
         assertNotNull("This implementation does not parse entity declarations", sp);
     }
@@ -195,7 +193,6 @@
      * Android's parsed DOM doesn't include notations. These nodes will only be
      * tested for implementations that support them.
      */
-    @KnownFailure("Dalvik doesn't parse notations")
     public void testNotations() {
         assertNotNull("This implementation does not parse notations", png);
     }
@@ -555,12 +552,10 @@
         assertNoFeature("XMLVersion", "2.0");
     }
 
-    @KnownFailure("Dalvik doesn't support load/save")
     public void testLoadSaveFeature() {
         assertFeature("LS", "3.0");
     }
 
-    @KnownFailure("Dalvik doesn't support the element traversal feature")
     public void testElementTraversalFeature() {
         assertFeature("ElementTraversal", "1.0");
     }
@@ -657,7 +652,6 @@
         assertFalse(text.isElementContentWhitespace());
     }
 
-    @KnownFailure("Dalvik doesn't recognize element content whitespace")
     public void testIsElementContentWhitespaceWithDeclaration() throws Exception {
         String xml = "<!DOCTYPE menu [\n"
                 + "  <!ELEMENT menu (item)*>\n"
@@ -691,7 +685,6 @@
         assertEquals("60%", vitamincText.getWholeText());
     }
 
-    @KnownFailure("Dalvik doesn't resolve entity references")
     public void testGetWholeTextWithEntityReference() {
         EntityReference spReference = document.createEntityReference("sp");
         description.insertBefore(spReference, descriptionText2);
@@ -1241,7 +1234,6 @@
         assertNull(document.getElementById("g"));
     }
 
-    @KnownFailure("Dalvik treats id attributes as identifiers")
     public void testAttributeNamedIdIsNotAnIdByDefault() {
         String message = "This implementation incorrectly interprets the "
                 + "\"id\" attribute as an identifier by default.";
@@ -1385,7 +1377,6 @@
                 1, document.getChildNodes().getLength());
     }
 
-    @KnownFailure("Dalvik document nodes accept arbitrary child nodes")
     public void testDocumentAddChild()
             throws IOException, SAXException {
         try {
diff --git a/luni/src/test/java/libcore/xml/NormalizeTest.java b/luni/src/test/java/libcore/xml/NormalizeTest.java
index 5e539f7..59a529d 100644
--- a/luni/src/test/java/libcore/xml/NormalizeTest.java
+++ b/luni/src/test/java/libcore/xml/NormalizeTest.java
@@ -16,7 +16,6 @@
 
 package libcore.xml;
 
-import dalvik.annotation.KnownFailure;
 import junit.framework.TestCase;
 import org.w3c.dom.CDATASection;
 import org.w3c.dom.Comment;
@@ -205,7 +204,6 @@
      * This fails under the RI because setParameter() succeeds even though
      * canSetParameter() returns false.
      */
-    @KnownFailure("Dalvik doesn't honor the schema-type parameter")
     public void testSchemaTypeDtd() {
         assertUnsupported("schema-type", "http://www.w3.org/TR/REC-xml"); // supported in RI v6
     }
diff --git a/luni/src/test/java/libcore/xml/PullParserTest.java b/luni/src/test/java/libcore/xml/PullParserTest.java
index fa00fe5..71125aa 100644
--- a/luni/src/test/java/libcore/xml/PullParserTest.java
+++ b/luni/src/test/java/libcore/xml/PullParserTest.java
@@ -720,6 +720,28 @@
         assertParseFailure("\ufeff<?xml version='1.0'?><input/>");
     }
 
+    // http://code.google.com/p/android/issues/detail?id=21425
+    public void testNextTextAdvancesToEndTag() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>bar</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("bar", parser.nextText());
+        assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+    }
+
+    public void testNextTag() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo> <bar></bar> </foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.nextTag());
+        assertEquals("foo", parser.getName());
+        assertEquals(XmlPullParser.START_TAG, parser.nextTag());
+        assertEquals("bar", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.nextTag());
+        assertEquals("bar", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.nextTag());
+        assertEquals("foo", parser.getName());
+    }
+
     private void assertParseFailure(String xml) throws Exception {
         XmlPullParser parser = newPullParser();
         parser.setInput(new StringReader(xml));
diff --git a/luni/src/test/java/libcore/xml/SaxTest.java b/luni/src/test/java/libcore/xml/SaxTest.java
index 108104d..721809a 100644
--- a/luni/src/test/java/libcore/xml/SaxTest.java
+++ b/luni/src/test/java/libcore/xml/SaxTest.java
@@ -16,7 +16,6 @@
 
 package libcore.xml;
 
-import dalvik.annotation.KnownFailure;
 import java.io.StringReader;
 import java.util.Arrays;
 import java.util.List;
@@ -96,7 +95,6 @@
      * Android's Expat-based SAX parser fails this test because Expat doesn't
      * supply us with our much desired {@code xmlns="http://..."} attributes.
      */
-    @KnownFailure("No xmlns attributes from Expat")
     public void testYesPrefixesYesNamespaces() throws Exception {
         parse(true, true, "<foo bar=\"baz\"/>", new DefaultHandler() {
             @Override public void startElement(String uri, String localName,
diff --git a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java
index 2911192..c8df4ab 100644
--- a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java
+++ b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java
@@ -21,11 +21,8 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketTimeoutException;
-import java.security.KeyStore.PrivateKeyEntry;
 import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
+import java.security.KeyStore.PrivateKeyEntry;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -43,8 +40,8 @@
 import junit.framework.TestCase;
 import libcore.java.security.StandardNames;
 import libcore.java.security.TestKeyStore;
-import org.apache.harmony.xnet.provider.jsse.CipherSuite;
 import org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSLHandshakeCallbacks;
+import static org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH;
 
 public class NativeCryptoTest extends TestCase {
 
@@ -153,6 +150,28 @@
         NativeCrypto.SSL_CTX_free(NativeCrypto.SSL_CTX_new());
     }
 
+    public void test_SSL_CTX_set_session_id_context() throws Exception {
+        byte[] empty = new byte[0];
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(NULL, empty);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        int c = NativeCrypto.SSL_CTX_new();
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        NativeCrypto.SSL_CTX_set_session_id_context(c, empty);
+        NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[32]);
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[33]);
+        } catch (IllegalArgumentException expected) {
+        }
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
     public void test_SSL_new() throws Exception {
         int c = NativeCrypto.SSL_CTX_new();
         int s = NativeCrypto.SSL_new(c);
@@ -161,6 +180,8 @@
         assertTrue((NativeCrypto.SSL_get_options(s) & 0x01000000L) != 0); // SSL_OP_NO_SSLv2
         assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) == 0);
         assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_TLSv1) == 0);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_TLSv1_1) == 0);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_TLSv1_2) == 0);
 
         int s2 = NativeCrypto.SSL_new(c);
         assertTrue(s != s2);
@@ -290,7 +311,7 @@
         NativeCrypto.SSL_CTX_free(c);
     }
 
-    public void test_SSL_set_mode() throws Exception {
+    public void test_SSL_set_mode_and_clear_mode() throws Exception {
         try {
             NativeCrypto.SSL_set_mode(NULL, 0);
             fail();
@@ -299,38 +320,17 @@
 
         int c = NativeCrypto.SSL_CTX_new();
         int s = NativeCrypto.SSL_new(c);
-        // check SSL_MODE_HANDSHAKE_CUTTHROUGH on
-        assertTrue((NativeCrypto.SSL_get_mode(s)
-                    & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0);
-        // clear SSL_MODE_HANDSHAKE_CUTTHROUGH off
-        NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
-        assertTrue((NativeCrypto.SSL_get_mode(s)
-                    & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) == 0);
+        // check SSL_MODE_HANDSHAKE_CUTTHROUGH off by default
+        assertEquals(0, NativeCrypto.SSL_get_mode(s) & SSL_MODE_HANDSHAKE_CUTTHROUGH);
         // set SSL_MODE_HANDSHAKE_CUTTHROUGH on
-        NativeCrypto.SSL_set_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
+        NativeCrypto.SSL_set_mode(s, SSL_MODE_HANDSHAKE_CUTTHROUGH);
         assertTrue((NativeCrypto.SSL_get_mode(s)
-                    & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0);
-
-        NativeCrypto.SSL_free(s);
-        NativeCrypto.SSL_CTX_free(c);
-    }
-
-    public void test_SSL_clear_mode() throws Exception {
-        try {
-            NativeCrypto.SSL_clear_mode(NULL, 0);
-            fail();
-        } catch (NullPointerException expected) {
-        }
-
-        int c = NativeCrypto.SSL_CTX_new();
-        int s = NativeCrypto.SSL_new(c);
-        // check SSL_MODE_HANDSHAKE_CUTTHROUGH on
-        assertTrue((NativeCrypto.SSL_get_mode(s)
-                    & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0);
+                & SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0);
         // clear SSL_MODE_HANDSHAKE_CUTTHROUGH off
-        NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
+        NativeCrypto.SSL_clear_mode(s, SSL_MODE_HANDSHAKE_CUTTHROUGH);
         assertTrue((NativeCrypto.SSL_get_mode(s)
-                    & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) == 0);
+                    & SSL_MODE_HANDSHAKE_CUTTHROUGH) == 0);
+
         NativeCrypto.SSL_free(s);
         NativeCrypto.SSL_CTX_free(c);
     }
@@ -516,7 +516,6 @@
             this.asn1DerEncodedCertificateChain = asn1DerEncodedCertificateChain;
             this.authMethod = authMethod;
             this.verifyCertificateChainCalled = true;
-            return;
         }
 
         public byte[] keyTypes;
@@ -546,7 +545,6 @@
                                    + " handshakeCompleted");
             }
             this.handshakeCompletedCalled = true;
-            return;
         }
     }
 
@@ -575,11 +573,10 @@
     }
 
     public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener,
-                                                              final int timeout,
-                                                              final boolean client,
-                                                              final Hooks hooks) {
+            final int timeout, final boolean client, final Hooks hooks, final byte[] npnProtocols) {
         ExecutorService executor = Executors.newSingleThreadExecutor();
-        Future future = executor.submit(new Callable<TestSSLHandshakeCallbacks>() {
+        Future<TestSSLHandshakeCallbacks> future = executor.submit(
+                new Callable<TestSSLHandshakeCallbacks>() {
             public TestSSLHandshakeCallbacks call() throws Exception {
                 Socket socket = (client
                                  ? new Socket(listener.getInetAddress(),
@@ -601,11 +598,8 @@
                                        + " timeout=" + timeout
                                        + " client=" + client);
                 }
-                int session = NativeCrypto.SSL_do_handshake(s,
-                                                            fd,
-                                                            callback,
-                                                            timeout,
-                                                            client);
+                int session = NativeCrypto.SSL_do_handshake(s, fd, callback, timeout, client,
+                        npnProtocols);
                 if (DEBUG) {
                     System.out.println("ssl=0x" + Integer.toString(s, 16)
                                        + " handshake"
@@ -621,7 +615,7 @@
 
     public void test_SSL_do_handshake_NULL_SSL() throws Exception {
         try {
-            NativeCrypto.SSL_do_handshake(NULL, null, null, 0, false);
+            NativeCrypto.SSL_do_handshake(NULL, null, null, 0, false, null);
             fail();
         } catch (NullPointerException expected) {
         }
@@ -632,15 +626,15 @@
         int s = NativeCrypto.SSL_new(c);
 
         try {
-            NativeCrypto.SSL_do_handshake(s, null, null, 0, true);
+            NativeCrypto.SSL_do_handshake(s, null, null, 0, true, null);
             fail();
-        } catch (NullPointerException e) {
+        } catch (NullPointerException expected) {
         }
 
         try {
-            NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0, true);
+            NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0, true, null);
             fail();
-        } catch (NullPointerException e) {
+        } catch (NullPointerException expected) {
         }
 
         NativeCrypto.SSL_free(s);
@@ -650,17 +644,10 @@
     public void test_SSL_do_handshake_normal() throws Exception {
         // normal client and server case
         final ServerSocket listener = new ServerSocket(0);
-        Hooks cHooks = new Hooks() {
-            @Override
-            public int beforeHandshake(int context) throws SSLException {
-                int s = super.beforeHandshake(context);
-                NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
-                return s;
-            }
-        };
+        Hooks cHooks = new Hooks();
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
@@ -680,12 +667,6 @@
 
         Hooks cHooks = new Hooks() {
             @Override
-            public int beforeHandshake(int context) throws SSLException {
-                int s = super.beforeHandshake(context);
-                NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
-                return s;
-            }
-            @Override
             public void clientCertificateRequested(int s) {
                 super.clientCertificateRequested(s);
                 NativeCrypto.SSL_use_PrivateKey(s, getClientPrivateKey());
@@ -701,8 +682,8 @@
                 return s;
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
@@ -711,7 +692,7 @@
         assertEquals("RSA", clientCallback.authMethod);
         assertTrue(serverCallback.verifyCertificateChainCalled);
         assertEqualCertificateChains(getClientCertificates(),
-                                     serverCallback.asn1DerEncodedCertificateChain);
+                serverCallback.asn1DerEncodedCertificateChain);
         assertEquals("RSA", serverCallback.authMethod);
 
         assertTrue(clientCallback.clientCertificateRequestedCalled);
@@ -746,8 +727,8 @@
                     return s;
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
@@ -772,7 +753,7 @@
             @Override
             public int beforeHandshake(int context) throws SSLException {
                 int s = super.beforeHandshake(context);
-                NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
+                NativeCrypto.SSL_clear_mode(s, SSL_MODE_HANDSHAKE_CUTTHROUGH);
                 return s;
             }
             @Override
@@ -797,13 +778,15 @@
                                        SSLHandshakeCallbacks callback)
                     throws Exception {
                 NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER);
+                NativeCrypto.SSL_set_options(
+                        s, NativeCrypto.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
                 NativeCrypto.SSL_renegotiate(s);
                 NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1);
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         try {
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -820,8 +803,8 @@
         try {
             Hooks cHooks = new Hooks();
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks, null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
@@ -835,8 +818,8 @@
         try {
             Hooks cHooks = new Hooks();
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks, null);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
@@ -894,8 +877,10 @@
                         serverSession[0] = session;
                     }
                 };
-                Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-                Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+                Future<TestSSLHandshakeCallbacks> client
+                        = handshake(listener, 0, true, cHooks, null);
+                Future<TestSSLHandshakeCallbacks> server
+                        = handshake(listener, 0, false, sHooks, null);
                 client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
                 server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             }
@@ -935,8 +920,10 @@
                         super.afterHandshake(NULL, s, NULL, sock, fd, callback);
                     }
                 };
-                Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-                Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+                Future<TestSSLHandshakeCallbacks> client
+                        = handshake(listener, 0, true, cHooks, null);
+                Future<TestSSLHandshakeCallbacks> server
+                        = handshake(listener, 0, false, sHooks, null);
                 client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
                 server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             }
@@ -976,8 +963,8 @@
                 }
             };
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
@@ -995,8 +982,8 @@
                     return s;
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
@@ -1063,8 +1050,58 @@
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    public void test_SSL_NpnNegotiateSuccess() throws Exception {
+        final byte[] clientNpnProtocols = new byte[] {
+                8, 'h', 't', 't', 'p', '/', '1', '.', '1',
+                3, 'f', 'o', 'o',
+                6, 's', 'p', 'd', 'y', '/', '2',
+        };
+        final byte[] serverNpnProtocols = new byte[] {
+                6, 's', 'p', 'd', 'y', '/', '2',
+                3, 'f', 'o', 'o',
+                3, 'b', 'a', 'r',
+        };
+
+        Hooks cHooks = new Hooks() {
+            @Override public int beforeHandshake(int context) throws SSLException {
+                NativeCrypto.SSL_CTX_enable_npn(context);
+                return super.beforeHandshake(context);
+            }
+            @Override public void afterHandshake(int session, int ssl, int context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.SSL_get_npn_negotiated_protocol(ssl);
+                assertEquals("spdy/2", new String(negotiated));
+                assertTrue("NPN should enable cutthrough on the client",
+                        0 != (NativeCrypto.SSL_get_mode(ssl) & SSL_MODE_HANDSHAKE_CUTTHROUGH));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override public int beforeHandshake(int context) throws SSLException {
+                NativeCrypto.SSL_CTX_enable_npn(context);
+                return super.beforeHandshake(context);
+            }
+            @Override public void afterHandshake(int session, int ssl, int c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.SSL_get_npn_negotiated_protocol(ssl);
+                assertEquals("spdy/2", new String(negotiated));
+                assertEquals("NPN should not enable cutthrough on the server",
+                        0, NativeCrypto.SSL_get_mode(ssl) & SSL_MODE_HANDSHAKE_CUTTHROUGH);
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ServerSocket listener = new ServerSocket(0);
+        Future<TestSSLHandshakeCallbacks> client
+                = handshake(listener, 0, true, cHooks, clientNpnProtocols);
+        Future<TestSSLHandshakeCallbacks> server
+                = handshake(listener, 0, false, sHooks, serverNpnProtocols);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1117,8 +1154,8 @@
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1153,8 +1190,8 @@
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1180,8 +1217,8 @@
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1284,8 +1321,8 @@
                     super.afterHandshake(session, s, c, sock, fd, callback);
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         }
@@ -1312,8 +1349,8 @@
                     super.afterHandshake(session, s, c, sock, fd, callback);
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
@@ -1427,8 +1464,8 @@
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1501,8 +1538,8 @@
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1530,8 +1567,8 @@
                 }
             };
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         }
@@ -1558,8 +1595,8 @@
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1585,8 +1622,8 @@
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1622,8 +1659,8 @@
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1655,8 +1692,8 @@
                 return s;
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1696,8 +1733,8 @@
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
@@ -1723,4 +1760,15 @@
         assertEquals(-1372642656, NativeCrypto.X509_NAME_hash(name)); // SHA1
         assertEquals(-1626170662, NativeCrypto.X509_NAME_hash_old(name)); // MD5
     }
+
+    public void test_ENGINE_by_id_Failure() throws Exception {
+        NativeCrypto.ENGINE_load_dynamic();
+
+        try {
+            int engine = NativeCrypto.ENGINE_by_id("non-existent");
+            fail("Shouldn't load non-existent engine");
+        } catch (RuntimeException e) {
+            // Success
+        }
+    }
 }
diff --git a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureTest.java b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureTest.java
new file mode 100644
index 0000000..76e423c
--- /dev/null
+++ b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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.xnet.provider.jsse;
+
+import java.security.NoSuchAlgorithmException;
+import junit.framework.TestCase;
+import org.apache.harmony.xnet.provider.jsse.OpenSSLSignature;
+
+public class OpenSSLSignatureTest extends TestCase {
+
+    public void test_getInstance() throws Exception {
+        try {
+            OpenSSLSignature.getInstance("SHA1WITHDSA");
+            OpenSSLSignature.getInstance("MD5WITHRSAENCRYPTION");
+            OpenSSLSignature.getInstance("SHA1WITHRSAENCRYPTION");
+            OpenSSLSignature.getInstance("SHA256WITHRSAENCRYPTION");
+            OpenSSLSignature.getInstance("SHA384WITHRSAENCRYPTION");
+            OpenSSLSignature.getInstance("SHA512WITHRSAENCRYPTION");
+        } catch (NoSuchAlgorithmException e) {
+            fail("getInstance is not case insensitive");
+        }
+    }
+}
diff --git a/luni/src/test/java/tests/api/java/util/BitSetTest.java b/luni/src/test/java/tests/api/java/util/BitSetTest.java
index 172c6d1..e1abc48 100644
--- a/luni/src/test/java/tests/api/java/util/BitSetTest.java
+++ b/luni/src/test/java/tests/api/java/util/BitSetTest.java
@@ -1178,6 +1178,33 @@
         // if the index is larger than bs.size(), nextClearBit should return index
         assertEquals(513, bs.nextClearBit(513));
         assertEquals(800, bs.nextClearBit(800));
+
+        bs.clear();
+        assertEquals(0, bs.nextClearBit(0));
+        assertEquals(3, bs.nextClearBit(3));
+        assertEquals(64, bs.nextClearBit(64));
+        assertEquals(128, bs.nextClearBit(128));
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=31036
+    public void test_31036_clear() {
+        BitSet bs = new BitSet(500);
+        for (int i = 0; i < 500; ++i) {
+            int nextClear = bs.nextClearBit(0);
+            assertEquals(i, nextClear);
+            bs.set(i);
+        }
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=31036
+    public void test_31036_set() {
+        BitSet bs = new BitSet(500);
+        bs.set(0, 511);
+        for (int i = 0; i < 500; ++i) {
+            int nextSet = bs.nextSetBit(0);
+            assertEquals(i, nextSet);
+            bs.clear(i);
+        }
     }
 
     public void test_isEmpty() {
diff --git a/luni/src/test/java/tests/api/java/util/CalendarTest.java b/luni/src/test/java/tests/api/java/util/CalendarTest.java
index ce31216..54524ad 100644
--- a/luni/src/test/java/tests/api/java/util/CalendarTest.java
+++ b/luni/src/test/java/tests/api/java/util/CalendarTest.java
@@ -654,10 +654,10 @@
     public void test_getInstance() {
         // test getInstance(Locale)
         Calendar us_calendar = Calendar.getInstance(Locale.US);
-        Calendar ch_calendar = Calendar.getInstance(Locale.CHINESE);
+        Calendar de_calendar = Calendar.getInstance(Locale.GERMAN);
         assertEquals(Calendar.SUNDAY, us_calendar
                 .getFirstDayOfWeek());
-        assertEquals(Calendar.MONDAY, ch_calendar
+        assertEquals(Calendar.MONDAY, de_calendar
                 .getFirstDayOfWeek());
 
         // test getInstance(Locale, TimeZone)
diff --git a/luni/src/test/java/tests/api/java/util/CurrencyTest.java b/luni/src/test/java/tests/api/java/util/CurrencyTest.java
index 9571b73..c43d339 100644
--- a/luni/src/test/java/tests/api/java/util/CurrencyTest.java
+++ b/luni/src/test/java/tests/api/java/util/CurrencyTest.java
@@ -136,16 +136,15 @@
         // BEGIN android-changed
         // KRW currency symbol is \u20a9 since CLDR1.7 release.
         assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol());
+        // IEP currency symbol is IEP since CLDR2.0 release.
+        assertEquals("currI.getSymbol()", "IEP", currI.getSymbol());
         // END android-changed
-        assertEquals("currI.getSymbol()", "IR\u00a3", currI.getSymbol());
         assertEquals("currUS.getSymbol()", "$", currUS.getSymbol());
 
         Locale.setDefault(new Locale("en", "IE"));
         // BEGIN android-changed
         assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol());
-        // END android-changed
-        assertEquals("currI.getSymbol()", "IR\u00a3", currI.getSymbol());
-        // BEGIN android-changed
+        assertEquals("currI.getSymbol()", "IEP", currI.getSymbol());
         assertEquals("currUS.getSymbol()", "$", currUS.getSymbol());
         // END android-changed
 
@@ -154,8 +153,8 @@
         Locale.setDefault(new Locale("kr", "KR"));
         // BEGIN android-changed
         assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol());
+        assertEquals("currI.getSymbol()", "IEP", currI.getSymbol());
         // END android-changed
-        assertEquals("currI.getSymbol()", "IR\u00a3", currI.getSymbol());
         assertEquals("currUS.getSymbol()", "$", currUS.getSymbol());
     }
 
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/CertificatesToPlayWith.java b/luni/src/test/java/tests/api/javax/net/ssl/CertificatesToPlayWith.java
index 60af3c3..5bb6f06 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/CertificatesToPlayWith.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/CertificatesToPlayWith.java
@@ -450,29 +450,29 @@
           "2b8a9c7d91c7d175ae";
 
     /**
-     * subjectAlt=IP Address:127.0.0.1, email:oleg@ural.ru, DNS:localhost.localdomain
+     * cat cert.cnf
+     * [req]
+     * distinguished_name=distinguished_name
+     * req_extensions=req_extensions
+     * x509_extensions=x509_extensions
+     * [distinguished_name]
+     * [req_extensions]
+     * [x509_extensions]
+     * subjectAltName=DNS:localhost.localdomain,DNS:localhost,IP:127.0.0.1
+     *
+     * $ openssl req -x509 -nodes -days 36500 -subj '/CN=localhost' -config ./cert.cnf \
+     *     -newkey rsa:512 -out cert.pem
      */
     public final static byte[] X509_MULTIPLE_SUBJECT_ALT = (
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIIDcTCCAtqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJDSDEL\n" +
-        "MAkGA1UECBMCWkgxDzANBgNVBAcTBlp1cmljaDETMBEGA1UEAxMKTXkgVGVzdCBD\n" +
-        "QTAeFw0wODEwMzExMTU3NDVaFw0wOTEwMzExMTU3NDVaMGkxCzAJBgNVBAYTAkNI\n" +
-        "MRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdV\n" +
-        "bmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggG4\n" +
-        "MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/\n" +
-        "gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQ\n" +
-        "IsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZ\n" +
-        "ndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5\n" +
-        "eZSvu/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbh\n" +
-        "PBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8G\n" +
-        "kotmXoB7VSVkAUw7/s9JKgOBhQACgYEA6ogAb/YLM1Rz9AoXKW4LA70VtFf7Mqqp\n" +
-        "divdu9f72WQc1vMKo1YMf3dQadkMfBYRvAAa1IXDnoiFCHhXnVRkWkoUBJyNebLB\n" +
-        "N92CZc0RVFZiMFgQMEh8UldnvAIi4cBk0/YuN3BGl4MzmquVIGrFovdWGqeaveOu\n" +
-        "Xcu4lKGJNiqjODA2MDQGA1UdEQQtMCuHBH8AAAGBDG9sZWdAdXJhbC5ydYIVbG9j\n" +
-        "YWxob3N0LmxvY2FsZG9tYWluMA0GCSqGSIb3DQEBBQUAA4GBAIgEwIoCSRkU3O7K\n" +
-        "USYaOYyfJB9hsvs6YpClvYXiQ/5kPGARP60pM62v4wC7wI9shEizokIAxY2+O3cC\n" +
-        "vwuJhNYaa2FJMELIwRN3XES8X8R6JHWbPaRjaAAPhczuEd8SZYy8yiVLmJTgw0gH\n" +
-        "BSW775NHlkjsscFVgXkNf0PobqJ9\n" +
+        "MIIBWDCCAQKgAwIBAgIJANS1EtICX2AZMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n" +
+        "BAMTCWxvY2FsaG9zdDAgFw0xMjAxMDIxOTA4NThaGA8yMTExMTIwOTE5MDg1OFow\n" +
+        "FDESMBAGA1UEAxMJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPpt\n" +
+        "atK8r4/hf4hSIs0os/BSlQLbRBaK9AfBReM4QdAklcQqe6CHsStKfI8pp0zs7Ptg\n" +
+        "PmMdpbttL0O7mUboBC8CAwEAAaM1MDMwMQYDVR0RBCowKIIVbG9jYWxob3N0Lmxv\n" +
+        "Y2FsZG9tYWlugglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEFBQADQQD0ntfL\n" +
+        "DCzOCv9Ma6Lv5o5jcYWVxvBSTsnt22hsJpWD1K7iY9lbkLwl0ivn73pG2evsAn9G\n" +
+        "X8YKH52fnHsCrhSD\n" +
         "-----END CERTIFICATE-----").getBytes();
 
 }
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/HostnameVerifierTest.java b/luni/src/test/java/tests/api/javax/net/ssl/HostnameVerifierTest.java
index 253fed2..839f3b5 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/HostnameVerifierTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/HostnameVerifierTest.java
@@ -19,20 +19,14 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
-import java.net.URL;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSession;
 import javax.security.auth.x500.X500Principal;
 import junit.framework.TestCase;
 import org.apache.harmony.xnet.tests.support.mySSLSession;
 
-/**
- * Tests for <code>HostnameVerifier</code> class constructors and methods.
- *
- */
 public class HostnameVerifierTest extends TestCase implements
         CertificatesToPlayWith {
 
@@ -69,7 +63,7 @@
         in = new ByteArrayInputStream(X509_FOO_BAR);
         x509 = (X509Certificate) cf.generateCertificate(in);
         session = new mySSLSession(new X509Certificate[] {x509});
-        assertTrue(verifier.verify("foo.com", session));
+        assertFalse(verifier.verify("foo.com", session));
         assertFalse(verifier.verify("a.foo.com", session));
         assertTrue(verifier.verify("bar.com", session));
         assertFalse(verifier.verify("a.bar.com", session));
@@ -113,25 +107,25 @@
         in = new ByteArrayInputStream(X509_WILD_FOO);
         x509 = (X509Certificate) cf.generateCertificate(in);
         session = new mySSLSession(new X509Certificate[] {x509});
-        assertFalse(verifier.verify("foo.com", session));
+        assertTrue(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("www.foo.com", session));
         assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session));
-        assertTrue(verifier.verify("a.b.foo.com", session));
+        assertFalse(verifier.verify("a.b.foo.com", session));
 
         in = new ByteArrayInputStream(X509_WILD_CO_JP);
         x509 = (X509Certificate) cf.generateCertificate(in);
         session = new mySSLSession(new X509Certificate[] {x509});
-        assertFalse(verifier.verify("foo.co.jp", session));
-        assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session));
+        assertTrue(verifier.verify("foo.co.jp", session));
+        assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session));
 
         in = new ByteArrayInputStream(X509_WILD_FOO_BAR_HANAKO);
         x509 = (X509Certificate) cf.generateCertificate(in);
         session = new mySSLSession(new X509Certificate[] {x509});
         // try the foo.com variations
-        assertFalse(verifier.verify("foo.com", session));
+        assertTrue(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("www.foo.com", session));
         assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session));
-        assertTrue(verifier.verify("a.b.foo.com", session));
+        assertFalse(verifier.verify("a.b.foo.com", session));
         // these checks test alternative subjects. The test data contains an
         // alternative subject starting with a japanese kanji character. This is
         // not supported by Android because the underlying implementation from
@@ -150,9 +144,7 @@
         mySSLSession session = new mySSLSession(new X509Certificate[] {x509});
 
         HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
-        String expected = "CN=localhost,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=CH";
-        assertEquals(new X500Principal(expected),
-                     x509.getSubjectX500Principal());
+        assertEquals(new X500Principal("CN=localhost"), x509.getSubjectX500Principal());
 
         assertTrue(verifier.verify("localhost", session));
         assertTrue(verifier.verify("localhost.localdomain", session));
@@ -192,7 +184,13 @@
         assertFalse(verifier.verify("127.0.0.1", session));
     }
 
-    public void testWildcardsMustHaveTwoDots() throws Exception {
+    /**
+     * Earlier implementations of Android's hostname verifier required that
+     * wildcard names wouldn't match "*.com" or similar. This was a nonstandard
+     * check that we've since dropped. It is the CA's responsibility to not hand
+     * out certificates that match so broadly.
+     */
+    public void testWildcardsDoesNotNeedTwoDots() throws Exception {
         // openssl req -x509 -nodes -days 36500 -subj '/CN=*.com' -newkey rsa:512 -out cert.pem
         String cert = "-----BEGIN CERTIFICATE-----\n"
                 + "MIIBjDCCATagAwIBAgIJAOVulXCSu6HuMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV\n"
@@ -211,7 +209,7 @@
         mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
         HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
 
-        assertFalse(verifier.verify("google.com", session));
+        assertTrue(verifier.verify("google.com", session));
     }
 
     public void testSubjectAltName() throws Exception {
@@ -244,7 +242,7 @@
         mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
         HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
 
-        assertTrue(verifier.verify("foo.com", session));
+        assertFalse(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("bar.com", session));
         assertTrue(verifier.verify("baz.com", session));
         assertFalse(verifier.verify("a.foo.com", session));
@@ -281,10 +279,10 @@
         mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
         HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
 
-        assertTrue(verifier.verify("foo.com", session));
+        assertFalse(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("bar.com", session));
         assertTrue(verifier.verify("a.baz.com", session));
-        assertFalse(verifier.verify("baz.com", session));
+        assertTrue(verifier.verify("baz.com", session));
         assertFalse(verifier.verify("a.foo.com", session));
         assertFalse(verifier.verify("a.bar.com", session));
         assertFalse(verifier.verify("quux.com", session));
diff --git a/luni/src/test/java/tests/api/javax/security/auth/DestroyFailedExceptionTest.java b/luni/src/test/java/tests/api/javax/security/auth/DestroyFailedExceptionTest.java
index 3fb3d97..4472241 100644
--- a/luni/src/test/java/tests/api/javax/security/auth/DestroyFailedExceptionTest.java
+++ b/luni/src/test/java/tests/api/javax/security/auth/DestroyFailedExceptionTest.java
@@ -27,18 +27,6 @@
  */
 public class DestroyFailedExceptionTest extends TestCase {
 
-    public static void main(String[] args) {
-    }
-
-    /**
-     * Constructor for DestroyFailedExceptionTest.
-     *
-     * @param arg0
-     */
-    public DestroyFailedExceptionTest(String arg0) {
-        super(arg0);
-    }
-
     private static String[] msgs = {
             "",
             "Check new message",
diff --git a/luni/src/test/java/tests/api/javax/security/auth/LoginExceptionTest.java b/luni/src/test/java/tests/api/javax/security/auth/LoginExceptionTest.java
index ef53a98..307d401 100644
--- a/luni/src/test/java/tests/api/javax/security/auth/LoginExceptionTest.java
+++ b/luni/src/test/java/tests/api/javax/security/auth/LoginExceptionTest.java
@@ -27,18 +27,6 @@
  */
 public class LoginExceptionTest extends TestCase {
 
-    public static void main(String[] args) {
-    }
-
-    /**
-     * Constructor for LoginExceptionTest.
-     *
-     * @param arg0
-     */
-    public LoginExceptionTest(String arg0) {
-        super(arg0);
-    }
-
     private static String[] msgs = {
             "",
             "Check new message",
diff --git a/luni/src/test/java/tests/api/javax/security/auth/UnsupportedCallbackExceptionTest.java b/luni/src/test/java/tests/api/javax/security/auth/UnsupportedCallbackExceptionTest.java
index ac41d69..abef563 100644
--- a/luni/src/test/java/tests/api/javax/security/auth/UnsupportedCallbackExceptionTest.java
+++ b/luni/src/test/java/tests/api/javax/security/auth/UnsupportedCallbackExceptionTest.java
@@ -28,18 +28,6 @@
  */
 public class UnsupportedCallbackExceptionTest extends TestCase {
 
-    public static void main(String[] args) {
-    }
-
-    /**
-     * Constructor for UnsupportedCallbackExceptionTest.
-     *
-     * @param arg0
-     */
-    public UnsupportedCallbackExceptionTest(String arg0) {
-        super(arg0);
-    }
-
     private static String[] msgs = {
             "",
             "Check new message",
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedCollection.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedCollection.golden.ser
deleted file mode 100644
index eec840e..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedCollection.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedList.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedList.golden.ser
deleted file mode 100644
index e9a4122..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedList.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedListRandomAccess.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedListRandomAccess.golden.ser
deleted file mode 100644
index dfa2d43..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedListRandomAccess.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedMap.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedMap.golden.ser
deleted file mode 100644
index 13415f6..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedMap.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSet.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSet.golden.ser
deleted file mode 100644
index ed1f305..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSet.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedMap.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedMap.golden.ser
deleted file mode 100644
index eca8ffa..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedMap.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedSet.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedSet.golden.ser
deleted file mode 100644
index 4520ed4..0000000
--- a/luni/src/test/resources/serialization/org/apache/harmony/luni/tests/java/util/Collections_CheckedSortedSet.golden.ser
+++ /dev/null
Binary files differ
diff --git a/luni/src/test/resources/serialization/tests/api/java/util/PriorityQueue.golden.ser b/luni/src/test/resources/serialization/tests/api/java/util/PriorityQueue.golden.ser
deleted file mode 100644
index d716dda..0000000
--- a/luni/src/test/resources/serialization/tests/api/java/util/PriorityQueue.golden.ser
+++ /dev/null
Binary files differ
diff --git a/support/src/test/java/libcore/dalvik/system/CloseGuardTester.java b/support/src/test/java/libcore/dalvik/system/CloseGuardTester.java
index 4ed59a2..e82d33d 100644
--- a/support/src/test/java/libcore/dalvik/system/CloseGuardTester.java
+++ b/support/src/test/java/libcore/dalvik/system/CloseGuardTester.java
@@ -26,6 +26,7 @@
 import java.util.logging.LogRecord;
 import java.util.logging.Logger;
 import junit.framework.Assert;
+import libcore.java.lang.ref.FinalizationTester;
 
 public final class CloseGuardTester implements Closeable {
 
@@ -45,14 +46,12 @@
          * Collect immediately before we start monitoring the CloseGuard logs.
          * This lowers the chance that we'll report an unrelated leak.
          */
-        System.gc();
-        System.runFinalization();
+        FinalizationTester.induceFinalization();
         logger.addHandler(logWatcher);
     }
 
     public void assertEverythingWasClosed() {
-        System.gc();
-        System.runFinalization();
+        FinalizationTester.induceFinalization();
 
         if (!logRecords.isEmpty()) {
             // print the log records with the output of this test
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index 12f85397..e8b29e4 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -172,6 +172,8 @@
         provide("Policy", "JavaPolicy");
         provide("SSLContext", "SSLv3");
         provide("SSLContext", "TLSv1");
+        provide("SSLContext", "TLSv1.1");
+        provide("SSLContext", "TLSv1.2");
         provide("SecretKeyFactory", "DES");
         provide("SecretKeyFactory", "DESede");
         provide("SecretKeyFactory", "PBEWithMD5AndDES");
@@ -448,7 +450,9 @@
         // "SSLv2",
         "SSLv3",
         "TLS",
-        "TLSv1"));
+        "TLSv1",
+        "TLSv1.1",
+        "TLSv1.2"));
     public static final String SSL_CONTEXT_PROTOCOL_DEFAULT = "TLS";
 
     public static final Set<String> KEY_TYPES = new HashSet<String>(Arrays.asList(
@@ -464,7 +468,9 @@
     public static final Set<String> SSL_SOCKET_PROTOCOLS = new HashSet<String>(Arrays.asList(
         // "SSLv2",
         "SSLv3",
-        "TLSv1"));
+        "TLSv1",
+        "TLSv1.1",
+        "TLSv1.2"));
     static {
         if (IS_RI) {
             /* Even though we use OpenSSL's SSLv23_method which
@@ -474,9 +480,15 @@
              * do to disable general use of SSLv2.
              */
             SSL_SOCKET_PROTOCOLS.add("SSLv2Hello");
+        }
+    }
 
-            SSL_SOCKET_PROTOCOLS.add("TLSv1.1");
-            SSL_SOCKET_PROTOCOLS.add("TLSv1.2");
+    public static final Set<String> SSL_SOCKET_PROTOCOLS_SSLENGINE = new HashSet<String>(SSL_SOCKET_PROTOCOLS);
+    static {
+        // No TLSv1.1 or TLSv1.2 support on SSLEngine based provider
+        if (!IS_RI) {
+            SSL_SOCKET_PROTOCOLS_SSLENGINE.remove("TLSv1.1");
+            SSL_SOCKET_PROTOCOLS_SSLENGINE.remove("TLSv1.2");
         }
     }
 
@@ -798,7 +810,7 @@
         assertTrue(protocols.length != 0);
 
         // Make sure all protocols names are expected
-        Set remainingProtocols = new HashSet<String>(StandardNames.SSL_SOCKET_PROTOCOLS);
+        Set remainingProtocols = new HashSet<String>(expected);
         Set unknownProtocols = new HashSet<String>();
         for (String protocol : protocols) {
             if (!remainingProtocols.remove(protocol)) {
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index 30e40fb..e24ee78 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -387,7 +387,8 @@
                 // 1.) we make the keys
                 int keySize;
                 if (keyAlgorithm.equals("RSA")) {
-                    keySize = StandardNames.IS_RI ? 1024 : 512; // 512 breaks SSL_RSA_EXPORT_* on RI
+                    // 512 breaks SSL_RSA_EXPORT_* on RI and TLS_ECDHE_RSA_WITH_RC4_128_SHA for us
+                    keySize =  1024;
                 } else if (keyAlgorithm.equals("DSA")) {
                     keySize = 512;
                 } else if (keyAlgorithm.equals("EC")) {
diff --git a/support/src/test/java/tests/security/KeyFactoryTest.java b/support/src/test/java/tests/security/KeyFactoryTest.java
index e2f687d..1d55c52 100644
--- a/support/src/test/java/tests/security/KeyFactoryTest.java
+++ b/support/src/test/java/tests/security/KeyFactoryTest.java
@@ -46,50 +46,17 @@
         factory = getFactory();
     }
 
-    private KeyFactory getFactory() {
-        try {
-            return KeyFactory.getInstance(algorithmName);
-        } catch (NoSuchAlgorithmException e) {
-            fail(e.getMessage());
-        }
-        return null;
+    private KeyFactory getFactory() throws Exception {
+        return KeyFactory.getInstance(algorithmName);
     }
 
-    public void testKeyFactory() {
-        PrivateKeySpec privateKeySpec = null;
-        try {
-            privateKeySpec = factory.getKeySpec(DefaultKeys.getPrivateKey(algorithmName),
-                    privateKeySpecClass);
-        } catch (InvalidKeySpecException e) {
-            fail(e.getMessage());
-        } catch (NoSuchAlgorithmException e) {
-            fail(e.getMessage());
-        }
-
-        PrivateKey privateKey = null;
-        try {
-            privateKey = factory.generatePrivate(privateKeySpec);
-        } catch (InvalidKeySpecException e) {
-            fail(e.getMessage());
-        }
-
-        PublicKeySpec publicKeySpec = null;
-        try {
-            publicKeySpec = factory.getKeySpec(DefaultKeys.getPublicKey(algorithmName),
-                    publicKeySpecClass);
-        } catch (InvalidKeySpecException e) {
-            fail(e.getMessage());
-        } catch (NoSuchAlgorithmException e) {
-            fail(e.getMessage());
-        }
-
-        PublicKey publicKey = null;
-        try {
-            publicKey = factory.generatePublic(publicKeySpec);
-        } catch (InvalidKeySpecException e) {
-            fail(e.getMessage());
-        }
-
+    public void testKeyFactory() throws Exception {
+        PrivateKeySpec privateKeySpec = factory.getKeySpec(DefaultKeys.getPrivateKey(algorithmName),
+                                                           privateKeySpecClass);
+        PrivateKey privateKey =  factory.generatePrivate(privateKeySpec);
+        PublicKeySpec publicKeySpec = factory.getKeySpec(DefaultKeys.getPublicKey(algorithmName),
+                                                         publicKeySpecClass);
+        PublicKey publicKey = factory.generatePublic(publicKeySpec);
         check(new KeyPair(publicKey, privateKey));
     }
 
diff --git a/support/src/test/java/tests/util/SerializationTester.java b/support/src/test/java/tests/util/SerializationTester.java
deleted file mode 100644
index bd9bc93..0000000
--- a/support/src/test/java/tests/util/SerializationTester.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package tests.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.net.URL;
-
-/**
- * This class simplifies the serialization test.
- *
- */
-public class SerializationTester {
-
-    /*
-     * --------------------------------------------------------------------
-     * Class variables
-     * --------------------------------------------------------------------
-     */
-
-    // the last deserialized object
-    private static Object lastOutput = null;
-
-    /*
-     * -------------------------------------------------------------------
-     * Constructors
-     * -------------------------------------------------------------------
-     */
-
-    private SerializationTester() {
-
-    }
-
-    /*
-     * -------------------------------------------------------------------
-     * Methods
-     * -------------------------------------------------------------------
-     */
-
-    /**
-     * Serialize an object and then deserialize it.
-     *
-     * @param inputObject
-     *            the input object
-     * @return the deserialized object
-     */
-    public static <T> T getDeserializedObject(T inputObject)
-            throws IOException, ClassNotFoundException {
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        ObjectOutputStream oos = new ObjectOutputStream(bos);
-        oos.writeObject(inputObject);
-        oos.close();
-
-        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
-        ObjectInputStream ois = new ObjectInputStream(bis);
-        Object outputObject = ois.readObject();
-        lastOutput = outputObject;
-        ois.close();
-        return (T) outputObject;
-    }
-
-    /**
-     * Tests the serialization and deserialization of const objects.
-     *
-     * @param inputObject
-     *            A const object
-     * @return true if the deserialized object is the same as the input object,
-     *         otherwise false
-     * @throws Exception
-     *             If any occurs.
-     */
-    public static boolean assertSame(Object inputObject) throws Exception {
-        return inputObject == getDeserializedObject(inputObject);
-    }
-
-    /**
-     * Tests the serialization and deserialization of instance objects.
-     *
-     * @param inputObject
-     *            An object
-     * @return true if the deserialized object is equal to the input object,
-     *         otherwise false
-     * @throws Exception
-     *             If any occurs.
-     */
-    public static boolean assertEquals(Object inputObject) throws Exception {
-        return inputObject.equals(getDeserializedObject(inputObject));
-    }
-
-    /**
-     * Tests the serialization compatibility with reference const objects.
-     *
-     * @param obj
-     *            the object to be checked
-     * @param fileName
-     *            the serialization output file generated by reference
-     * @return true if compatible, otherwise false
-     * @throws Exception
-     *             If any occurs.
-     */
-    public static boolean assertCompatibilitySame(Object obj, String fileName)
-            throws Exception {
-        return obj == readObject(obj, fileName);
-    }
-
-    /**
-     * Tests the serialization compatibility with reference for instance
-     * objects.
-     *
-     * @param obj
-     *            the object to be checked
-     * @param fileName
-     *            the serialization output file generated by reference
-     * @return true if compatible, otherwise false
-     * @throws Exception
-     *             If any occurs.
-     */
-    public static boolean assertCompatibilityEquals(Object obj, String fileName)
-            throws Exception {
-        return obj.equals(readObject(obj, fileName));
-    }
-
-    /**
-     * Deserialize an object from a file.
-     *
-     * @param obj
-     *            the object to be serialized if no serialization file is found
-     * @param fileName
-     *            the serialization file
-     * @return the deserialized object
-     * @throws Exception
-     *             If any occurs.
-     */
-    public static Object readObject(Object obj, String fileName)
-            throws Exception {
-        InputStream input = null;
-        ObjectInputStream oinput = null;
-        URL url = SerializationTester.class.getResource(
-                fileName);
-        if (null == url) {
-            // serialization file does not exist, create one in the current dir
-            writeObject(obj, new File(fileName).getName());
-            throw new Error(
-                    "Serialization file does not exist, created in the current dir.");
-        }
-        input = url.openStream();
-        try {
-            oinput = new ObjectInputStream(input);
-            Object newObj = oinput.readObject();
-            return newObj;
-        } finally {
-            try {
-                if (null != oinput) {
-                    oinput.close();
-                }
-            } catch (Exception e) {
-                // ignore
-            }
-            try {
-                if (null != input) {
-                    input.close();
-                }
-            } catch (Exception e) {
-                // ignore
-            }
-        }
-    }
-
-    /*
-     * Creates a serialization output.
-     *
-     * @param obj the object to be serialized @param fileName the output file
-     * @throws Exception If any occurs.
-     */
-    public static void writeObject(Object obj, String fileName)
-            throws Exception {
-        // String path = SerializationTester.class.getResource(".").getPath();
-        // if (path.endsWith(".")) {
-        // path = path.substring(0, path.length() - 1);
-        // }
-        // if (!path.endsWith("/")) {
-        // path += "/";
-        // }
-        // path += fileName;
-        // System.out.println(path);
-        OutputStream output = null;
-        ObjectOutputStream ooutput = null;
-        try {
-            output = new FileOutputStream(fileName);
-            ooutput = new ObjectOutputStream(output);
-            ooutput.writeObject(obj);
-        } finally {
-            try {
-                if (null != ooutput) {
-                    ooutput.close();
-                }
-            } catch (Exception e) {
-                // ignore
-            }
-            try {
-                if (null != output) {
-                    output.close();
-                }
-            } catch (Exception e) {
-                // ignore
-            }
-        }
-    }
-
-    /**
-     * Gets the last deserialized object.
-     *
-     * @return the last deserialized object
-     */
-    public static Object getLastOutput() {
-        return lastOutput;
-    }
-
-    /*
-     * For test purpose.
-     */
-    public static void main(String[] args) {
-    }
-}
diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
index 36e6025..e042eb8 100644
--- a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
+++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
@@ -112,6 +112,7 @@
  * Start tag foo
  * Text Hello World!
  * End tag foo
+ * End document
  * </pre>
  *
  * <p>For more details on API usage, please refer to the
@@ -160,9 +161,9 @@
      * Logical end of the xml document. Returned from getEventType, next()
      * and nextToken()
      * when the end of the input document has been reached.
-     * <p><strong>NOTE:</strong> calling again
+     * <p><strong>NOTE:</strong> subsequent calls to
      * <a href="#next()">next()</a> or <a href="#nextToken()">nextToken()</a>
-     * will result in exception being thrown.
+     * may result in exception being thrown.
      *
      * @see #next
      * @see #nextToken
@@ -1051,7 +1052,7 @@
      * If current event is START_TAG then if next element is TEXT then element content is returned
      * or if next event is END_TAG then empty string is returned, otherwise exception is thrown.
      * After calling this function successfully parser will be positioned on END_TAG.
-     *
+     * 
      * <p>The motivation for this function is to allow to parse consistently both
      * empty elements and elements that has non empty content, for example for input: <ol>
      * <li>&lt;tag&gt;foo&lt;/tag&gt;
@@ -1089,6 +1090,15 @@
      *       "parser must be on START_TAG or TEXT to read text", this, null);
      *  }
      * </pre>
+     *
+     * <p><strong>Warning:</strong> Prior to API level 14, the pull parser returned by {@code
+     * android.util.Xml} did not always advance to the END_TAG event when this method was called.
+     * Work around by using manually advancing after calls to nextText(): <pre>
+     *  String text = xpp.nextText();
+     *  if (xpp.getEventType() != XmlPullParser.END_TAG) {
+     *      xpp.next();
+     *  }
+     * </pre>
      */
     String nextText() throws XmlPullParserException, IOException;