am 5ff3bc94: am 4c7d72a6: am fe1f3d77: am 11708986: am b00f46fc: am 23b3ea3a: am e496d90d: am cef32f3b: Merge "SSLEngine: Test that server params are verified" into jb-dev

* commit '5ff3bc94bf2ec5bca48cb0027060df7fe4e48580':
  SSLEngine: Test that server params are verified
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1618f1b..992b0dc 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -45,6 +45,8 @@
 #$(call add-clean-step, find $(OUT) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
 #$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/system/framework/conscrypt-nojarjar.jar)
+$(call add-clean-step, rm -rf $(OUT)/system/framework/conscrypt-nojarjar.jar)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 673c0bf..f4cde58 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -50,18 +50,20 @@
 endef
 
 # The Java files and their associated resources.
-core_src_files := $(call all-main-java-files-under,dalvik dex dom json luni support xml)
-core_src_files += $(call all-main-java-files-under,libdvm)
+common_core_src_files := $(call all-main-java-files-under,dalvik dex dom json luni xml)
 core_resource_dirs := $(call all-core-resource-dirs,main)
 test_resource_dirs := $(call all-core-resource-dirs,test)
 
 ifeq ($(EMMA_INSTRUMENT),true)
 ifneq ($(EMMA_INSTRUMENT_STATIC),true)
-    core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated)
+    common_core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated)
     core_resource_dirs += ../external/emma/core/res ../external/emma/pregenerated/res
 endif
 endif
 
+libdvm_core_src_files += $(common_core_src_files) $(call all-main-java-files-under,libdvm)
+libart_core_src_files += $(common_core_src_files) $(call all-main-java-files-under,libart)
+
 local_javac_flags=-encoding UTF-8
 #local_javac_flags+=-Xlint:all -Xlint:-serial,-deprecation,-unchecked
 local_javac_flags+=-Xmaxwarns 9999999
@@ -73,23 +75,28 @@
 # Definitions to make the core library.
 
 include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(core_src_files)
+LOCAL_SRC_FILES := $(libdvm_core_src_files)
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
-
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
 LOCAL_DX_FLAGS := --core-library
-
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := core
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 LOCAL_REQUIRED_MODULES := tzdata
-
 include $(BUILD_JAVA_LIBRARY)
 
-core-intermediates := ${intermediates}
-
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libart_core_src_files)
+LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := core-libart
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+LOCAL_REQUIRED_MODULES := tzdata
+include $(BUILD_JAVA_LIBRARY)
 
 # Create the conscrypt library
 include $(CLEAR_VARS)
@@ -100,6 +107,7 @@
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/crypto/jarjar-rules.txt
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := conscrypt
+LOCAL_REQUIRED_MODULES := libjavacrypto
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 include $(BUILD_JAVA_LIBRARY)
 
@@ -112,24 +120,53 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := conscrypt-nojarjar
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
-include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
 
 
 ifeq ($(LIBCORE_SKIP_TESTS),)
 # Make the core-tests library.
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-test-java-files-under,crypto dalvik dom harmony-tests json luni support xml)
+LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom harmony-tests json luni xml)
 LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := bouncycastle core conscrypt-nojarjar core-junit okhttp
-LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc mockwebserver nist-pkix-tests okhttp-tests
+LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit okhttp
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support sqlite-jdbc mockwebserver nist-pkix-tests okhttp-tests
 LOCAL_JAVACFLAGS := $(local_javac_flags)
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/crypto/jarjar-rules.txt
 LOCAL_MODULE := core-tests
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 include $(BUILD_STATIC_JAVA_LIBRARY)
 endif
 
+ifeq ($(LIBCORE_SKIP_TESTS),)
+# Make the core-tests-support library.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-test-java-files-under,support)
+LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE := core-tests-support
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+endif
+
+ifeq ($(LIBCORE_SKIP_TESTS),)
+# Make the conscrypt-tests library.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-test-java-files-under,crypto)
+LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support conscrypt-nojarjar
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := conscrypt-tests
+LOCAL_REQUIRED_MODULES := libjavacrypto
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/crypto/jarjar-rules.txt
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+endif
+
 # This one's tricky. One of our tests needs to have a
 # resource with a "#" in its name, but Perforce doesn't
 # allow us to submit such a file. So we create it here
@@ -158,23 +195,30 @@
 ifeq ($(WITH_HOST_DALVIK),true)
 
     # Definitions to make the core library.
-
     include $(CLEAR_VARS)
-
-    LOCAL_SRC_FILES := $(core_src_files)
+    LOCAL_SRC_FILES := $(libdvm_core_src_files)
     LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
-
     LOCAL_NO_STANDARD_LIBRARIES := true
     LOCAL_JAVACFLAGS := $(local_javac_flags)
     LOCAL_DX_FLAGS := --core-library
-
     LOCAL_BUILD_HOST_DEX := true
-
     LOCAL_MODULE_TAGS := optional
     LOCAL_MODULE := core-hostdex
     LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
     LOCAL_REQUIRED_MODULES := tzdata-host
+    include $(BUILD_HOST_JAVA_LIBRARY)
 
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES := $(libart_core_src_files)
+    LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+    LOCAL_NO_STANDARD_LIBRARIES := true
+    LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_DX_FLAGS := --core-library
+    LOCAL_BUILD_HOST_DEX := true
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := core-libart-hostdex
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+    LOCAL_REQUIRED_MODULES := tzdata-host
     include $(BUILD_HOST_JAVA_LIBRARY)
 
     # Make the conscrypt-hostdex library
@@ -187,6 +231,7 @@
     LOCAL_BUILD_HOST_DEX := true
     LOCAL_MODULE_TAGS := optional
     LOCAL_MODULE := conscrypt-hostdex
+    LOCAL_REQUIRED_MODULES := libjavacrypto
     LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
     include $(BUILD_HOST_JAVA_LIBRARY)
 
@@ -205,19 +250,49 @@
     # Make the core-tests library.
     ifeq ($(LIBCORE_SKIP_TESTS),)
     include $(CLEAR_VARS)
-    LOCAL_SRC_FILES := $(call all-test-java-files-under,crypto dalvik dom json luni support xml)
+    LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni xml)
     LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
     LOCAL_NO_STANDARD_LIBRARIES := true
-    LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex conscrypt-hostdex-nojarjar core-junit-hostdex okhttp-hostdex
+    LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex core-junit-hostdex core-tests-support-hostdex okhttp-hostdex
     LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc-host mockwebserver-host nist-pkix-tests-host
     LOCAL_JAVACFLAGS := $(local_javac_flags)
-    LOCAL_JARJAR_RULES := $(LOCAL_PATH)/crypto/jarjar-rules.txt
     LOCAL_MODULE_TAGS := optional
     LOCAL_MODULE := core-tests-hostdex
     LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
     LOCAL_BUILD_HOST_DEX := true
     include $(BUILD_HOST_JAVA_LIBRARY)
     endif
+
+    # Make the core-tests-support library.
+    ifeq ($(LIBCORE_SKIP_TESTS),)
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES := $(call all-test-java-files-under,support)
+    LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+    LOCAL_NO_STANDARD_LIBRARIES := true
+    LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex core-junit-hostdex
+    LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := core-tests-support-hostdex
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+    LOCAL_BUILD_HOST_DEX := true
+    include $(BUILD_HOST_JAVA_LIBRARY)
+    endif
+
+    # Make the conscrypt-tests library.
+    ifeq ($(LIBCORE_SKIP_TESTS),)
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES := $(call all-test-java-files-under,crypto)
+    LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+    LOCAL_NO_STANDARD_LIBRARIES := true
+    LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex core-junit-hostdex core-tests-support-hostdex conscrypt-hostdex-nojarjar
+    LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := conscrypt-tests-hostdex
+    LOCAL_REQUIRED_MODULES := libjavacrypto
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+    LOCAL_BUILD_HOST_DEX := true
+    include $(BUILD_HOST_JAVA_LIBRARY)
+    endif
 endif
 
 #
diff --git a/NativeCode.mk b/NativeCode.mk
index 3a22575..1a47ee4 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -59,7 +59,7 @@
 
 # Include the sub.mk files.
 $(foreach dir, \
-    crypto/src/main/native dalvik/src/main/native luni/src/main/native, \
+    dalvik/src/main/native luni/src/main/native, \
     $(eval $(call include-core-native-dir,$(dir))))
 
 # Extract out the allowed LOCAL_* variables.
@@ -67,7 +67,7 @@
 core_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
 core_static_libraries := $(LOCAL_STATIC_LIBRARIES)
 core_cflags := -Wall -Wextra -Werror
-core_cflags += -DJNI_JARJAR_PREFIX="com/android/"
+core_cppflags += -std=gnu++11
 
 core_test_files := \
   luni/src/test/native/test_openssl_engine.cpp \
@@ -81,7 +81,7 @@
 LOCAL_CPPFLAGS += $(core_cppflags)
 LOCAL_SRC_FILES += $(core_src_files)
 LOCAL_C_INCLUDES += $(core_c_includes)
-LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz libnativehelper
+LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libcrypto libexpat libicuuc libicui18n libnativehelper libz
 LOCAL_STATIC_LIBRARIES += $(core_static_libraries)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libjavacore
@@ -89,6 +89,22 @@
 include external/stlport/libstlport.mk
 include $(BUILD_SHARED_LIBRARY)
 
+# Platform conscrypt crypto library
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CFLAGS += -DJNI_JARJAR_PREFIX="com/android/"
+LOCAL_CPPFLAGS += $(core_cppflags)
+LOCAL_SRC_FILES := \
+        crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
+LOCAL_C_INCLUDES += $(core_c_includes) \
+        libcore/luni/src/main/native
+LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libcrypto libssl libnativehelper libz libjavacore
+LOCAL_STATIC_LIBRARIES += $(core_static_libraries)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libjavacrypto
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+include external/stlport/libstlport.mk
+include $(BUILD_SHARED_LIBRARY)
 
 # Test JNI library.
 ifeq ($(LIBCORE_SKIP_TESTS),)
@@ -122,22 +138,38 @@
     LOCAL_MODULE_TAGS := optional
     LOCAL_MODULE := libjavacore
     LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
-    LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat-host libicuuc-host libicui18n-host libssl-host libcrypto-host libz-host
+    LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat-host libicuuc-host libicui18n-host libcrypto-host libz-host
+    LOCAL_STATIC_LIBRARIES += $(core_static_libraries)
+    include $(BUILD_HOST_SHARED_LIBRARY)
+
+    # Conscrypt native library for host
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES += \
+            crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
+    LOCAL_C_INCLUDES += $(core_c_includes) \
+            libcore/luni/src/main/native
+    LOCAL_CPPFLAGS += $(core_cppflags)
+    LOCAL_LDLIBS += -lpthread
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libjavacrypto
+    LOCAL_CFLAGS += -DJNI_JARJAR_PREFIX="com/android/"
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+    LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libssl-host libcrypto-host libjavacore
     LOCAL_STATIC_LIBRARIES += $(core_static_libraries)
     include $(BUILD_HOST_SHARED_LIBRARY)
 
     # Conscrypt native library for nojarjar'd version
     include $(CLEAR_VARS)
     LOCAL_SRC_FILES += \
-            crypto/src/main/native/org_conscrypt_NativeCrypto.cpp \
-            luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
-    LOCAL_C_INCLUDES += $(core_c_includes)
+            crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
+    LOCAL_C_INCLUDES += $(core_c_includes) \
+            libcore/luni/src/main/native
     LOCAL_CPPFLAGS += $(core_cppflags)
     LOCAL_LDLIBS += -lpthread
     LOCAL_MODULE_TAGS := optional
     LOCAL_MODULE := libconscrypt_jni
     LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
-    LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libssl-host libcrypto-host
+    LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libssl-host libcrypto-host libjavacore
     LOCAL_STATIC_LIBRARIES += $(core_static_libraries)
     include $(BUILD_HOST_SHARED_LIBRARY)
 
diff --git a/benchmarks/src/benchmarks/regression/CharsetBenchmark.java b/benchmarks/src/benchmarks/regression/CharsetBenchmark.java
index 6ecada7..21b9966 100644
--- a/benchmarks/src/benchmarks/regression/CharsetBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/CharsetBenchmark.java
@@ -68,65 +68,6 @@
         }
     }
 
-    // FIXME: benchmark this pure-java implementation for US-ASCII and ISO-8859-1 too!
-
-    /**
-     * Translates the given characters to US-ASCII or ISO-8859-1 bytes, using the fact that
-     * Unicode code points between U+0000 and U+007f inclusive are identical to US-ASCII, while
-     * U+0000 to U+00ff inclusive are identical to ISO-8859-1.
-     */
-    private static byte[] toDirectMappedBytes(char[] chars, int offset, int length, int maxValidChar) {
-        byte[] result = new byte[length];
-        int o = offset;
-        for (int i = 0; i < length; ++i) {
-            int ch = chars[o++];
-            result[i] = (byte) ((ch <= maxValidChar) ? ch : '?');
-        }
-        return result;
-    }
-
-    private static byte[] toUtf8Bytes(char[] chars, int offset, int length) {
-        UnsafeByteSequence result = new UnsafeByteSequence(length);
-        toUtf8Bytes(chars, offset, length, result);
-        return result.toByteArray();
-    }
-
-    private static void toUtf8Bytes(char[] chars, int offset, int length, UnsafeByteSequence out) {
-        final int end = offset + length;
-        for (int i = offset; i < end; ++i) {
-            int ch = chars[i];
-            if (ch < 0x80) {
-                // One byte.
-                out.write(ch);
-            } else if (ch < 0x800) {
-                // Two bytes.
-                out.write((ch >> 6) | 0xc0);
-                out.write((ch & 0x3f) | 0x80);
-            } else if (ch >= Character.MIN_SURROGATE && ch <= Character.MAX_SURROGATE) {
-                // A supplementary character.
-                char high = (char) ch;
-                char low = (i + 1 != end) ? chars[i + 1] : '\u0000';
-                if (!Character.isSurrogatePair(high, low)) {
-                    out.write('?');
-                    continue;
-                }
-                // Now we know we have a *valid* surrogate pair, we can consume the low surrogate.
-                ++i;
-                ch = Character.toCodePoint(high, low);
-                // Four bytes.
-                out.write((ch >> 18) | 0xf0);
-                out.write(((ch >> 12) & 0x3f) | 0x80);
-                out.write(((ch >> 6) & 0x3f) | 0x80);
-                out.write((ch & 0x3f) | 0x80);
-            } else {
-                // Three bytes.
-                out.write((ch >> 12) | 0xe0);
-                out.write(((ch >> 6) & 0x3f) | 0x80);
-                out.write((ch & 0x3f) | 0x80);
-            }
-        }
-    }
-
     private static String makeString(int length) {
         StringBuilder result = new StringBuilder(length);
         for (int i = 0; i < length; ++i) {
diff --git a/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
new file mode 100644
index 0000000..02d8f97
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 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 benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static libcore.icu.DateIntervalFormat.*;
+
+public class DateIntervalFormatBenchmark extends SimpleBenchmark {
+  public void timeDateIntervalFormat_formatDateRange_DATE(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      formatDateRange(l, utc, 0L, 0L, flags);
+    }
+  }
+
+  public void timeDateIntervalFormat_formatDateRange_TIME(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      formatDateRange(l, utc, 0L, 0L, flags);
+    }
+  }
+
+  public void timeDateIntervalFormat_formatDateRange_DATE_TIME(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      formatDateRange(l, utc, 0L, 0L, flags);
+    }
+  }
+}
diff --git a/benchmarks/src/benchmarks/regression/IcuBenchmark.java b/benchmarks/src/benchmarks/regression/IcuBenchmark.java
new file mode 100644
index 0000000..ee8270a
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/IcuBenchmark.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013 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 benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import libcore.icu.ICU;
+
+public class IcuBenchmark extends SimpleBenchmark {
+  public void time_getBestDateTimePattern(int reps) throws Exception {
+    for (int rep = 0; rep < reps; ++rep) {
+      ICU.getBestDateTimePattern("dEEEMMM", "US");
+    }
+  }
+}
diff --git a/crypto/src/main/java/org/conscrypt/KeyManagerImpl.java b/crypto/src/main/java/org/conscrypt/KeyManagerImpl.java
index ed37fd6..7a6c92c 100644
--- a/crypto/src/main/java/org/conscrypt/KeyManagerImpl.java
+++ b/crypto/src/main/java/org/conscrypt/KeyManagerImpl.java
@@ -161,11 +161,11 @@
                 if (keyAlgorithm == null) {
                     continue;
                 }
-                String sigAlgorithm;
+                final String sigAlgorithm;
                 // handle cases like EC_EC and EC_RSA
                 int index = keyAlgorithm.indexOf('_');
                 if (index == -1) {
-                    sigAlgorithm = keyAlgorithm;
+                    sigAlgorithm = null;
                 } else {
                     sigAlgorithm = keyAlgorithm.substring(index + 1);
                     keyAlgorithm = keyAlgorithm.substring(0, index);
@@ -181,7 +181,8 @@
                  * "SHA1WithECDSA" or simply "ECDSA".
                  */
                 // sig algorithm does not match
-                if (certSigAlg != null && !certSigAlg.contains(sigAlgorithm)) {
+                if (sigAlgorithm != null && certSigAlg != null
+                        && !certSigAlg.contains(sigAlgorithm)) {
                     continue;
                 }
                 // no issuers to match, just add to return list and continue
diff --git a/crypto/src/main/java/org/conscrypt/NativeCrypto.java b/crypto/src/main/java/org/conscrypt/NativeCrypto.java
index 9094935..86c99d8 100644
--- a/crypto/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/crypto/src/main/java/org/conscrypt/NativeCrypto.java
@@ -46,11 +46,12 @@
     // --- OpenSSL library initialization --------------------------------------
     static {
         /*
-         * If we're compiled as part of Android, we don't need to explicitly
-         * call loadLibrary. Detect this by looking for the jarjar'd package
-         * name.
+         * If we're compiled as part of Android, should use a different JNI
+         * library name. Detect this by looking for the jarjar'd package name.
          */
-        if (!"com.android.org.conscrypt".equals(NativeCrypto.class.getPackage().getName())) {
+        if ("com.android.org.conscrypt".equals(NativeCrypto.class.getPackage().getName())) {
+            System.loadLibrary("javacrypto");
+        } else {
             System.loadLibrary("conscrypt_jni");
         }
 
diff --git a/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java b/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java
index 632f9e2..7acccc7 100644
--- a/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java
+++ b/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java
@@ -16,6 +16,7 @@
 
 package org.conscrypt;
 
+import java.io.IOException;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -118,6 +119,11 @@
     }
 
     /**
+     * Returns the standard name for the particular algorithm.
+     */
+    protected abstract String getBaseCipherName();
+
+    /**
      * Returns the OpenSSL cipher name for the particular {@code keySize} and
      * cipher {@code mode}.
      */
@@ -214,10 +220,22 @@
 
     @Override
     protected AlgorithmParameters engineGetParameters() {
+        if (iv != null && iv.length > 0) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance(getBaseCipherName());
+                params.init(iv);
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                return null;
+            } catch (IOException e) {
+                return null;
+            }
+        }
         return null;
     }
 
-    private void engineInitInternal(int opmode, Key key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
+    private void engineInitInternal(int opmode, Key key, byte[] iv, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
         if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
             encrypting = true;
         } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
@@ -245,9 +263,15 @@
         }
 
         final int ivLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
-        if (iv == null) {
+        if (iv == null && ivLength != 0) {
             iv = new byte[ivLength];
-        } else if (iv.length != ivLength) {
+            if (encrypting) {
+                if (random == null) {
+                    random = new SecureRandom();
+                }
+                random.nextBytes(iv);
+            }
+        } else if (iv != null && iv.length != ivLength) {
             throw new InvalidAlgorithmParameterException("expected IV length of " + ivLength);
         }
 
@@ -273,7 +297,7 @@
     @Override
     protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
         try {
-            engineInitInternal(opmode, key, null);
+            engineInitInternal(opmode, key, null, random);
         } catch (InvalidAlgorithmParameterException e) {
             throw new RuntimeException(e);
         }
@@ -290,7 +314,7 @@
             iv = null;
         }
 
-        engineInitInternal(opmode, key, iv);
+        engineInitInternal(opmode, key, iv, random);
     }
 
     @Override
@@ -631,6 +655,11 @@
         }
 
         @Override
+        protected String getBaseCipherName() {
+            return "AES";
+        }
+
+        @Override
         protected String getCipherName(int keyLength, Mode mode) {
             return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
         }
@@ -721,6 +750,11 @@
         }
 
         @Override
+        protected String getBaseCipherName() {
+            return "DESede";
+        }
+
+        @Override
         protected String getCipherName(int keySize, Mode mode) {
             final String baseCipherName;
             if (keySize == 16) {
@@ -780,6 +814,11 @@
         }
 
         @Override
+        protected String getBaseCipherName() {
+            return "ARCFOUR";
+        }
+
+        @Override
         protected String getCipherName(int keySize, Mode mode) {
             return "rc4";
         }
diff --git a/crypto/src/main/java/org/conscrypt/OpenSSLProvider.java b/crypto/src/main/java/org/conscrypt/OpenSSLProvider.java
index 7314478..4055e50 100644
--- a/crypto/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/crypto/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -36,6 +36,9 @@
     public OpenSSLProvider() {
         super(PROVIDER_NAME, 1.0, "Android's OpenSSL-backed security provider");
 
+        // Make sure the platform is initialized.
+        Platform.setup();
+
         final String prefix = getClass().getPackage().getName() + ".";
 
         /* === SSL Contexts === */
diff --git a/crypto/src/main/java/org/conscrypt/OpenSSLSocketImpl.java b/crypto/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
index 7c5d78f..0caeff3 100644
--- a/crypto/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
+++ b/crypto/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
@@ -506,6 +506,11 @@
             return;
         }
 
+        // Note that OpenSSL says to use SSL_use_certificate before SSL_use_PrivateKey.
+
+        byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates);
+        NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes);
+
         try {
             final OpenSSLKey key = OpenSSLKey.fromPrivateKey(privateKey);
             NativeCrypto.SSL_use_PrivateKey(sslNativePointer, key.getPkeyContext());
@@ -513,9 +518,6 @@
             throw new SSLException(e);
         }
 
-        byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates);
-        NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes);
-
         // checks the last installed private key and certificate,
         // so need to do this once per loop iteration
         NativeCrypto.SSL_check_private_key(sslNativePointer);
diff --git a/crypto/src/main/java/org/conscrypt/PinFailureLogger.java b/crypto/src/main/java/org/conscrypt/PinFailureLogger.java
index 67bbc1f..bdb44a9 100644
--- a/crypto/src/main/java/org/conscrypt/PinFailureLogger.java
+++ b/crypto/src/main/java/org/conscrypt/PinFailureLogger.java
@@ -59,7 +59,7 @@
             }
             sb.append("|");
         }
-        DropBox.addText("cert_pin_failure", sb.toString());
+        DropBox.addText("exp_det_cert_pin_failure", sb.toString());
     }
 
     protected static boolean timeToLog() {
diff --git a/crypto/src/main/java/org/conscrypt/Platform.java b/crypto/src/main/java/org/conscrypt/Platform.java
new file mode 100644
index 0000000..3a577ce
--- /dev/null
+++ b/crypto/src/main/java/org/conscrypt/Platform.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import org.apache.harmony.security.utils.AlgNameMapper;
+import org.apache.harmony.security.utils.AlgNameMapperSource;
+
+class Platform {
+    private static class NoPreloadHolder {
+        public static final Platform MAPPER = new Platform();
+    }
+
+    /**
+     * Runs all the setup for the platform that only needs to run once.
+     */
+    public static void setup() {
+        NoPreloadHolder.MAPPER.ping();
+    }
+
+    /**
+     * Just a placeholder to make sure the class is initialized.
+     */
+    private void ping() {
+    }
+
+    private Platform() {
+        AlgNameMapper.setSource(new OpenSSLMapper());
+    }
+
+    private static class OpenSSLMapper implements AlgNameMapperSource {
+        @Override
+        public String mapNameToOid(String algName) {
+            return NativeCrypto.OBJ_txt2nid_oid(algName);
+        }
+
+        @Override
+        public String mapOidToName(String oid) {
+            return NativeCrypto.OBJ_txt2nid_longName(oid);
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp b/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
index 8a59fdc..6e171a7 100644
--- a/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -47,6 +47,7 @@
 #include <openssl/x509v3.h>
 
 #include "AsynchronousSocketCloseMonitor.h"
+#include "cutils/log.h"
 #include "JNIHelp.h"
 #include "JniConstants.h"
 #include "JniException.h"
@@ -252,9 +253,8 @@
     X509Chain(size_t n) : x509s_(n) {}
 
     ~X509Chain() {
-        // TODO: C++0x auto
-        for (std::vector<X509*>::const_iterator it = x509s_.begin(); it != x509s_.end(); ++it) {
-            X509_free(*it);
+        for (const auto& x509 : x509s_) {
+            X509_free(x509);
         }
     }
 
@@ -292,6 +292,7 @@
 
 struct sk_SSL_CIPHER_Delete {
     void operator()(STACK_OF(SSL_CIPHER)* p) const {
+        // We don't own SSL_CIPHER references, so no need for pop_free
         sk_SSL_CIPHER_free(p);
     }
 };
@@ -299,28 +300,28 @@
 
 struct sk_X509_Delete {
     void operator()(STACK_OF(X509)* p) const {
-        sk_X509_free(p);
+        sk_X509_pop_free(p, X509_free);
     }
 };
 typedef UniquePtr<STACK_OF(X509), sk_X509_Delete> Unique_sk_X509;
 
 struct sk_X509_NAME_Delete {
     void operator()(STACK_OF(X509_NAME)* p) const {
-        sk_X509_NAME_free(p);
+        sk_X509_NAME_pop_free(p, X509_NAME_free);
     }
 };
 typedef UniquePtr<STACK_OF(X509_NAME), sk_X509_NAME_Delete> Unique_sk_X509_NAME;
 
 struct sk_ASN1_OBJECT_Delete {
     void operator()(STACK_OF(ASN1_OBJECT)* p) const {
-        sk_ASN1_OBJECT_free(p);
+        sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free);
     }
 };
 typedef UniquePtr<STACK_OF(ASN1_OBJECT), sk_ASN1_OBJECT_Delete> Unique_sk_ASN1_OBJECT;
 
 struct sk_GENERAL_NAME_Delete {
     void operator()(STACK_OF(GENERAL_NAME)* p) const {
-        sk_GENERAL_NAME_free(p);
+        sk_GENERAL_NAME_pop_free(p, GENERAL_NAME_free);
     }
 };
 typedef UniquePtr<STACK_OF(GENERAL_NAME), sk_GENERAL_NAME_Delete> Unique_sk_GENERAL_NAME;
@@ -417,6 +418,7 @@
         throwBadPaddingException(env, message);
         break;
     case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
+    case EVP_R_WRONG_FINAL_BLOCK_LENGTH:
         throwIllegalBlockSizeException(env, message);
         break;
     case EVP_R_BAD_KEY_LENGTH:
@@ -856,6 +858,14 @@
 }
 
 /**
+ * To avoid the round-trip to ASN.1 and back in X509_dup, we just up the reference count.
+ */
+static X509* X509_dup_nocopy(X509* x509) {
+    CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
+    return x509;
+}
+
+/**
  * BIO for InputStream
  */
 class BIO_Stream {
@@ -999,7 +1009,7 @@
             return -1;
         }
 
-        return 1;
+        return len;
     }
 };
 
@@ -3620,7 +3630,7 @@
         return;
     }
 
-    EVP_CIPHER_CTX_set_padding(ctx, enablePadding);
+    EVP_CIPHER_CTX_set_padding(ctx, enablePadding); // Not void, but always returns 1.
     JNI_TRACE("EVP_CIPHER_CTX_set_padding(%p, %d) => success", ctx, enablePadding);
 }
 
@@ -3635,16 +3645,24 @@
         return;
     }
 
-    EVP_CIPHER_CTX_set_key_length(ctx, keySizeBits);
+    if (!EVP_CIPHER_CTX_set_key_length(ctx, keySizeBits)) {
+        throwExceptionIfNecessary(env, "NativeCrypto_EVP_CIPHER_CTX_set_key_length");
+        JNI_TRACE("NativeCrypto_EVP_CIPHER_CTX_set_key_length => threw error");
+        return;
+    }
     JNI_TRACE("EVP_CIPHER_CTX_set_key_length(%p, %d) => success", ctx, keySizeBits);
 }
 
-static void NativeCrypto_EVP_CIPHER_CTX_cleanup(JNIEnv*, jclass, jlong ctxRef) {
+static void NativeCrypto_EVP_CIPHER_CTX_cleanup(JNIEnv* env, jclass, jlong ctxRef) {
     EVP_CIPHER_CTX* ctx = reinterpret_cast<EVP_CIPHER_CTX*>(ctxRef);
     JNI_TRACE("EVP_CIPHER_CTX_cleanup(%p)", ctx);
 
     if (ctx != NULL) {
-        EVP_CIPHER_CTX_cleanup(ctx);
+        if (!EVP_CIPHER_CTX_cleanup(ctx)) {
+            throwExceptionIfNecessary(env, "EVP_CIPHER_CTX_cleanup");
+            JNI_TRACE("EVP_CIPHER_CTX_cleanup => threw error");
+            return;
+        }
     }
     JNI_TRACE("EVP_CIPHER_CTX_cleanup(%p) => success", ctx);
 }
@@ -3681,7 +3699,7 @@
     }
 
     unsigned char* tmp = reinterpret_cast<unsigned char*>(outputBytes.get());
-    if (!RAND_bytes(tmp, outputBytes.size())) {
+    if (RAND_bytes(tmp, outputBytes.size()) <= 0) {
         throwExceptionIfNecessary(env, "NativeCrypto_RAND_bytes");
         JNI_TRACE("tmp=%p NativeCrypto_RAND_bytes => threw error", tmp);
         return;
@@ -3844,7 +3862,7 @@
     }
 
     env->GetByteArrayRegion(inputJavaBytes, offset, length, reinterpret_cast<jbyte*>(buffer.get()));
-    if (BIO_write(bio, buffer.get(), length) != 1) {
+    if (BIO_write(bio, buffer.get(), length) != length) {
         freeOpenSslErrorState();
         jniThrowException(env, "java/io/IOException", "BIO_write");
         JNI_TRACE("BIO_write(%p, %p, %d, %d) => IO error", bio, inputJavaBytes, offset, length);
@@ -4381,8 +4399,12 @@
         return;
     }
 
-    X509_CRL_print(bio, crl);
-    JNI_TRACE("X509_CRL_print(%p, %p) => success", bio, crl);
+    if (!X509_CRL_print(bio, crl)) {
+        throwExceptionIfNecessary(env, "X509_CRL_print");
+        JNI_TRACE("X509_CRL_print(%p, %p) => threw error", bio, crl);
+    } else {
+        JNI_TRACE("X509_CRL_print(%p, %p) => success", bio, crl);
+    }
 }
 
 static jstring NativeCrypto_get_X509_CRL_sig_alg_oid(JNIEnv* env, jclass, jlong x509CrlRef) {
@@ -4526,6 +4548,8 @@
     return reinterpret_cast<uintptr_t>(revoked->revocationDate);
 }
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wwrite-strings"
 static void NativeCrypto_X509_REVOKED_print(JNIEnv* env, jclass, jlong bioRef, jlong x509RevokedRef) {
     BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
     X509_REVOKED* revoked = reinterpret_cast<X509_REVOKED*>(static_cast<uintptr_t>(x509RevokedRef));
@@ -4543,16 +4567,14 @@
         return;
     }
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wwrite-strings"
     BIO_printf(bio, "Serial Number: ");
     i2a_ASN1_INTEGER(bio, revoked->serialNumber);
     BIO_printf(bio, "\nRevocation Date: ");
     ASN1_TIME_print(bio, revoked->revocationDate);
     BIO_printf(bio, "\n");
     X509V3_extensions_print(bio, "CRL entry extensions", revoked->extensions, 0, 0);
-#pragma GCC diagnostic pop
 }
+#pragma GCC diagnostic pop
 
 static jbyteArray NativeCrypto_get_X509_CRL_crl_enc(JNIEnv* env, jclass, jlong x509CrlRef) {
     X509_CRL* crl = reinterpret_cast<X509_CRL*>(static_cast<uintptr_t>(x509CrlRef));
@@ -4950,7 +4972,7 @@
     ScopedLocalRef<jlongArray> certArray(env, env->NewLongArray(size));
     ScopedLongArrayRW certs(env, certArray.get());
     for (size_t i = 0; i < size; i++) {
-        X509* item = reinterpret_cast<X509*>(sk_X509_value(path.get(), i));
+        X509* item = reinterpret_cast<X509*>(sk_X509_shift(path.get()));
         certs[i] = reinterpret_cast<uintptr_t>(item);
     }
 
@@ -4974,7 +4996,7 @@
 
     for (size_t i = 0; i < certsArray.size(); i++) {
         X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(certsArray[i]));
-        sk_X509_push(certStack.get(), X509_dup(x509));
+        sk_X509_push(certStack.get(), X509_dup_nocopy(x509));
     }
 
     int len;
@@ -5079,8 +5101,12 @@
         return;
     }
 
-    X509_print_ex(bio, x509, nmflag, certflag);
-    JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => success", bio, x509, nmflag, certflag);
+    if (!X509_print_ex(bio, x509, nmflag, certflag)) {
+        throwExceptionIfNecessary(env, "X509_print_ex");
+        JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => threw error", bio, x509, nmflag, certflag);
+    } else {
+        JNI_TRACE("X509_print_ex(%p, %p, %ld, %ld) => success", bio, x509, nmflag, certflag);
+    }
 }
 
 static jlong NativeCrypto_X509_get_pubkey(JNIEnv* env, jclass, jlong x509Ref) {
@@ -7217,14 +7243,14 @@
         JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => threw exception", ssl);
         return NULL;
     }
-    if (!sk_X509_push(chain.get(), certificate)) {
+    if (!sk_X509_push(chain.get(), X509_dup_nocopy(certificate))) {
         jniThrowOutOfMemory(env, "Unable to push local certificate");
         JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
         return NULL;
     }
     STACK_OF(X509)* cert_chain = SSL_get_certificate_chain(ssl, certificate);
     for (int i=0; i<sk_X509_num(cert_chain); i++) {
-        if (!sk_X509_push(chain.get(), sk_X509_value(cert_chain, i))) {
+        if (!sk_X509_push(chain.get(), X509_dup_nocopy(sk_X509_value(cert_chain, i)))) {
             jniThrowOutOfMemory(env, "Unable to push local certificate chain");
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
             return NULL;
@@ -7252,13 +7278,21 @@
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => NULL", ssl);
             return NULL;
         }
-        chain_copy.reset(sk_X509_dup(chain));
+        chain_copy.reset(sk_X509_new_null());
         if (chain_copy.get() == NULL) {
             jniThrowOutOfMemory(env, "Unable to allocate peer certificate chain");
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate dup error", ssl);
             return NULL;
         }
-        if (!sk_X509_push(chain_copy.get(), x509)) {
+        size_t chain_size = sk_X509_num(chain);
+        for (size_t i = 0; i < chain_size; i++) {
+            if (!sk_X509_push(chain_copy.get(), X509_dup_nocopy(sk_X509_value(chain, i)))) {
+                jniThrowOutOfMemory(env, "Unable to push server's peer certificate chain");
+                JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate chain push error", ssl);
+                return NULL;
+            }
+        }
+        if (!sk_X509_push(chain_copy.get(), X509_dup_nocopy(x509))) {
             jniThrowOutOfMemory(env, "Unable to push server's peer certificate");
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate push error", ssl);
             return NULL;
@@ -8163,8 +8197,6 @@
     outputStream_flushMethod = env->GetMethodID(outputStreamClass, "flush", "()V");
 }
 
-#if defined(CONSCRYPT_UNBUNDLED)
-
 static jclass findClass(JNIEnv* env, const char* name) {
     ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
     jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
@@ -8176,7 +8208,7 @@
 }
 
 // Use JNI_OnLoad for when we're standalone
-int JNI_OnLoad(JavaVM *vm, void* reserved) {
+int JNI_OnLoad(JavaVM *vm, void*) {
     JNI_TRACE("JNI_OnLoad NativeCrypto");
     gJavaVM = vm;
 
@@ -8198,24 +8230,3 @@
     initialize_conscrypt(env);
     return JNI_VERSION_1_6;
 }
-
-#else
-
-// Use this when built into Android
-void register_org_conscrypt_NativeCrypto(JNIEnv* env) {
-    JNI_TRACE("register_org_conscrypt_NativeCrypto");
-
-    byteArrayClass = JniConstants::byteArrayClass;
-    calendarClass = JniConstants::calendarClass;
-    inputStreamClass = JniConstants::inputStreamClass;
-    integerClass = JniConstants::integerClass;
-    objectClass = JniConstants::objectClass;
-    objectArrayClass = JniConstants::objectArrayClass;
-    outputStreamClass = JniConstants::outputStreamClass;
-    stringClass = JniConstants::stringClass;
-
-    env->GetJavaVM(&gJavaVM);
-    initialize_conscrypt(env);
-}
-
-#endif // defined(CONSCRYPT_UNBUNDLED)
diff --git a/crypto/src/test/java/org/conscrypt/NativeCryptoTest.java b/crypto/src/test/java/org/conscrypt/NativeCryptoTest.java
index 8692f8a..1118a49 100644
--- a/crypto/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/crypto/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -337,6 +337,8 @@
     }
 
     public void test_SSL_use_PrivateKey_for_tls_channel_id() throws Exception {
+        initChannelIdKey();
+
         try {
             NativeCrypto.SSL_set1_tls_channel_id(NULL, NULL);
             fail();
diff --git a/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
similarity index 90%
rename from libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java
rename to dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index d3ec95a..6a1a493 100644
--- a/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -18,7 +18,9 @@
 
 import java.io.File;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.List;
 
 /**
  * Base class for common functionality between various dex-based
@@ -48,9 +50,14 @@
 
     @Override
     protected Class<?> findClass(String name) throws ClassNotFoundException {
-        Class c = pathList.findClass(name);
+        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
+        Class c = pathList.findClass(name, suppressedExceptions);
         if (c == null) {
-            throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
+            ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
+            for (Throwable t : suppressedExceptions) {
+                cnfe.addSuppressed(t);
+            }
+            throw cnfe;
         }
         return c;
     }
diff --git a/libdvm/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
similarity index 87%
rename from libdvm/src/main/java/dalvik/system/DexFile.java
rename to dalvik/src/main/java/dalvik/system/DexFile.java
index 18b730b..d2fd5da 100644
--- a/libdvm/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -19,7 +19,9 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.List;
 import libcore.io.ErrnoException;
 import libcore.io.Libcore;
 import libcore.io.StructStat;
@@ -169,9 +171,11 @@
      *             normally should not happen
      */
     public void close() throws IOException {
-        guard.close();
-        closeDexFile(mCookie);
-        mCookie = 0;
+        if (mCookie != 0) {
+            guard.close();
+            closeDexFile(mCookie);
+            mCookie = 0;
+        }
     }
 
     /**
@@ -197,7 +201,7 @@
      */
     public Class loadClass(String name, ClassLoader loader) {
         String slashName = name.replace('.', '/');
-        return loadClassBinaryName(slashName, loader);
+        return loadClassBinaryName(slashName, loader, null);
     }
 
     /**
@@ -207,11 +211,29 @@
      *
      * @hide
      */
-    public Class loadClassBinaryName(String name, ClassLoader loader) {
-        return defineClass(name, loader, mCookie);
+    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
+        return defineClass(name, loader, mCookie, suppressed);
     }
 
-    private native static Class defineClass(String name, ClassLoader loader, int cookie);
+    private static Class defineClass(String name, ClassLoader loader, int cookie,
+                                     List<Throwable> suppressed) {
+        Class result = null;
+        try {
+            result = defineClassNative(name, loader, cookie);
+        } catch (NoClassDefFoundError e) {
+            if (suppressed != null) {
+                suppressed.add(e);
+            }
+        } catch (ClassNotFoundException e) {
+            if (suppressed != null) {
+                suppressed.add(e);
+            }
+        }
+        return result;
+    }
+
+    private static native Class defineClassNative(String name, ClassLoader loader, int cookie)
+        throws ClassNotFoundException, NoClassDefFoundError;
 
     /**
      * Enumerate the names of the classes in this DEX file.
@@ -269,7 +291,14 @@
      * Open a DEX file.  The value returned is a magic VM cookie.  On
      * failure, an IOException is thrown.
      */
-    native private static int openDexFile(String sourceName, String outputName,
+    private static int openDexFile(String sourceName, String outputName,
+        int flags) throws IOException {
+        return openDexFileNative(new File(sourceName).getCanonicalPath(),
+                                 (outputName == null) ? null : new File(outputName).getCanonicalPath(),
+                                 flags);
+    }
+
+    native private static int openDexFileNative(String sourceName, String outputName,
         int flags) throws IOException;
 
     /*
diff --git a/libdvm/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
similarity index 89%
rename from libdvm/src/main/java/dalvik/system/DexPathList.java
rename to dalvik/src/main/java/dalvik/system/DexPathList.java
index a10e90e..ff0be41 100644
--- a/libdvm/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -24,7 +24,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
-import java.util.regex.Pattern;
+import java.util.List;
 import java.util.zip.ZipFile;
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
@@ -65,6 +65,11 @@
     private final File[] nativeLibraryDirectories;
 
     /**
+     * Exceptions thrown during creation of the dexElements list.
+     */
+    private final IOException[] dexElementsSuppressedExceptions;
+
+    /**
      * Constructs an instance.
      *
      * @param definingContext the context in which any as-yet unresolved
@@ -103,7 +108,15 @@
         }
 
         this.definingContext = definingContext;
-        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
+        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
+        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
+                                           suppressedExceptions);
+        if (suppressedExceptions.size() > 0) {
+            this.dexElementsSuppressedExceptions =
+                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
+        } else {
+            dexElementsSuppressedExceptions = null;
+        }
         this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
     }
 
@@ -137,17 +150,13 @@
      * do not refer to existing and readable directories.
      */
     private static File[] splitLibraryPath(String path) {
-        /*
-         * Native libraries may exist in both the system and
-         * application library paths, and we use this search order:
-         *
-         *   1. this class loader's library path for application
-         *      libraries
-         *   2. the VM's library path from the system
-         *      property for system libraries
-         *
-         * This order was reversed prior to Gingerbread; see http://b/2933456.
-         */
+        // Native libraries may exist in both the system and
+        // application library paths, and we use this search order:
+        //
+        //   1. this class loader's library path for application libraries
+        //   2. the VM's library path from the system property for system libraries
+        //
+        // This order was reversed prior to Gingerbread; see http://b/2933456.
         ArrayList<File> result = splitPaths(path, System.getProperty("java.library.path"), true);
         return result.toArray(new File[result.size()]);
     }
@@ -194,10 +203,9 @@
      * Makes an array of dex/resource path elements, one per element of
      * the given array.
      */
-    private static Element[] makeDexElements(ArrayList<File> files,
-            File optimizedDirectory) {
+    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
+                                             ArrayList<IOException> suppressedExceptions) {
         ArrayList<Element> elements = new ArrayList<Element>();
-
         /*
          * Open all files and load the (direct or contained) dex files
          * up front.
@@ -220,14 +228,14 @@
 
                 try {
                     dex = loadDexFile(file, optimizedDirectory);
-                } catch (IOException ignored) {
+                } catch (IOException suppressed) {
                     /*
-                     * IOException might get thrown "legitimately" by
-                     * the DexFile constructor if the zip file turns
-                     * out to be resource-only (that is, no
-                     * classes.dex file in it). Safe to just ignore
-                     * the exception here, and let dex == null.
+                     * IOException might get thrown "legitimately" by the DexFile constructor if the
+                     * zip file turns out to be resource-only (that is, no classes.dex file in it).
+                     * Let dex == null and hang on to the exception to add to the tea-leaves for
+                     * when findClass returns null.
                      */
+                    suppressedExceptions.add(suppressed);
                 }
             } else if (file.isDirectory()) {
                 // We support directories for looking up resources.
@@ -301,21 +309,25 @@
      * defined, then this method will define it in the defining
      * context that this instance was constructed with.
      *
+     * @param name of class to find
+     * @param suppressed exceptions encountered whilst finding the class
      * @return the named class or {@code null} if the class is not
      * found in any of the dex files
      */
-    public Class findClass(String name) {
+    public Class findClass(String name, List<Throwable> suppressed) {
         for (Element element : dexElements) {
             DexFile dex = element.dexFile;
 
             if (dex != null) {
-                Class clazz = dex.loadClassBinaryName(name, definingContext);
+                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                 if (clazz != null) {
                     return clazz;
                 }
             }
         }
-
+        if (dexElementsSuppressedExceptions != null) {
+            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
+        }
         return null;
     }
 
diff --git a/libdvm/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
similarity index 84%
rename from libdvm/src/main/java/dalvik/system/VMDebug.java
rename to dalvik/src/main/java/dalvik/system/VMDebug.java
index a80cfcc..a46119f 100644
--- a/libdvm/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -30,14 +30,6 @@
  */
 public final class VMDebug {
     /**
-     * Specifies the default method trace data file name.
-     *
-     * @deprecated Only used in one place, which is unused and deprecated.
-     */
-    @Deprecated
-    static public final String DEFAULT_METHOD_TRACE_FILE_NAME = "/sdcard/dmtrace.trace";
-
-    /**
      * flag for startMethodTracing(), which adds the results from
      * startAllocCounting to the trace key file.
      */
@@ -144,7 +136,7 @@
      */
     @Deprecated
     public static void startMethodTracing() {
-        startMethodTracing(DEFAULT_METHOD_TRACE_FILE_NAME, 0, 0);
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -166,12 +158,7 @@
      * is currently defined is {@link #TRACE_COUNT_ALLOCS}.
      */
     public static void startMethodTracing(String traceFileName, int bufferSize, int flags) {
-
-        if (traceFileName == null) {
-            throw new NullPointerException("traceFileName == null");
-        }
-
-        startMethodTracingNative(traceFileName, null, bufferSize, flags);
+        startMethodTracingFilename(traceFileName, checkBufferSize(bufferSize), flags);
     }
 
     /**
@@ -179,17 +166,11 @@
      * FileDescriptor in which the trace is written.  The file name is also
      * supplied simply for logging.  Makes a dup of the file descriptor.
      */
-    public static void startMethodTracing(String traceFileName,
-        FileDescriptor fd, int bufferSize, int flags)
-    {
-        if (traceFileName == null) {
-            throw new NullPointerException("traceFileName == null");
-        }
+    public static void startMethodTracing(String traceFileName, FileDescriptor fd, int bufferSize, int flags) {
         if (fd == null) {
             throw new NullPointerException("fd == null");
         }
-
-        startMethodTracingNative(traceFileName, fd, bufferSize, flags);
+        startMethodTracingFd(traceFileName, fd, checkBufferSize(bufferSize), flags);
     }
 
     /**
@@ -197,20 +178,30 @@
      * is called, the result is sent directly to DDMS.  (If DDMS is not
      * attached when tracing ends, the profiling data will be discarded.)
      */
-    public static void startMethodTracingDdms(int bufferSize, int flags) {
-        startMethodTracingNative(null, null, bufferSize, flags);
+    public static void startMethodTracingDdms(int bufferSize, int flags, boolean samplingEnabled, int intervalUs) {
+        startMethodTracingDdmsImpl(checkBufferSize(bufferSize), flags, samplingEnabled, intervalUs);
     }
 
-    /**
-     * Implements all startMethodTracing variants.
-     */
-    private static native void startMethodTracingNative(String traceFileName,
-        FileDescriptor fd, int bufferSize, int flags);
+    private static int checkBufferSize(int bufferSize) {
+        if (bufferSize == 0) {
+            // Default to 8MB per the documentation.
+            bufferSize = 8 * 1024 * 1024;
+        }
+        if (bufferSize < 1024) {
+            throw new IllegalArgumentException("buffer size < 1024: " + bufferSize);
+        }
+        return bufferSize;
+    }
+
+    private static native void startMethodTracingDdmsImpl(int bufferSize, int flags, boolean samplingEnabled, int intervalUs);
+    private static native void startMethodTracingFd(String traceFileName, FileDescriptor fd, int bufferSize, int flags);
+    private static native void startMethodTracingFilename(String traceFileName, int bufferSize, int flags);
 
     /**
-     * Determine whether method tracing is currently active.
+     * Determine whether method tracing is currently active and what type is
+     * active.
      */
-    public static native boolean isMethodTracingActive();
+    public static native int getMethodTracingMode();
 
     /**
      * Stops method tracing.
@@ -252,7 +243,7 @@
 
     /**
      * This method exists for binary compatibility.  It was part of
-     * the allocation limits API which was removed in Honeycomb.
+     * the allocation limits API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public static int setAllocationLimit(int limit) {
@@ -261,7 +252,7 @@
 
     /**
      * This method exists for binary compatibility.  It was part of
-     * the allocation limits API which was removed in Honeycomb.
+     * the allocation limits API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public static int setGlobalAllocationLimit(int limit) {
@@ -373,4 +364,25 @@
      * @return the number of matching instances.
      */
     public static native long countInstancesOfClass(Class klass, boolean assignable);
+
+    /**
+     * Export the heap per-space stats for dumpsys meminfo.
+     *
+     * The content of the array is:
+     *
+     * <pre>
+     *   data[0] : the application heap space size
+     *   data[1] : the application heap space allocated bytes
+     *   data[2] : the application heap space free bytes
+     *   data[3] : the zygote heap space size
+     *   data[4] : the zygote heap space allocated size
+     *   data[5] : the zygote heap space free size
+     *   data[6] : the large object space size
+     *   data[7] : the large object space allocated bytes
+     *   data[8] : the large object space free bytes
+     * </pre>
+     *
+     * @param data the array into which the stats are written.
+     */
+    public static native void getHeapSpaceStats(long[] data);
 }
diff --git a/dex/src/main/java/com/android/dex/Dex.java b/dex/src/main/java/com/android/dex/Dex.java
index 29cd30e..116a33c 100644
--- a/dex/src/main/java/com/android/dex/Dex.java
+++ b/dex/src/main/java/com/android/dex/Dex.java
@@ -53,78 +53,19 @@
     private static final int CHECKSUM_SIZE = 4;
     private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
     private static final int SIGNATURE_SIZE = 20;
+    // Provided as a convenience to avoid a memory allocation to benefit Dalvik.
+    // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
+    static final short[] EMPTY_SHORT_ARRAY = new short[0];
 
     private ByteBuffer data;
     private final TableOfContents tableOfContents = new TableOfContents();
     private int nextSectionStart = 0;
-
-    private static abstract class AbstractRandomAccessList<T>
-            extends AbstractList<T> implements RandomAccess {
-    }
-
-    private List<String> strings = new AbstractRandomAccessList<String>() {
-        @Override public String get(int index) {
-            checkBounds(index, tableOfContents.stringIds.size);
-            return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
-                    .readString();
-        }
-        @Override public int size() {
-            return tableOfContents.stringIds.size;
-        }
-    };
-
-    private final List<Integer> typeIds = new AbstractRandomAccessList<Integer>() {
-        @Override public Integer get(int index) {
-            checkBounds(index, tableOfContents.typeIds.size);
-            return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();
-        }
-        @Override public int size() {
-            return tableOfContents.typeIds.size;
-        }
-    };
-
-    private final List<String> typeNames = new AbstractRandomAccessList<String>() {
-        @Override public String get(int index) {
-            checkBounds(index, tableOfContents.typeIds.size);
-            return strings.get(typeIds.get(index));
-        }
-        @Override public int size() {
-            return tableOfContents.typeIds.size;
-        }
-    };
-
-    private final List<ProtoId> protoIds = new AbstractRandomAccessList<ProtoId>() {
-        @Override public ProtoId get(int index) {
-            checkBounds(index, tableOfContents.protoIds.size);
-            return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
-                    .readProtoId();
-        }
-        @Override public int size() {
-            return tableOfContents.protoIds.size;
-        }
-    };
-
-    private final List<FieldId> fieldIds = new AbstractRandomAccessList<FieldId>() {
-        @Override public FieldId get(int index) {
-            checkBounds(index, tableOfContents.fieldIds.size);
-            return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
-                    .readFieldId();
-        }
-        @Override public int size() {
-            return tableOfContents.fieldIds.size;
-        }
-    };
-
-    private final List<MethodId> methodIds = new AbstractRandomAccessList<MethodId>() {
-        @Override public MethodId get(int index) {
-            checkBounds(index, tableOfContents.methodIds.size);
-            return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
-                    .readMethodId();
-        }
-        @Override public int size() {
-            return tableOfContents.methodIds.size;
-        }
-    };
+    private final StringTable strings = new StringTable();
+    private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
+    private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
+    private final ProtoIdTable protoIds = new ProtoIdTable();
+    private final FieldIdTable fieldIds = new FieldIdTable();
+    private final MethodIdTable methodIds = new MethodIdTable();
 
     /**
      * Creates a new dex that reads from {@code data}. It is an error to modify
@@ -312,31 +253,7 @@
     }
 
     public Iterable<ClassDef> classDefs() {
-        return new Iterable<ClassDef>() {
-            public Iterator<ClassDef> iterator() {
-                if (!tableOfContents.classDefs.exists()) {
-                    return Collections.<ClassDef>emptySet().iterator();
-                }
-                return new Iterator<ClassDef>() {
-                    private Dex.Section in = open(tableOfContents.classDefs.off);
-                    private int count = 0;
-
-                    public boolean hasNext() {
-                        return count < tableOfContents.classDefs.size;
-                    }
-                    public ClassDef next() {
-                        if (!hasNext()) {
-                            throw new NoSuchElementException();
-                        }
-                        count++;
-                        return in.readClassDef();
-                    }
-                    public void remove() {
-                        throw new UnsupportedOperationException();
-                    }
-                };
-            }
-        };
+        return new ClassDefIterable();
     }
 
     public TypeList readTypeList(int offset) {
@@ -412,6 +329,187 @@
         open(CHECKSUM_OFFSET).writeInt(computeChecksum());
     }
 
+    /**
+     * Look up a field id name index from a field index. Cheaper than:
+     * {@code fieldIds().get(fieldDexIndex).getNameIndex();}
+     */
+    public int nameIndexFromFieldIndex(int fieldIndex) {
+        checkBounds(fieldIndex, tableOfContents.fieldIds.size);
+        int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
+        position += SizeOf.USHORT;  // declaringClassIndex
+        position += SizeOf.USHORT;  // typeIndex
+        return data.getInt(position);  // nameIndex
+    }
+
+    public int findStringIndex(String s) {
+        return Collections.binarySearch(strings, s);
+    }
+
+    public int findTypeIndex(String descriptor) {
+        return Collections.binarySearch(typeNames, descriptor);
+    }
+
+    public int findFieldIndex(FieldId fieldId) {
+        return Collections.binarySearch(fieldIds, fieldId);
+    }
+
+    public int findMethodIndex(MethodId methodId) {
+        return Collections.binarySearch(methodIds, methodId);
+    }
+
+    public int findClassDefIndexFromTypeIndex(int typeIndex) {
+        checkBounds(typeIndex, tableOfContents.typeIds.size);
+        if (!tableOfContents.classDefs.exists()) {
+            return -1;
+        }
+        for (int i = 0; i < tableOfContents.classDefs.size; i++) {
+            if (typeIndexFromClassDefIndex(i) == typeIndex) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Look up a field id type index from a field index. Cheaper than:
+     * {@code fieldIds().get(fieldDexIndex).getTypeIndex();}
+     */
+    public int typeIndexFromFieldIndex(int fieldIndex) {
+        checkBounds(fieldIndex, tableOfContents.fieldIds.size);
+        int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
+        position += SizeOf.USHORT;  // declaringClassIndex
+        return data.getShort(position) & 0xFFFF;  // typeIndex
+    }
+
+    /**
+     * Look up a method id declaring class index from a method index. Cheaper than:
+     * {@code methodIds().get(methodIndex).getDeclaringClassIndex();}
+     */
+    public int declaringClassIndexFromMethodIndex(int methodIndex) {
+        checkBounds(methodIndex, tableOfContents.methodIds.size);
+        int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+        return data.getShort(position) & 0xFFFF;  // declaringClassIndex
+    }
+
+    /**
+     * Look up a method id name index from a method index. Cheaper than:
+     * {@code methodIds().get(methodIndex).getNameIndex();}
+     */
+    public int nameIndexFromMethodIndex(int methodIndex) {
+        checkBounds(methodIndex, tableOfContents.methodIds.size);
+        int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+        position += SizeOf.USHORT;  // declaringClassIndex
+        position += SizeOf.USHORT;  // protoIndex
+        return data.getInt(position);  // nameIndex
+    }
+
+    /**
+     * Look up a parameter type ids from a method index. Cheaper than:
+     * {@code readTypeList(protoIds.get(methodIds().get(methodDexIndex).getProtoIndex()).getParametersOffset()).getTypes();}
+     */
+    public short[] parameterTypeIndicesFromMethodIndex(int methodIndex) {
+        checkBounds(methodIndex, tableOfContents.methodIds.size);
+        int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+        position += SizeOf.USHORT;  // declaringClassIndex
+        int protoIndex = data.getShort(position) & 0xFFFF;
+        checkBounds(protoIndex, tableOfContents.protoIds.size);
+        position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
+        position += SizeOf.UINT;  // shortyIndex
+        position += SizeOf.UINT;  // returnTypeIndex
+        int parametersOffset = data.getInt(position);
+        if (parametersOffset == 0) {
+            return EMPTY_SHORT_ARRAY;
+        }
+        position = parametersOffset;
+        int size = data.getInt(position);
+        if (size <= 0) {
+            throw new AssertionError("Unexpected parameter type list size: " + size);
+        }
+        position += SizeOf.UINT;
+        short[] types = new short[size];
+        for (int i = 0; i < size; i++) {
+            types[i] = data.getShort(position);
+            position += SizeOf.USHORT;
+        }
+        return types;
+    }
+
+    /**
+     * Look up a method id return type index from a method index. Cheaper than:
+     * {@code protoIds().get(methodIds().get(methodDexIndex).getProtoIndex()).getReturnTypeIndex();}
+     */
+    public int returnTypeIndexFromMethodIndex(int methodIndex) {
+        checkBounds(methodIndex, tableOfContents.methodIds.size);
+        int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+        position += SizeOf.USHORT;  // declaringClassIndex
+        int protoIndex = data.getShort(position) & 0xFFFF;
+        checkBounds(protoIndex, tableOfContents.protoIds.size);
+        position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
+        position += SizeOf.UINT;  // shortyIndex
+        return data.getInt(position);  // returnTypeIndex
+    }
+
+    /**
+     * Look up a descriptor index from a type index. Cheaper than:
+     * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
+     */
+    public int descriptorIndexFromTypeIndex(int typeIndex) {
+       checkBounds(typeIndex, tableOfContents.typeIds.size);
+       int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
+       return data.getInt(position);
+    }
+
+    /**
+     * Look up a type index index from a class def index.
+     */
+    public int typeIndexFromClassDefIndex(int classDefIndex) {
+        checkBounds(classDefIndex, tableOfContents.classDefs.size);
+        int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
+        return data.getInt(position);
+    }
+
+    /**
+     * Look up a type index index from a class def index.
+     */
+    public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) {
+        checkBounds(classDefIndex, tableOfContents.classDefs.size);
+        int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
+        position += SizeOf.UINT;  // type
+        position += SizeOf.UINT;  // accessFlags
+        position += SizeOf.UINT;  // superType
+        position += SizeOf.UINT;  // interfacesOffset
+        position += SizeOf.UINT;  // sourceFileIndex
+        return data.getInt(position);
+    }
+
+    /**
+     * Look up interface types indices from a  return type index from a method index. Cheaper than:
+     * {@code ...getClassDef(classDefIndex).getInterfaces();}
+     */
+    public short[] interfaceTypeIndicesFromClassDefIndex(int classDefIndex) {
+        checkBounds(classDefIndex, tableOfContents.classDefs.size);
+        int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
+        position += SizeOf.UINT;  // type
+        position += SizeOf.UINT;  // accessFlags
+        position += SizeOf.UINT;  // superType
+        int interfacesOffset = data.getInt(position);
+        if (interfacesOffset == 0) {
+            return EMPTY_SHORT_ARRAY;
+        }
+        position = interfacesOffset;
+        int size = data.getInt(position);
+        if (size <= 0) {
+            throw new AssertionError("Unexpected interfaces list size: " + size);
+        }
+        position += SizeOf.UINT;
+        short[] types = new short[size];
+        for (int i = 0; i < size; i++) {
+            types[i] = data.getShort(position);
+            position += SizeOf.USHORT;
+        }
+        return types;
+    }
+
     public final class Section implements ByteInput, ByteOutput {
         private final String name;
         private final ByteBuffer data;
@@ -450,6 +548,9 @@
         }
 
         public short[] readShortArray(int length) {
+            if (length == 0) {
+                return EMPTY_SHORT_ARRAY;
+            }
             short[] result = new short[length];
             for (int i = 0; i < length; i++) {
                 result[i] = readShort();
@@ -475,10 +576,7 @@
 
         public TypeList readTypeList() {
             int size = readInt();
-            short[] types = new short[size];
-            for (int i = 0; i < size; i++) {
-                types[i] = readShort();
-            }
+            short[] types = readShortArray(size);
             alignToFourBytes();
             return new TypeList(Dex.this, types);
         }
@@ -552,71 +650,71 @@
             Try[] tries;
             CatchHandler[] catchHandlers;
             if (triesSize > 0) {
-              if (instructions.length % 2 == 1) {
-                  readShort(); // padding
-              }
+                if (instructions.length % 2 == 1) {
+                    readShort(); // padding
+                }
 
-              /*
-               * We can't read the tries until we've read the catch handlers.
-               * Unfortunately they're in the opposite order in the dex file
-               * so we need to read them out-of-order.
-               */
-              Section triesSection = open(data.position());
-              skip(triesSize * SizeOf.TRY_ITEM);
-              catchHandlers = readCatchHandlers();
-              tries = triesSection.readTries(triesSize, catchHandlers);
-          } else {
-              tries = new Try[0];
-              catchHandlers = new CatchHandler[0];
-          }
-          return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
-                  tries, catchHandlers);
+                /*
+                 * We can't read the tries until we've read the catch handlers.
+                 * Unfortunately they're in the opposite order in the dex file
+                 * so we need to read them out-of-order.
+                 */
+                Section triesSection = open(data.position());
+                skip(triesSize * SizeOf.TRY_ITEM);
+                catchHandlers = readCatchHandlers();
+                tries = triesSection.readTries(triesSize, catchHandlers);
+            } else {
+                tries = new Try[0];
+                catchHandlers = new CatchHandler[0];
+            }
+            return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
+                            tries, catchHandlers);
         }
 
         private CatchHandler[] readCatchHandlers() {
-          int baseOffset = data.position();
-          int catchHandlersSize = readUleb128();
-          CatchHandler[] result = new CatchHandler[catchHandlersSize];
-          for (int i = 0; i < catchHandlersSize; i++) {
-            int offset = data.position() - baseOffset;
-            result[i] = readCatchHandler(offset);
-          }
-          return result;
+            int baseOffset = data.position();
+            int catchHandlersSize = readUleb128();
+            CatchHandler[] result = new CatchHandler[catchHandlersSize];
+            for (int i = 0; i < catchHandlersSize; i++) {
+                int offset = data.position() - baseOffset;
+                result[i] = readCatchHandler(offset);
+            }
+            return result;
         }
 
         private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
-          Try[] result = new Try[triesSize];
-          for (int i = 0; i < triesSize; i++) {
-            int startAddress = readInt();
-            int instructionCount = readUnsignedShort();
-            int handlerOffset = readUnsignedShort();
-            int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
-            result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
-          }
-          return result;
+            Try[] result = new Try[triesSize];
+            for (int i = 0; i < triesSize; i++) {
+                int startAddress = readInt();
+                int instructionCount = readUnsignedShort();
+                int handlerOffset = readUnsignedShort();
+                int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
+                result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
+            }
+            return result;
         }
 
         private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
-          for (int i = 0; i < catchHandlers.length; i++) {
-            CatchHandler catchHandler = catchHandlers[i];
-            if (catchHandler.getOffset() == offset) {
-              return i;
+            for (int i = 0; i < catchHandlers.length; i++) {
+                CatchHandler catchHandler = catchHandlers[i];
+                if (catchHandler.getOffset() == offset) {
+                    return i;
+                }
             }
-          }
-          throw new IllegalArgumentException();
+            throw new IllegalArgumentException();
         }
 
         private CatchHandler readCatchHandler(int offset) {
-          int size = readSleb128();
-          int handlersCount = Math.abs(size);
-          int[] typeIndexes = new int[handlersCount];
-          int[] addresses = new int[handlersCount];
-          for (int i = 0; i < handlersCount; i++) {
-            typeIndexes[i] = readUleb128();
-            addresses[i] = readUleb128();
-          }
-          int catchAllAddress = size <= 0 ? readUleb128() : -1;
-          return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
+            int size = readSleb128();
+            int handlersCount = Math.abs(size);
+            int[] typeIndexes = new int[handlersCount];
+            int[] addresses = new int[handlersCount];
+            for (int i = 0; i < handlersCount; i++) {
+                typeIndexes[i] = readUleb128();
+                addresses[i] = readUleb128();
+            }
+            int catchAllAddress = size <= 0 ? readUleb128() : -1;
+            return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
         }
 
         private ClassData readClassData() {
@@ -680,11 +778,11 @@
         }
 
         public void skip(int count) {
-          if (count < 0) {
-              throw new IllegalArgumentException();
-          }
-          data.position(data.position() + count);
-      }
+            if (count < 0) {
+                throw new IllegalArgumentException();
+            }
+            data.position(data.position() + count);
+        }
 
         /**
          * Skips bytes until the position is aligned to a multiple of 4.
@@ -788,4 +886,98 @@
             return data.position() - initialPosition;
         }
     }
+
+    private final class StringTable extends AbstractList<String> implements RandomAccess {
+        @Override public String get(int index) {
+            checkBounds(index, tableOfContents.stringIds.size);
+            return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
+                    .readString();
+        }
+        @Override public int size() {
+            return tableOfContents.stringIds.size;
+        }
+    };
+
+    private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
+            implements RandomAccess {
+        @Override public Integer get(int index) {
+            return descriptorIndexFromTypeIndex(index);
+        }
+        @Override public int size() {
+            return tableOfContents.typeIds.size;
+        }
+    };
+
+    private final class TypeIndexToDescriptorTable extends AbstractList<String>
+            implements RandomAccess {
+        @Override public String get(int index) {
+            return strings.get(descriptorIndexFromTypeIndex(index));
+        }
+        @Override public int size() {
+            return tableOfContents.typeIds.size;
+        }
+    };
+
+    private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
+        @Override public ProtoId get(int index) {
+            checkBounds(index, tableOfContents.protoIds.size);
+            return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
+                    .readProtoId();
+        }
+        @Override public int size() {
+            return tableOfContents.protoIds.size;
+        }
+    };
+
+    private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
+        @Override public FieldId get(int index) {
+            checkBounds(index, tableOfContents.fieldIds.size);
+            return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+                    .readFieldId();
+        }
+        @Override public int size() {
+            return tableOfContents.fieldIds.size;
+        }
+    };
+
+    private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
+        @Override public MethodId get(int index) {
+            checkBounds(index, tableOfContents.methodIds.size);
+            return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+                    .readMethodId();
+        }
+        @Override public int size() {
+            return tableOfContents.methodIds.size;
+        }
+    };
+
+    private final class ClassDefIterator implements Iterator<ClassDef> {
+        private final Dex.Section in = open(tableOfContents.classDefs.off);
+        private int count = 0;
+
+        @Override
+        public boolean hasNext() {
+            return count < tableOfContents.classDefs.size;
+        }
+        @Override
+        public ClassDef next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            count++;
+            return in.readClassDef();
+        }
+        @Override
+            public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    };
+
+    private final class ClassDefIterable implements Iterable<ClassDef> {
+        public Iterator<ClassDef> iterator() {
+            return !tableOfContents.classDefs.exists()
+               ? Collections.<ClassDef>emptySet().iterator()
+               : new ClassDefIterator();
+        }
+    };
 }
diff --git a/dex/src/main/java/com/android/dex/DexFormat.java b/dex/src/main/java/com/android/dex/DexFormat.java
index 85941fd..9319bc2 100644
--- a/dex/src/main/java/com/android/dex/DexFormat.java
+++ b/dex/src/main/java/com/android/dex/DexFormat.java
@@ -56,6 +56,19 @@
     public static final int ENDIAN_TAG = 0x12345678;
 
     /**
+     * Maximum addressable field or method index.
+     * The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or
+     * meth@CCCC.
+     */
+    public static final int MAX_MEMBER_IDX = 0xFFFF;
+
+    /**
+     * Maximum addressable type index.
+     * The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC.
+     */
+    public static final int MAX_TYPE_IDX = 0xFFFF;
+
+    /**
      * Returns the API level corresponding to the given magic number,
      * or {@code -1} if the given array is not a well-formed dex file
      * magic number.
diff --git a/dex/src/main/java/com/android/dex/TypeList.java b/dex/src/main/java/com/android/dex/TypeList.java
index 6e321fb..123e82c 100644
--- a/dex/src/main/java/com/android/dex/TypeList.java
+++ b/dex/src/main/java/com/android/dex/TypeList.java
@@ -20,7 +20,7 @@
 
 public final class TypeList implements Comparable<TypeList> {
 
-    public static final TypeList EMPTY = new TypeList(null, new short[0]);
+    public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY);
 
     private final Dex dex;
     private final short[] types;
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index ae70c55..24813dd 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -793,12 +793,6 @@
   substring: "junit.framework.AssertionFailedError: expected:<1.1> but was:<2.0>"
 },
 {
-  description: "We don't permit runtime switching of the default charset (and neither does the RI)",
-  result: EXEC_FAILED,
-  name: "org.apache.harmony.nio_char.tests.java.nio.charset.CharsetTest#test_defaultCharset",
-  substring: "expected:<ISO-8859-1> but was:<UTF-8>"
-},
-{
   description: "this test needs external interaction",
   result: UNSUPPORTED,
   names: [
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 33f381f..5055cbf 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -3,6 +3,16 @@
  */
 [
 {
+  description: "four known CharsetDecoder-/CharsetEncoder-related failures",
+  names: [
+    "org.apache.harmony.nio_char.tests.java.nio.charset.ASCIICharsetEncoderTest#testCanEncodeSurrogate",
+    "org.apache.harmony.nio_char.tests.java.nio.charset.ASCIICharsetEncoderTest#testMultiStepEncode",
+    "org.apache.harmony.nio_char.tests.java.nio.charset.CharsetDecoderTest#testInvalidDecoding",
+    "org.apache.harmony.nio_char.tests.java.nio.charset.CharsetDecoderTest#test_decode"
+  ],
+  bug: 10729779
+},
+{
   description: "can't compile a pattern with negative look-behind and quantifiers with upper bounds",
   name: "org.apache.harmony.regex.tests.java.util.regex.PatternTest#test_bug_40103",
   bug: 40103
@@ -245,16 +255,16 @@
   description: "UTF-16 and UTF-32 encoders don't write big-endian output on little-endian devices",
   names: [
     "libcore.java.nio.charset.CharsetEncoderTest#test_defaultReplacementBytesUtf_16",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_16",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_32",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_16BE",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_16LE",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_32BE",
-    "libcore.java.nio.charset.CharsetTest#test_UTF_32LE",
-    "libcore.java.nio.charset.CharsetTest#test_x_UTF_16LE_BOM",
-    "libcore.java.nio.charset.CharsetTest#test_X_UTF_32BE_BOM",
-    "libcore.java.nio.charset.CharsetTest#test_X_UTF_32LE_BOM",
-    "libcore.java.nio.charset.OldCharset_MultiByte_UTF_16#test_Encode"
+    "libcore.java.nio.charset.OldCharset_MultiByte_UTF_16#test_Encode",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_16",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_32",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_16BE",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_16LE",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_32BE",
+    "tests.api.java.nio.charset.CharsetTest#test_UTF_32LE",
+    "tests.api.java.nio.charset.CharsetTest#test_x_UTF_16LE_BOM",
+    "tests.api.java.nio.charset.CharsetTest#test_X_UTF_32BE_BOM",
+    "tests.api.java.nio.charset.CharsetTest#test_X_UTF_32LE_BOM"
   ],
   bug: 2702411
 },
diff --git a/harmony-tests/src/test/java/org/apache/harmony/luni/tests/java/util/ScannerTest.java b/harmony-tests/src/test/java/org/apache/harmony/luni/tests/java/util/ScannerTest.java
index 459bce1..b3e1abc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/luni/tests/java/util/ScannerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/luni/tests/java/util/ScannerTest.java
@@ -4,9 +4,9 @@
  * 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.
@@ -15,15 +15,21 @@
  */
 package org.apache.harmony.luni.tests.java.util;
 
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.io.StringReader;
@@ -33,10 +39,12 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketAddress;
-import java.nio.CharBuffer;
 import java.nio.channels.FileChannel;
+import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
+import java.nio.CharBuffer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -44,9 +52,9 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.NoSuchElementException;
-import java.util.Scanner;
 import java.util.regex.MatchResult;
 import java.util.regex.Pattern;
+import java.util.Scanner;
 
 import junit.framework.TestCase;
 
@@ -88,24 +96,24 @@
 
         try {
             s = new Scanner(tmpFile);
-            fail("should throw FileNotFoundException");
-        } catch (FileNotFoundException e) {
-            // expected
+            fail();
+        } catch (FileNotFoundException expected) {
         }
 
         tmpFile = File.createTempFile("TestFileForScanner", ".tmp");
         FileOutputStream fos = new FileOutputStream(tmpFile);
         fos.write("test".getBytes());
+        fos.close();
 
         s = new Scanner(tmpFile);
+        s.close();
         tmpFile.delete();
 
         // Scanner(File = null)
         try {
             s = new Scanner((File) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // TODO: test if the default charset is used.
@@ -124,24 +132,21 @@
 
         try {
             s = new Scanner(tmpFile, Charset.defaultCharset().name());
-            fail("should throw FileNotFoundException");
-        } catch (FileNotFoundException e) {
-            // expected
+            fail();
+        } catch (FileNotFoundException expected) {
         }
 
         try {
             s = new Scanner(tmpFile, null);
-            fail("should throw FileNotFoundException");
-        } catch (FileNotFoundException e) {
-            // expected
+            fail();
+        } catch (FileNotFoundException expected) {
         }
 
         tmpFile = File.createTempFile("TestFileForScanner", ".tmp");
         try {
             s = new Scanner(tmpFile, "invalid charset");
-            fail("should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
 
         //fail on RI. File is opened but not closed when exception is thrown on
@@ -151,34 +156,30 @@
         // Scanner(File = null, Charset = null)
         try {
             s = new Scanner((File) null, null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // Scanner(File = null, Charset = UTF-8)
         try {
             s = new Scanner((File) null, "UTF-8");
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // Scanner(File = null, Charset = invalid)
         try {
             s = new Scanner((File) null, "invalid");
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // Scanner(File, Charset = null)
         try {
             File f = File.createTempFile("test", ".tmp");
             s = new Scanner(f, null);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
 
         // TODO: test if the specified charset is used.
@@ -195,9 +196,8 @@
         // Scanner(InputStream)
         try {
             s = new Scanner((InputStream) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // TODO: test if the default charset is used.
@@ -213,23 +213,20 @@
 
         try {
             s = new Scanner((PipedInputStream) null, "invalid charset");
-            fail("should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         try {
             s = new Scanner(new PipedInputStream(), null);
-            fail("should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         try {
             s = new Scanner(new PipedInputStream(), "invalid charset");
-            fail("should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
 
         // TODO: test if the specified charset is used.
@@ -246,9 +243,8 @@
         // Scanner(Readable)
         try {
             s = new Scanner((Readable) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
     }
 
@@ -267,9 +263,8 @@
         // Scanner(ReadableByteChannel)
         try {
             s = new Scanner((ReadableByteChannel) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // Test if the default charset is used.
@@ -307,9 +302,8 @@
         fc = new FileOutputStream(tmpFile).getChannel();
         try {
             s = new Scanner(fc, "invalid charset");
-            fail("should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
         fc.close();
         assertTrue(tmpFile.delete());
@@ -317,29 +311,49 @@
         // Scanner(ReadableByteChannel = null, Charset = null)
         try {
             s = new Scanner((ReadableByteChannel) null, null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // Scanner(ReadableByteChannel = null, Charset = invalid)
         try {
             s = new Scanner((ReadableByteChannel) null, "invalid");
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         // Scanner(ReadableByteChannel, Charset = null)
         try {
             s = new Scanner(fc, null);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
         // TODO: test if the specified charset is used.
     }
 
+    public void test_Constructor_LReadableByteChannel() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(null);
+
+        SocketChannel sc = SocketChannel.open();
+        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.configureBlocking(false);
+        assertFalse(sc.isBlocking());
+
+        ssc.accept().close();
+        ssc.close();
+        assertFalse(sc.isBlocking());
+
+        Scanner s = new Scanner(sc);
+        try {
+            s.hasNextInt();
+            fail();
+        } catch (IllegalBlockingModeException expected) {
+        }
+
+        sc.close();
+    }
+
     /**
      * @tests java.util.Scanner#Scanner(String)
      */
@@ -351,9 +365,8 @@
         // Scanner(String)
         try {
             s = new Scanner((String) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
     }
 
@@ -376,9 +389,8 @@
         // thrown out.
         try {
             fos.write(12);
-            fail("Should throw IOException");
-        } catch (IOException e) {
-            // expected
+            fail();
+        } catch (IOException expected) {
         }
 
         s.close(); // no exception should be thrown
@@ -427,9 +439,8 @@
         s = new Scanner("test");
         try {
             s.useDelimiter((String) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         s = new Scanner("test");
@@ -452,9 +463,8 @@
         s = new Scanner("test");
         try {
             s.useLocale(null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         s.useLocale(new Locale("test", "test"));
@@ -476,15 +486,13 @@
         s = new Scanner("test");
         try {
             s.useRadix(Character.MIN_RADIX - 1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
         try {
             s.useRadix(Character.MAX_RADIX + 1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
         s.useRadix(11);
         assertEquals(11, s.radix());
@@ -497,9 +505,8 @@
         s = new Scanner("aab*b*").useDelimiter("\\*");
         try {
             s.remove();
-            fail("should throw UnsupportedOperationException");
-        } catch (UnsupportedOperationException e) {
-            //Expected
+            fail();
+        } catch (UnsupportedOperationException expected) {
         }
     }
 
@@ -511,9 +518,8 @@
         s = new Scanner("1 2 ");
         try {
             s.match();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         assertEquals("1", s.next());
         assertEquals("2", s.next());
@@ -527,29 +533,25 @@
         assertEquals(0, result.groupCount());
         try {
             result.start(1);
-            fail("should throw IndexOutOfBoundsException");
-        } catch (IndexOutOfBoundsException e) {
-            // Expected
+            fail();
+        } catch (IndexOutOfBoundsException expected) {
         }
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
         try {
             s.match();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         s = new Scanner("True faLse");
         try {
             s.match();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         assertTrue(s.nextBoolean());
         result = s.match();
@@ -562,17 +564,15 @@
         assertFalse(s.nextBoolean());
         try {
             s.nextBoolean();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
         try {
             s.match();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         s = new Scanner("True faLse");
         assertTrue(s.nextBoolean());
         result = s.match();
@@ -585,9 +585,8 @@
         s.close();
         try {
             s.nextBoolean();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         result = s.match();
         assertEquals(0, result.start());
@@ -596,30 +595,27 @@
         assertEquals(4, result.end(0));
         assertEquals("True", result.group());
         assertEquals(0, result.groupCount());
-        
+
         s = new Scanner("True fase");
         assertTrue(s.nextBoolean());
         assertEquals(0, result.groupCount());
         try {
             s.nextBoolean();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         try {
             s.match();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         s = new Scanner("True fase");
         assertTrue(s.nextBoolean());
         try {
             s.next((Pattern)null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // Expected
+            fail();
+        } catch (NullPointerException expected) {
         }
         result = s.match();
         assertEquals(0, result.start());
@@ -628,9 +624,9 @@
         assertEquals(4, result.end(0));
         assertEquals("True", result.group());
         assertEquals(0, result.groupCount());
-        
+
     }
-     
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#next()
@@ -647,28 +643,25 @@
         assertEquals("2", s.next());
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
-        
+
         s = new Scanner("a").useDelimiter("a?");
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
-        
+
         s = new Scanner("aa").useDelimiter("a?");
         assertEquals("", s.next());
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
-        
+
 
         s = new Scanner("word( )test( )").useDelimiter("\\( \\)");
         assertEquals("word", s.next());
@@ -685,27 +678,24 @@
         // test boundary case
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // just delimiter exists in this scanner
         s = new Scanner(" ");
         try {
             s.next();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // nothing exists in this scanner
         s = new Scanner("");
         try {
             s.next();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // no delimiter exists in this scanner
@@ -741,23 +731,21 @@
                 Pattern.MULTILINE));
         assertEquals("test\n", s.next());
         assertEquals("test", s.next());
-        
+
         s = new Scanner("").useDelimiter(Pattern.compile("^",
                 Pattern.MULTILINE));
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
-        
+
         s = new Scanner("").useDelimiter(Pattern.compile("^*",
                 Pattern.MULTILINE));
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("test\ntest").useDelimiter(Pattern.compile("^*",
@@ -786,13 +774,12 @@
         assertEquals("2", s.next());
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#next(Pattern)
@@ -804,9 +791,8 @@
         assertEquals("aab", s.next(pattern));
         try {
             s.next(pattern);
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word ? ");
@@ -814,9 +800,8 @@
         assertEquals("word", s.next(pattern));
         try {
             s.next(pattern);
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word1 word2  ");
@@ -826,9 +811,8 @@
         // test boundary case
         try {
             s.next(pattern);
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // test socket inputStream
@@ -841,9 +825,8 @@
         assertEquals("aab", s.next(pattern));
         try {
             s.next(pattern);
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
     }
 
@@ -856,18 +839,16 @@
         assertEquals("b", s.next("a*b"));
         try {
             s.next("a*b");
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word ? ");
         assertEquals("word", s.next("\\w+"));
         try {
             s.next("\\w+");
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word1 next  ");
@@ -876,9 +857,8 @@
         // test boundary case
         try {
             s.next("\\w+");
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // test socket inputStream
@@ -889,12 +869,11 @@
         assertEquals("aab", s.next("a*b"));
         try {
             s.next("a*b");
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextBoolean()
@@ -909,25 +888,22 @@
         assertFalse(s.nextBoolean());
         try {
             s.nextBoolean();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("true1");
         try {
             s.nextBoolean();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         try {
             s = new Scanner("");
             s.nextBoolean();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // test socket inputStream
@@ -943,16 +919,15 @@
         assertTrue(s.nextBoolean());
         try {
             s.nextBoolean();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("false( )").useDelimiter("\\( \\)");
         assertFalse(s.nextBoolean());
 
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextInt(int)
@@ -963,9 +938,8 @@
         assertEquals(456, s.nextInt(10));
         try {
             s.nextInt(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -973,18 +947,16 @@
         assertEquals(38, s.nextInt(5));
         try {
             s.nextInt(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("123456789123456789123456789123456789");
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -995,9 +967,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -1011,9 +982,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -1027,9 +997,8 @@
         assertEquals(102, s.nextInt(10));
         try {
             s.nextInt(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertEquals(162, s.nextInt(10));
 
@@ -1041,9 +1010,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -1055,9 +1023,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -1086,7 +1053,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextInt(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextInt(10));
@@ -1094,7 +1061,7 @@
         /*
          * There are three types of negative prefix all in all. '' '-' '(' There
          * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
-         * must be used togethor. Prefix '-' and suffix '-' must be used 
+         * must be used togethor. Prefix '-' and suffix '-' must be used
          * exclusively.
          */
 
@@ -1112,9 +1079,8 @@
         assertEquals(-123, s.nextInt(10));
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("-123 123-");
@@ -1122,9 +1088,8 @@
         assertEquals(-123, s.nextInt(10));
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         // Skip the un-recognizable token 123-.
         assertEquals("123-", s.next());
@@ -1133,15 +1098,13 @@
         // RI
         try {
             s.nextInt(Character.MIN_RADIX - 1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // Expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
         try {
             s.nextInt(Character.MAX_RADIX + 1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // Expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
     }
 
@@ -1155,9 +1118,8 @@
         assertEquals(456, s.nextInt());
         try {
             s.nextInt();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -1166,18 +1128,16 @@
         assertEquals(38, s.nextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("123456789123456789123456789123456789");
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -1188,9 +1148,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -1204,9 +1163,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -1221,9 +1179,8 @@
         s.useRadix(5);
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertEquals(162, s.nextInt());
@@ -1236,9 +1193,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -1250,9 +1206,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -1284,7 +1239,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextInt());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextInt());
@@ -1310,9 +1265,8 @@
         assertEquals(-123, s.nextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("-123 123-");
@@ -1320,14 +1274,13 @@
         assertEquals(-123, s.nextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         // Skip the un-recognizable token 123-.
         assertEquals("123-", s.next());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextByte(int)
@@ -1338,9 +1291,8 @@
         assertEquals(126, s.nextByte(10));
         try {
             s.nextByte(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -1348,18 +1300,16 @@
         assertEquals(38, s.nextByte(5));
         try {
             s.nextByte(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("1234");
         try {
             s.nextByte(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -1369,9 +1319,8 @@
         assertEquals(102, s.nextByte(10));
         try {
             s.nextByte(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertEquals(126, s.nextByte(10));
 
@@ -1392,7 +1341,7 @@
         s = new Scanner("1\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(100, s.nextByte(10));
-        
+
         s = new Scanner("1\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(100, s.nextByte(10));
@@ -1400,7 +1349,7 @@
         s = new Scanner("-123");
         s.useLocale(new Locale("ar", "AE"));
         assertEquals(-123, s.nextByte(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
@@ -1417,9 +1366,8 @@
         assertEquals(126, s.nextByte());
         try {
             s.nextByte();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -1428,18 +1376,16 @@
         assertEquals(38, s.nextByte());
         try {
             s.nextByte();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("1234");
         try {
             s.nextByte();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -1450,9 +1396,8 @@
         s.useRadix(5);
         try {
             s.nextByte();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertEquals(126, s.nextByte());
@@ -1475,7 +1420,7 @@
         s = new Scanner("1\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(100, s.nextByte());
-        
+
         s = new Scanner("1\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(100, s.nextByte());
@@ -1488,7 +1433,7 @@
         s.useLocale(new Locale("mk", "MK"));
         assertEquals(-123, s.nextByte());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextFloat()
@@ -1502,9 +1447,8 @@
         assertEquals((float)0.123, s.nextFloat());
         try {
             s.nextFloat();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("+123.4 -456.7 123,456.789 0.1\u06623,4");
@@ -1514,9 +1458,8 @@
         assertEquals((float)123456.789, s.nextFloat());
         try {
             s.nextFloat();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // Scientific notation
@@ -1534,7 +1477,7 @@
         String str=String.valueOf(Float.MAX_VALUE*2);
         s=new Scanner(str);
         assertEquals(Float.POSITIVE_INFINITY,s.nextFloat());
-        
+
         /*
          * Different locale can only recognize corresponding locale sensitive
          * string. ',' is used in many locales as group separator.
@@ -1565,25 +1508,23 @@
 //        assertEquals((float)-123.4, s.nextFloat());
         try {
             s.nextFloat();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("123- -123");
         s.useLocale(new Locale("mk", "MK"));
         try {
             s.nextFloat();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         // Skip the un-recognizable token 123-.
         assertEquals("123-", s.next());
         assertEquals((float)-123.0, s.nextFloat());
 
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextBigInteger(int)
@@ -1594,9 +1535,8 @@
         assertEquals(new BigInteger("456"), s.nextBigInteger(10));
         try {
             s.nextBigInteger(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -1604,9 +1544,8 @@
         assertEquals(new BigInteger("38"), s.nextBigInteger(5));
         try {
             s.nextBigInteger(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -1617,9 +1556,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -1633,9 +1571,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -1649,9 +1586,8 @@
         assertEquals(new BigInteger("102"), s.nextBigInteger(10));
         try {
             s.nextBigInteger(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertEquals(new BigInteger("162"), s.nextBigInteger(10));
 
@@ -1663,9 +1599,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -1677,9 +1612,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -1703,7 +1637,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(new BigInteger("12300"), s.nextBigInteger(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(new BigInteger("12300"), s.nextBigInteger(10));
@@ -1711,7 +1645,7 @@
         s = new Scanner("-123");
         s.useLocale(new Locale("ar", "AE"));
         assertEquals(new BigInteger("-123"), s.nextBigInteger(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
@@ -1728,9 +1662,8 @@
         assertEquals(new BigInteger("456"), s.nextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -1739,9 +1672,8 @@
         assertEquals(new BigInteger("38"), s.nextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -1752,9 +1684,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -1768,9 +1699,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -1785,9 +1715,8 @@
         s.useRadix(5);
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertEquals(new BigInteger("162"), s.nextBigInteger());
@@ -1800,9 +1729,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -1814,9 +1742,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -1841,7 +1768,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(new BigInteger("12300"), s.nextBigInteger());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(new BigInteger("12300"), s.nextBigInteger());
@@ -1854,7 +1781,7 @@
         s.useLocale(new Locale("mk", "MK"));
         assertEquals(new BigInteger("-123"), s.nextBigInteger());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextShort(int)
@@ -1865,9 +1792,8 @@
         assertEquals(456, s.nextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -1875,18 +1801,16 @@
         assertEquals(38, s.nextShort(5));
         try {
             s.nextShort(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("123456789");
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -1897,9 +1821,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -1913,9 +1836,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -1929,9 +1851,8 @@
         assertEquals(102, s.nextShort(10));
         try {
             s.nextShort(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertEquals(162, s.nextShort(10));
 
@@ -1943,9 +1864,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -1957,9 +1877,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -1983,7 +1902,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextShort(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextShort(10));
@@ -1991,7 +1910,7 @@
         s = new Scanner("-123");
         s.useLocale(new Locale("ar", "AE"));
         assertEquals(-123, s.nextShort(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
@@ -2008,9 +1927,8 @@
         assertEquals(456, s.nextShort());
         try {
             s.nextShort();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -2019,18 +1937,16 @@
         assertEquals(38, s.nextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("123456789");
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -2041,9 +1957,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -2057,9 +1972,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -2074,9 +1988,8 @@
         s.useRadix(5);
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertEquals(162, s.nextShort());
@@ -2089,9 +2002,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -2103,9 +2015,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -2130,7 +2041,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextShort());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextShort());
@@ -2143,7 +2054,7 @@
         s.useLocale(new Locale("mk", "MK"));
         assertEquals(-123, s.nextShort());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextLong(int)
@@ -2154,9 +2065,8 @@
         assertEquals(456, s.nextLong(10));
         try {
             s.nextLong(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -2164,18 +2074,16 @@
         assertEquals(38, s.nextLong(5));
         try {
             s.nextLong(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("123456789123456789123456789123456789");
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -2186,9 +2094,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -2202,9 +2109,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -2218,9 +2124,8 @@
         assertEquals(102, s.nextLong(10));
         try {
             s.nextLong(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertEquals(162, s.nextLong(10));
 
@@ -2232,9 +2137,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -2246,9 +2150,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -2272,7 +2175,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextLong(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextLong(10));
@@ -2280,13 +2183,13 @@
         s = new Scanner("-123");
         s.useLocale(new Locale("ar", "AE"));
         assertEquals(-123, s.nextLong(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
         assertEquals(-123, s.nextLong(10));
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextLong()
@@ -2297,9 +2200,8 @@
         assertEquals(456, s.nextLong());
         try {
             s.nextLong();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -2308,18 +2210,16 @@
         assertEquals(38, s.nextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
         s = new Scanner("123456789123456789123456789123456789");
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -2330,9 +2230,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -2346,9 +2245,8 @@
         s.useLocale(Locale.GERMANY);
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -2363,9 +2261,8 @@
         s.useRadix(5);
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertEquals(162, s.nextLong());
@@ -2378,9 +2275,8 @@
         s.useLocale(Locale.CHINESE);
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -2392,9 +2288,8 @@
         s.useLocale(Locale.ENGLISH);
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -2419,7 +2314,7 @@
         s = new Scanner("123\u0966\u0966");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextLong());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertEquals(12300, s.nextLong());
@@ -2432,7 +2327,7 @@
         s.useLocale(new Locale("mk", "MK"));
         assertEquals(-123, s.nextLong());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNext()
@@ -2447,9 +2342,8 @@
         s.close();
         try {
             s.hasNext();
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("1( )2( )").useDelimiter("\\( \\)");
@@ -2472,9 +2366,8 @@
         // test boundary case
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("1'\n'2  ");
@@ -2485,9 +2378,8 @@
         // test boundary case
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("  ");
@@ -2505,12 +2397,11 @@
         assertFalse(s.hasNext());
         try {
             s.next();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNext(Pattern)
@@ -2524,9 +2415,8 @@
         assertFalse(s.hasNext(pattern));
         try {
             s.next(pattern);
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word ? ");
@@ -2536,9 +2426,8 @@
         assertFalse(s.hasNext(pattern));
         try {
             s.next(pattern);
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word1 WorD2  ");
@@ -2550,27 +2439,24 @@
         assertFalse(s.hasNext(pattern));
         try {
             s.next(pattern);
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("word1 WorD2  ");
         pattern = Pattern.compile("\\w+");
         try {
             s.hasNext((Pattern) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
         s.close();
         try {
             s.hasNext(pattern);
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         // test socket inputStream
         os.write("aab b".getBytes());
         serverSocket.close();
@@ -2582,12 +2468,11 @@
         assertFalse(s.hasNext(pattern));
         try {
             s.next(pattern);
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNext(String)
@@ -2596,27 +2481,24 @@
         s = new Scanner("aab@2@abb@").useDelimiter("\\@");
         try {
             s.hasNext((String)null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
-        
+
         s = new Scanner("aab*b*").useDelimiter("\\*");
         assertTrue(s.hasNext("a+b"));
         assertEquals("aab", s.next("a+b"));
         assertFalse(s.hasNext("a+b"));
         try {
             s.next("a+b");
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.close();
         try {
             s.hasNext("a+b");
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("WORD ? ");
@@ -2625,9 +2507,8 @@
         assertFalse(s.hasNext("\\w+"));
         try {
             s.next("\\w+");
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("word1 word2  ");
@@ -2636,9 +2517,8 @@
         // test boundary case
         try {
             s.next("\\w+");
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // test socket inputStream
@@ -2652,12 +2532,11 @@
         assertFalse(s.hasNext("a*b"));
         try {
             s.next("a*b");
-            fail("should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextBoolean()
@@ -2693,9 +2572,8 @@
         assertFalse(s.hasNextBoolean());
         try {
             s.nextBoolean();
-            fail("should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("false( )").useDelimiter("\\( \\)");
@@ -2704,7 +2582,7 @@
         assertFalse(s.hasNextBoolean());
 
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextByte(int)
@@ -2718,9 +2596,8 @@
         assertFalse(s.hasNextByte(10));
         try {
             s.nextByte(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -2730,9 +2607,8 @@
         assertFalse(s.hasNextByte(5));
         try {
             s.nextByte(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -2740,9 +2616,8 @@
         assertFalse(s.hasNextByte(10));
         try {
             s.nextByte(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -2754,9 +2629,8 @@
         assertFalse(s.hasNextByte(5));
         try {
             s.nextByte(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertTrue(s.hasNextByte(10));
         assertEquals(126, s.nextByte(10));
@@ -2782,7 +2656,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextByte(10));
         assertEquals(100, s.nextByte(10));
-        
+
         s = new Scanner("1\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextByte(10));
@@ -2792,7 +2666,7 @@
         s.useLocale(new Locale("ar", "AE"));
         assertTrue(s.hasNextByte(10));
         assertEquals(-123, s.nextByte(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
@@ -2800,42 +2674,34 @@
         assertEquals(-123, s.nextByte(10));
     }
 
-    /**
-     * @throws IOException
-     * @tests java.util.Scanner#hasNextByte(int)
-     */
     public void test_hasNextByteI_cache() throws IOException{
         //regression for HARMONY-2063
-    	s = new Scanner("123 45");
-		assertTrue(s.hasNextByte(8));
-		assertEquals(83, s.nextByte());
-		assertEquals(45, s.nextByte());
+        s = new Scanner("123 45");
+        assertTrue(s.hasNextByte(8));
+        assertEquals(83, s.nextByte());
+        assertEquals(45, s.nextByte());
 
-		s = new Scanner("123 45");
-		assertTrue(s.hasNextByte(10));
-		assertTrue(s.hasNextByte(8));
-		assertEquals(83, s.nextByte());
-		assertEquals(45, s.nextByte());
+        s = new Scanner("123 45");
+        assertTrue(s.hasNextByte(10));
+        assertTrue(s.hasNextByte(8));
+        assertEquals(83, s.nextByte());
+        assertEquals(45, s.nextByte());
 
-		s = new Scanner("-123 -45");
-		assertTrue(s.hasNextByte(8));
-		assertEquals(-123, s.nextInt());
-		assertEquals(-45, s.nextByte());
-		
-		s = new Scanner("123 45");
-		assertTrue(s.hasNextByte());
-		s.close();
-		try {
-			s.nextByte();
-			fail("Should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-			// expected
-		}
+        s = new Scanner("-123 -45");
+        assertTrue(s.hasNextByte(8));
+        assertEquals(-123, s.nextInt());
+        assertEquals(-45, s.nextByte());
+
+        s = new Scanner("123 45");
+        assertTrue(s.hasNextByte());
+        s.close();
+        try {
+          s.nextByte();
+          fail();
+        } catch (IllegalStateException expected) {
+        }
     }
-    /**
-     * @throws IOException
-     * @tests java.util.Scanner#hasNextByte()
-     */
+
     public void test_hasNextByte() throws IOException {
         s = new Scanner("123 126");
         assertTrue(s.hasNextByte());
@@ -2845,9 +2711,8 @@
         assertFalse(s.hasNextByte());
         try {
             s.nextByte();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -2858,9 +2723,8 @@
         assertFalse(s.hasNextByte());
         try {
             s.nextByte();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -2868,9 +2732,8 @@
         assertFalse(s.hasNextByte());
         try {
             s.nextByte();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -2883,9 +2746,8 @@
         assertFalse(s.hasNextByte());
         try {
             s.nextByte();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertTrue(s.hasNextByte());
@@ -2912,7 +2774,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextByte());
         assertEquals(100, s.nextByte());
-        
+
         s = new Scanner("1\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextByte());
@@ -2928,7 +2790,7 @@
         assertTrue(s.hasNextByte());
         assertEquals(-123, s.nextByte());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextBigInteger(int)
@@ -2942,9 +2804,8 @@
         assertFalse(s.hasNextBigInteger(10));
         try {
             s.nextBigInteger(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -2954,9 +2815,8 @@
         assertFalse(s.hasNextBigInteger(5));
         try {
             s.nextBigInteger(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -2968,9 +2828,8 @@
         assertFalse(s.hasNextBigInteger(10));
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -2987,9 +2846,8 @@
         assertFalse(s.hasNextBigInteger(10));
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -3007,9 +2865,8 @@
         assertFalse(s.hasNextBigInteger(5));
         try {
             s.nextBigInteger(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertTrue(s.hasNextBigInteger(10));
         assertEquals(new BigInteger("162"), s.nextBigInteger(10));
@@ -3023,9 +2880,8 @@
         assertFalse(s.hasNextBigInteger(10));
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -3040,9 +2896,8 @@
         assertFalse(s.hasNextBigInteger(10));
         try {
             s.nextBigInteger(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -3071,7 +2926,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextBigInteger(10));
         assertEquals(new BigInteger("12300"), s.nextBigInteger(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextBigInteger(10));
@@ -3081,47 +2936,46 @@
         s.useLocale(new Locale("ar", "AE"));
         assertTrue(s.hasNextBigInteger(10));
         assertEquals(new BigInteger("-123"), s.nextBigInteger(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
         assertTrue(s.hasNextBigInteger(10));
         assertEquals(new BigInteger("-123"), s.nextBigInteger(10));
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextBigInteger(int)
      */
     public void test_hasNextBigIntegerI_cache() throws IOException {
         //regression for HARMONY-2063
-    	s = new Scanner("123 123456789123456789");
-		assertTrue(s.hasNextBigInteger(16));
-		assertEquals(new BigInteger("291"), s.nextBigInteger());
-		assertEquals(new BigInteger("123456789123456789"), s.nextBigInteger());
+        s = new Scanner("123 123456789123456789");
+        assertTrue(s.hasNextBigInteger(16));
+        assertEquals(new BigInteger("291"), s.nextBigInteger());
+        assertEquals(new BigInteger("123456789123456789"), s.nextBigInteger());
 
-		s = new Scanner("123456789123456789 456");
-		assertTrue(s.hasNextBigInteger(16));
-		assertTrue(s.hasNextBigInteger(10));
-		assertEquals(new BigInteger("123456789123456789"), s.nextBigInteger());
-		assertEquals(new BigInteger("456"), s.nextBigInteger());
+        s = new Scanner("123456789123456789 456");
+        assertTrue(s.hasNextBigInteger(16));
+        assertTrue(s.hasNextBigInteger(10));
+        assertEquals(new BigInteger("123456789123456789"), s.nextBigInteger());
+        assertEquals(new BigInteger("456"), s.nextBigInteger());
 
-		s = new Scanner("-123 -123456789123456789");
-		assertTrue(s.hasNextBigInteger(8));
-		assertEquals(-123, s.nextShort());
-		assertEquals(new BigInteger("-123456789123456789"), s.nextBigInteger());
-		
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextBigInteger());
-		s.close();
-		try {
-			s.nextBigInteger();
-			fail("Should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-			// expected
-		}
+        s = new Scanner("-123 -123456789123456789");
+        assertTrue(s.hasNextBigInteger(8));
+        assertEquals(-123, s.nextShort());
+        assertEquals(new BigInteger("-123456789123456789"), s.nextBigInteger());
+
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextBigInteger());
+        s.close();
+        try {
+            s.nextBigInteger();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextBigInteger()
@@ -3135,9 +2989,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -3148,9 +3001,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -3162,9 +3014,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -3181,9 +3032,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -3201,9 +3051,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertTrue(s.hasNextBigInteger());
@@ -3218,9 +3067,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -3235,9 +3083,8 @@
         assertFalse(s.hasNextBigInteger());
         try {
             s.nextBigInteger();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -3267,7 +3114,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextBigInteger());
         assertEquals(new BigInteger("12300"), s.nextBigInteger());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextBigInteger());
@@ -3283,7 +3130,7 @@
         assertTrue(s.hasNextBigInteger());
         assertEquals(new BigInteger("-123"), s.nextBigInteger());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextInt(int)
@@ -3296,9 +3143,8 @@
         assertFalse(s.hasNextInt(10));
         try {
             s.nextInt(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -3308,9 +3154,8 @@
         assertFalse(s.hasNextInt(5));
         try {
             s.nextInt(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -3353,9 +3198,8 @@
         assertFalse(s.hasNextInt(10));
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         assertTrue(s.hasNextInt(10));
@@ -3366,9 +3210,8 @@
         assertFalse(s.hasNextInt(10));
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -3393,9 +3236,8 @@
         // If parameter radix is illegal, the following test case fails on RI
         try {
             s.hasNextInt(Character.MIN_RADIX - 1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // Expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
 
         /*
@@ -3420,7 +3262,7 @@
         /*
          * There are three types of negative prefix all in all. '' '-' '(' There
          * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
-         * must be used togethor. Prefix '-' and suffix '-' must be used 
+         * must be used together. Prefix '-' and suffix '-' must be used
          * exclusively.
          */
 
@@ -3428,7 +3270,7 @@
          * According to Integer regular expression: Integer :: = ( [-+]? (*
          * Numeral ) ) | LocalPositivePrefix Numeral LocalPositiveSuffix |
          * LocalNegativePrefix Numeral LocalNegativeSuffix 123- should be
-         * recognized by scanner with locale ar_AE, (123) shouble be recognized
+         * recognized by scanner with locale ar_AE, (123) should be recognized
          * by scanner with locale mk_MK. But this is not the case on RI.
          */
         s = new Scanner("-123 123- -123-");
@@ -3441,9 +3283,8 @@
         assertFalse(s.hasNextInt(10));
         try {
             s.nextInt(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("-123 123-");
@@ -3453,9 +3294,8 @@
         assertFalse(s.hasNextInt(10));
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         // Skip the un-recognizable token 123-.
         assertEquals("123-", s.next());
@@ -3467,33 +3307,32 @@
      */
     public void test_hasNextIntI_cache() throws IOException {
         //regression for HARMONY-2063
-    	s = new Scanner("123 456");
-		assertTrue(s.hasNextInt(16));
-		assertEquals(291, s.nextInt(10));
-		assertEquals(456, s.nextInt());
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextInt(16));
+        assertEquals(291, s.nextInt(10));
+        assertEquals(456, s.nextInt());
 
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextInt(16));
-		assertTrue(s.hasNextInt(8));
-		assertEquals(83, s.nextInt());
-		assertEquals(456, s.nextInt());
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextInt(16));
+        assertTrue(s.hasNextInt(8));
+        assertEquals(83, s.nextInt());
+        assertEquals(456, s.nextInt());
 
-		s = new Scanner("-123 -456 -789");
-		assertTrue(s.hasNextInt(8));
-		assertEquals(-123, s.nextShort());
-		assertEquals(-456, s.nextInt());
-		assertTrue(s.hasNextShort(16));
-		assertEquals(-789, s.nextInt());
-		
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextInt());
-		s.close();
-		try {
-			s.nextInt();
-			fail("Should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-			// expected
-		}
+        s = new Scanner("-123 -456 -789");
+        assertTrue(s.hasNextInt(8));
+        assertEquals(-123, s.nextShort());
+        assertEquals(-456, s.nextInt());
+        assertTrue(s.hasNextShort(16));
+        assertEquals(-789, s.nextInt());
+
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextInt());
+        s.close();
+        try {
+            s.nextInt();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
     }
     /**
      * @throws IOException
@@ -3507,9 +3346,8 @@
         assertFalse(s.hasNextInt());
         try {
             s.nextInt();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -3520,9 +3358,8 @@
         assertFalse(s.hasNextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -3571,9 +3408,8 @@
         assertFalse(s.hasNextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -3619,7 +3455,7 @@
         /*
          * There are three types of negative prefix all in all. '' '-' '(' There
          * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
-         * must be used togethor. Prefix '-' and suffix '-' must be used 
+         * must be used togethor. Prefix '-' and suffix '-' must be used
          * exclusively.
          */
 
@@ -3640,9 +3476,8 @@
         assertFalse(s.hasNextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("-123 123-");
@@ -3651,14 +3486,13 @@
         assertEquals(-123, s.nextInt());
         try {
             s.nextInt();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         // Skip the un-recognizable token 123-.
         assertEquals("123-", s.next());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextFloat()
@@ -3677,9 +3511,8 @@
         assertFalse(s.hasNextFloat());
         try {
             s.nextFloat();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("+123.4 -456.7 123,456.789 0.1\u06623,4");
@@ -3693,9 +3526,8 @@
         assertFalse(s.hasNextFloat());
         try {
             s.nextFloat();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // Scientific notation
@@ -3720,7 +3552,7 @@
         s=new Scanner(str);
         assertTrue(s.hasNextFloat());
         assertEquals(Float.POSITIVE_INFINITY,s.nextFloat());
-        
+
         /*
          * Different locale can only recognize corresponding locale sensitive
          * string. ',' is used in many locales as group separator.
@@ -3759,9 +3591,8 @@
 //        assertEquals((float)-123.4, s.nextFloat());
 //        try {
 //            s.nextFloat();
-//            fail("Should throw InputMismatchException");
-//        } catch (InputMismatchException e) {
-//            // Expected
+//            fail();
+//        } catch (InputMismatchException expected) {
 //        }
 
         s = new Scanner("123- -123");
@@ -3769,28 +3600,26 @@
         assertFalse(s.hasNextFloat());
         try {
             s.nextFloat();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         // Skip the un-recognizable token 123-.
         assertEquals("123-", s.next());
         assertTrue(s.hasNextFloat());
         assertEquals((float)-123.0, s.nextFloat());
-        
+
         s = new Scanner("+123.4 -456.7");
         s.useLocale(Locale.ENGLISH);
         assertTrue(s.hasNextFloat());
         s.close();
         try{
-        	s.nextFloat();
-        	fail("Should throw IllegalStateException");
-        }catch(IllegalStateException e){
-        	//expected
+            s.nextFloat();
+            fail();
+        }catch(IllegalStateException expected) {
         }
 
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextShort(int)
@@ -3804,9 +3633,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -3816,9 +3644,8 @@
         assertFalse(s.hasNextShort(5));
         try {
             s.nextShort(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -3826,9 +3653,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -3840,9 +3666,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -3859,9 +3684,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -3879,9 +3703,8 @@
         assertFalse(s.hasNextShort(5));
         try {
             s.nextShort(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertTrue(s.hasNextShort(10));
         assertEquals(162, s.nextShort(10));
@@ -3895,9 +3718,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -3912,9 +3734,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextShort(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -3943,7 +3764,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextShort(10));
         assertEquals(12300, s.nextShort(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextShort(10));
@@ -3953,7 +3774,7 @@
         s.useLocale(new Locale("ar", "AE"));
         assertTrue(s.hasNextShort(10));
         assertEquals(-123, s.nextShort(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
@@ -3974,9 +3795,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -3987,9 +3807,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -3997,9 +3816,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -4011,9 +3829,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -4030,9 +3847,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -4050,9 +3866,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertTrue(s.hasNextShort());
@@ -4067,9 +3882,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -4084,9 +3898,8 @@
         assertFalse(s.hasNextShort());
         try {
             s.nextShort();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -4116,7 +3929,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextShort());
         assertEquals(12300, s.nextShort());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextShort());
@@ -4132,46 +3945,45 @@
         assertTrue(s.hasNextShort());
         assertEquals(-123, s.nextShort());
     }
-    
+
     /**
-	 * @throws IOException
-	 * @tests java.util.Scanner#hasNextShort(int)
-	 */
-	public void test_hasNextShortI_cache() throws IOException {
+     * @throws IOException
+     * @tests java.util.Scanner#hasNextShort(int)
+     */
+    public void test_hasNextShortI_cache() throws IOException {
         //regression for HARMONY-2063
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextShort(16));
-		assertEquals(291, s.nextShort());
-		assertEquals(456, s.nextShort());
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextShort(16));
+        assertEquals(291, s.nextShort());
+        assertEquals(456, s.nextShort());
 
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextShort(16));
-		assertTrue(s.hasNextShort(8));
-		assertEquals(83, s.nextShort());
-		assertEquals(456, s.nextShort());
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextShort(16));
+        assertTrue(s.hasNextShort(8));
+        assertEquals(83, s.nextShort());
+        assertEquals(456, s.nextShort());
 
-		s = new Scanner("-123 -456 -789");
-		assertTrue(s.hasNextShort(8));
-		assertEquals(-123, s.nextInt());
-		assertEquals(-456, s.nextShort());
-		assertTrue(s.hasNextInt(16));
-		assertEquals(-789, s.nextShort());
-		
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextShort());
-		s.close();
-		try {
-			s.nextShort();
-			fail("Should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-			// expected
-		}
-	}
-    
+        s = new Scanner("-123 -456 -789");
+        assertTrue(s.hasNextShort(8));
+        assertEquals(-123, s.nextInt());
+        assertEquals(-456, s.nextShort());
+        assertTrue(s.hasNextInt(16));
+        assertEquals(-789, s.nextShort());
+
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextShort());
+        s.close();
+        try {
+            s.nextShort();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
     /**
-	 * @throws IOException
-	 * @tests java.util.Scanner#hasNextLong(int)
-	 */
+     * @throws IOException
+     * @tests java.util.Scanner#hasNextLong(int)
+     */
     public void test_hasNextLongI() throws IOException {
         s = new Scanner("123 456");
         assertTrue(s.hasNextLong(10));
@@ -4181,9 +3993,8 @@
         assertFalse(s.hasNextLong(10));
         try {
             s.nextLong(10);
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -4193,9 +4004,8 @@
         assertFalse(s.hasNextLong(5));
         try {
             s.nextLong(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -4203,9 +4013,8 @@
         assertFalse(s.hasNextLong(10));
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -4217,9 +4026,8 @@
         assertFalse(s.hasNextShort(10));
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -4236,9 +4044,8 @@
         assertFalse(s.hasNextLong(10));
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -4256,9 +4063,8 @@
         assertFalse(s.hasNextLong(5));
         try {
             s.nextLong(5);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         assertTrue(s.hasNextLong(10));
         assertEquals(162, s.nextLong(10));
@@ -4272,9 +4078,8 @@
         assertFalse(s.hasNextLong(10));
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -4289,9 +4094,8 @@
         assertFalse(s.hasNextLong(10));
         try {
             s.nextLong(10);
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -4320,7 +4124,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextLong(10));
         assertEquals(12300, s.nextLong(10));
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextLong(10));
@@ -4330,47 +4134,46 @@
         s.useLocale(new Locale("ar", "AE"));
         assertTrue(s.hasNextLong(10));
         assertEquals(-123, s.nextLong(10));
-       
+
 
         s = new Scanner("-123");
         s.useLocale(new Locale("mk", "MK"));
         assertTrue(s.hasNextLong(10));
         assertEquals(-123, s.nextLong(10));
     }
-    
+
     /**
-	 * @throws IOException
-	 * @tests java.util.Scanner#hasNextLong(int)
-	 */
+     * @throws IOException
+     * @tests java.util.Scanner#hasNextLong(int)
+     */
     public void test_hasNextLongI_cache() throws IOException {
         //regression for HARMONY-2063
-    	s = new Scanner("123 456");
-		assertTrue(s.hasNextLong(16));
-		assertEquals(291, s.nextLong());
-		assertEquals(456, s.nextLong());
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextLong(16));
+        assertEquals(291, s.nextLong());
+        assertEquals(456, s.nextLong());
 
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextLong(16));
-		assertTrue(s.hasNextLong(8));
-		assertEquals(83, s.nextLong());
-		assertEquals(456, s.nextLong());
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextLong(16));
+        assertTrue(s.hasNextLong(8));
+        assertEquals(83, s.nextLong());
+        assertEquals(456, s.nextLong());
 
-		s = new Scanner("-123 -456 -789");
-		assertTrue(s.hasNextLong(8));
-		assertEquals(-123, s.nextInt());
-		assertEquals(-456, s.nextLong());
-		assertTrue(s.hasNextShort(16));
-		assertEquals(-789, s.nextLong());
-		
-		s = new Scanner("123 456");
-		assertTrue(s.hasNextLong());
-		s.close();
-		try {
-			s.nextLong();
-			fail("Should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-			// expected
-		}
+        s = new Scanner("-123 -456 -789");
+        assertTrue(s.hasNextLong(8));
+        assertEquals(-123, s.nextInt());
+        assertEquals(-456, s.nextLong());
+        assertTrue(s.hasNextShort(16));
+        assertEquals(-789, s.nextLong());
+
+        s = new Scanner("123 456");
+        assertTrue(s.hasNextLong());
+        s.close();
+        try {
+            s.nextLong();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
     }
 
     /**
@@ -4386,9 +4189,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         // If the radix is different from 10
@@ -4399,9 +4201,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // If the number is out of range
@@ -4409,9 +4210,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -4423,9 +4223,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.ENGLISH);
         // If exception is thrown out, input will not be advanced.
@@ -4442,9 +4241,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(new Locale("it", "CH"));
         // If exception is thrown out, input will not be advanced.
@@ -4462,9 +4260,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useRadix(10);
         assertTrue(s.hasNextLong());
@@ -4479,9 +4276,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
         s.useLocale(Locale.GERMANY);
         // If exception is thrown out, input will not be advanced.
@@ -4496,9 +4292,8 @@
         assertFalse(s.hasNextLong());
         try {
             s.nextLong();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         s = new Scanner("03456");
@@ -4528,7 +4323,7 @@
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextLong());
         assertEquals(12300, s.nextLong());
-        
+
         s = new Scanner("123\u0e50\u0e50");
         s.useLocale(Locale.CHINESE);
         assertTrue(s.hasNextLong());
@@ -4544,7 +4339,7 @@
         assertTrue(s.hasNextLong());
         assertEquals(-123, s.nextLong());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextDouble()
@@ -4563,9 +4358,8 @@
         assertFalse(s.hasNextDouble());
         try {
             s.nextDouble();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("+123.4 -456.7 123,456.789 0.1\u06623,4");
@@ -4579,9 +4373,8 @@
         assertFalse(s.hasNextDouble());
         try {
             s.nextDouble();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // Scientific notation
@@ -4606,7 +4399,7 @@
         s=new Scanner(str);
         assertTrue(s.hasNextDouble());
         assertEquals(Double.POSITIVE_INFINITY,s.nextDouble());
-        
+
         /*
          * Different locale can only recognize corresponding locale sensitive
          * string. ',' is used in many locales as group separator.
@@ -4639,19 +4432,18 @@
         s.useLocale(Locale.ENGLISH);
         assertTrue(s.hasNextDouble());
         assertEquals(-123.4, s.nextDouble());
-        
+
         s = new Scanner("+123.4 -456.7");
         s.useLocale(Locale.ENGLISH);
         assertTrue(s.hasNextDouble());
         s.close();
         try{
-        	s.nextDouble();
-        	fail("Should throw IllegalStateException");
-        }catch(IllegalStateException e){
-        	//expected
+            s.nextDouble();
+            fail();
+        }catch(IllegalStateException expected) {
         }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#hasNextBigDecimal()
@@ -4670,9 +4462,8 @@
         assertFalse(s.hasNextBigDecimal());
         try {
             s.nextBigDecimal();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("+123.4 -456.7 123,456.789 0.1\u06623,4");
@@ -4686,9 +4477,8 @@
         assertFalse(s.hasNextBigDecimal());
         try {
             s.nextBigDecimal();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // Scientific notation
@@ -4705,9 +4495,8 @@
         assertFalse(s.hasNextBigDecimal());
         try {
             s.nextBigDecimal();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -4740,7 +4529,7 @@
         assertTrue(s.hasNextBigDecimal());
         assertEquals(new BigDecimal("-123.4"), s.nextBigDecimal());
     }
-    
+
     private static class MockStringReader extends StringReader {
 
         public MockStringReader(String param) {
@@ -4756,22 +4545,22 @@
         }
 
     }
-    
+
     private static class MockStringReader2Read extends StringReader {
-        private int timesRead = 1;
+        int timesRead = 0;
 
         public MockStringReader2Read(String param) {
             super(param);
         }
 
         public int read(CharBuffer target) throws IOException {
-            if (timesRead == 1) {
+            if (timesRead == 0) {
                 target.append('1');
                 target.append('2');
                 target.append('3');
                 timesRead++;
                 return 3;
-            } else if (timesRead == 2) {
+            } else if (timesRead == 1) {
                 target.append('t');
                 timesRead++;
                 return 1;
@@ -4781,11 +4570,22 @@
         }
 
     }
-    
+
+    // https://code.google.com/p/android/issues/detail?id=40555
+    public void test_40555() throws Exception {
+      MockStringReader2Read reader = new MockStringReader2Read("MockStringReader");
+      s = new Scanner(reader);
+      // We should get a match straight away.
+      String result = s.findWithinHorizon("1", 0);
+      assertEquals("1", result);
+      // The stream should not be consumed as there's already a match after the first read.
+      assertEquals(1, reader.timesRead);
+    }
+
     /**
      * @tests java.util.Scanner#findWithinHorizon(Pattern, int)
      */
-    public void test_findWithinHorizon_LPatternI(){
+    public void test_findWithinHorizon_LPatternI() {
 
         // This method searches through the input up to the specified search
         // horizon(exclusive).
@@ -4823,9 +4623,8 @@
 
         try {
             s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         assertEquals("345", mresult.group());
         assertEquals(2, mresult.start());
@@ -4853,24 +4652,21 @@
 
         try {
             s.findWithinHorizon((Pattern) null, -1);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
 
         try {
             s.findWithinHorizon(Pattern.compile("\\p{Digit}+"), -1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
+            fail();
+        } catch (IllegalArgumentException expected) {
         }
 
         s.close();
         try {
             s.findWithinHorizon((Pattern) null, -1);
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("test");
@@ -4878,22 +4674,19 @@
         assertEquals("test", result);
 
         s = new Scanner("aa\n\rb");
-        String patternStr = "^(a)$";
         result = s.findWithinHorizon(Pattern.compile("a"), 5);
         assertEquals("a", result);
         mresult = s.match();
         assertEquals(0, mresult.start());
         assertEquals(1, mresult.end());
 
-        result = s.findWithinHorizon(Pattern.compile(patternStr,
-                Pattern.MULTILINE), 5);
+        result = s.findWithinHorizon(Pattern.compile("^(a)$", Pattern.MULTILINE), 5);
         assertNull(result);
 
         try {
             mresult = s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("");
@@ -4916,9 +4709,8 @@
 
         try {
             mresult = s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         assertEquals(0, mresult.groupCount());
 
@@ -4936,18 +4728,16 @@
         s.close();
         try {
             s.findWithinHorizon(Pattern.compile("test"), 1);
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("word1 WorD2  ");
         s.close();
         try {
             s.findWithinHorizon(Pattern.compile("\\d+"), 10);
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("word1 WorD2 wOrd3 ");
@@ -4970,6 +4760,7 @@
         s = new Scanner(stringBuilder.toString());
         pattern = Pattern.compile("\\p{Lower}+");
         result = s.findWithinHorizon(pattern, 1026);
+        assertEquals(stringBuilder.toString().length(), result.length());
         assertEquals(stringBuilder.toString(), result);
 
         // Test the situation when input length is longer than buffer size and
@@ -5005,7 +4796,7 @@
         pattern = Pattern.compile("\\p{Lower}+");
         result = s.findWithinHorizon(pattern, 0);
         assertEquals(stringBuilder.toString(), result);
-        
+
         stringBuilder = new StringBuilder();
         for (int i = 0; i < 10240; i++) {
             stringBuilder.append('-');
@@ -5014,12 +4805,12 @@
         s = new Scanner(stringBuilder.toString());
         result = s.findWithinHorizon(Pattern.compile("aa"), 0);
         assertEquals("aa", result);
-        
+
         s = new Scanner("aaaa");
         result = s.findWithinHorizon(Pattern.compile("a*"), 0);
         assertEquals("aaaa", result);
     }
-    
+
     /**
      * @tests java.util.Scanner#findInLine(Pattern)
      */
@@ -5028,9 +4819,8 @@
         Scanner s = new Scanner("");
         try {
             s.findInLine((Pattern) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // Expected
+            fail();
+        } catch (NullPointerException expected) {
         }
         String result = s.findInLine(Pattern.compile("^"));
         assertEquals("", result);
@@ -5067,9 +4857,8 @@
         assertNull(result);
         try {
             matchResult = s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         assertEquals(0, matchResult.start());
         assertEquals(4, matchResult.end());
@@ -5096,9 +4885,8 @@
         s.close();
         try {
             s.findInLine((Pattern) null);
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("test1234\n1234 test");
@@ -5125,13 +4913,13 @@
         matchResult = s.match();
         assertEquals(14, matchResult.start());
         assertEquals(18, matchResult.end());
-        
+
         s = new Scanner("test\u0085\ntest");
         result = s.findInLine("est");
         assertEquals("est", result);
         result = s.findInLine("est");
         assertEquals("est", result);
-        
+
         s = new Scanner("test\ntest");
         result = s.findInLine("est");
         assertEquals("est", result);
@@ -5144,39 +4932,134 @@
         result = s.findInLine("est");
         // RI fails. It is a RI's bug.
         assertNull(result);
-        
+
         s = new Scanner( "   *\n");
         result = s.findInLine(Pattern.compile( "^\\s*(?:\\*(?=[^/]))"));
         assertEquals("   *", result);
     }
 
-    /**
-     * @tests java.util.Scanner#findInLine(String)
-     */
-    public void test_findInLine_LString() {
+    public void test_findInLine_LString_NPEs() {
         s = new Scanner("test");
         try {
             s.findInLine((String) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
-
         s.close();
         try {
             s.findInLine((String) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
         try {
             s.findInLine("test");
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // exptected
+            fail();
+        } catch (IllegalStateException expected) {
         }
     }
-    
+
+    public void test_findInLine_LString() {
+      Scanner s = new Scanner("");
+      String result = s.findInLine("^");
+      assertEquals("", result);
+      MatchResult matchResult = s.match();
+      assertEquals(0, matchResult.start());
+      assertEquals(0, matchResult.end());
+
+      result = s.findInLine("$");
+      assertEquals("", result);
+      matchResult = s.match();
+      assertEquals(0, matchResult.start());
+      assertEquals(0, matchResult.end());
+
+      // When we use the operation of findInLine(Pattern), the match region
+      // should not span the line separator.
+      s = new Scanner("aa\nb.b");
+      result = s.findInLine("a\nb*");
+      assertNull(result);
+
+      s = new Scanner("aa\nbb.b");
+      result = s.findInLine("\\.");
+      assertNull(result);
+
+      s = new Scanner("abcd1234test\n");
+      result = s.findInLine("\\p{Lower}+");
+      assertEquals("abcd", result);
+      matchResult = s.match();
+      assertEquals(0, matchResult.start());
+      assertEquals(4, matchResult.end());
+
+      result = s.findInLine("\\p{Digit}{5}");
+      assertNull(result);
+      try {
+        matchResult = s.match();
+        fail();
+      } catch (IllegalStateException expected) {
+      }
+      assertEquals(0, matchResult.start());
+      assertEquals(4, matchResult.end());
+
+      result = s.findInLine("\\p{Lower}+");
+      assertEquals("test", result);
+      matchResult = s.match();
+      assertEquals(8, matchResult.start());
+      assertEquals(12, matchResult.end());
+
+      char[] chars = new char[2048];
+      Arrays.fill(chars, 'a');
+      StringBuilder stringBuilder = new StringBuilder();
+      stringBuilder.append(chars);
+      stringBuilder.append("1234");
+      s = new Scanner(stringBuilder.toString());
+      result = s.findInLine("\\p{Digit}+");
+      assertEquals("1234", result);
+      matchResult = s.match();
+      assertEquals(2048, matchResult.start());
+      assertEquals(2052, matchResult.end());
+
+      s = new Scanner("test1234\n1234 test");
+      result = s.findInLine("test");
+      assertEquals("test", result);
+      matchResult = s.match();
+      assertEquals(0, matchResult.start());
+      assertEquals(4, matchResult.end());
+
+      int number = s.nextInt();
+      assertEquals(1234, number);
+      matchResult = s.match();
+      assertEquals(4, matchResult.start());
+      assertEquals(8, matchResult.end());
+
+      result = s.next();
+      assertEquals("1234", result);
+      matchResult = s.match();
+      assertEquals(9, matchResult.start());
+      assertEquals(13, matchResult.end());
+
+      result = s.findInLine("test");
+      assertEquals("test", result);
+      matchResult = s.match();
+      assertEquals(14, matchResult.start());
+      assertEquals(18, matchResult.end());
+
+      s = new Scanner("test\u0085\ntest");
+      result = s.findInLine("est");
+      assertEquals("est", result);
+      result = s.findInLine("est");
+      assertEquals("est", result);
+
+      s = new Scanner("test\ntest");
+      result = s.findInLine("est");
+      assertEquals("est", result);
+      result = s.findInLine("est");
+      assertEquals("est", result);
+
+      s = new Scanner("test\n123\ntest");
+      result = s.findInLine("est");
+      assertEquals("est", result);
+      result = s.findInLine("est");
+    }
+
     /**
      * @tests java.util.Scanner#skip(Pattern)
      */
@@ -5184,25 +5067,22 @@
         s = new Scanner("test");
         try {
             s.skip((String) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
-        
+
         // If pattern does not match, NoSuchElementException will be thrown out.
         s = new Scanner("1234");
         try {
             s.skip(Pattern.compile("\\p{Lower}"));
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
         // Then, no matchResult will be thrown out.
         try {
             s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s.skip(Pattern.compile("\\p{Digit}"));
@@ -5218,24 +5098,21 @@
         s.close();
         try {
             s.skip(Pattern.compile("test"));
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         MockStringReader2Read reader = new MockStringReader2Read("test");
         s = new Scanner(reader);
         try {
             s.skip(Pattern.compile("\\p{Digit}{4}"));
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
         try {
             s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
         s.skip(Pattern.compile("\\p{Digit}{3}\\p{Lower}"));
         matchResult = s.match();
@@ -5245,11 +5122,10 @@
         s.close();
         try {
             s.skip((Pattern) null);
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         StringBuilder stringBuilder = new StringBuilder();
         char [] chars = new char[1024];
         Arrays.fill(chars, 'a');
@@ -5260,7 +5136,7 @@
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1025, matchResult.end());
-        
+
         // Large amount of input may be cached
         chars = new char[102400];
         Arrays.fill(chars, 'a');
@@ -5271,7 +5147,7 @@
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(102400, matchResult.end());
-        
+
         // skip something without risking a NoSuchElementException
         s.skip(Pattern.compile("[ \t]*"));
         matchResult = s.match();
@@ -5286,12 +5162,11 @@
         s = new Scanner("test");
         try {
             s.skip((String) null);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+            fail();
+        } catch (NullPointerException expected) {
         }
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextDouble()
@@ -5305,9 +5180,8 @@
         assertEquals(0.123, s.nextDouble());
         try {
             s.nextDouble();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("+123.4 -456.7 123,456.789 0.1\u06623,4");
@@ -5317,9 +5191,8 @@
         assertEquals(123456.789, s.nextDouble());
         try {
             s.nextDouble();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // Scientific notation
@@ -5333,7 +5206,7 @@
         assertEquals(Double.NaN, s.nextDouble());
         assertEquals(Double.POSITIVE_INFINITY, s.nextDouble());
         assertEquals(Double.NEGATIVE_INFINITY, s.nextDouble());
-        
+
         //The following test case fails on RI
         s=new Scanner("\u221e");
         s.useLocale(Locale.ENGLISH);
@@ -5342,7 +5215,7 @@
         String str=String.valueOf(Double.MAX_VALUE*2);
         s=new Scanner(str);
         assertEquals(Double.POSITIVE_INFINITY,s.nextDouble());
-        
+
         /*
          * Different locale can only recognize corresponding locale sensitive
          * string. ',' is used in many locales as group separator.
@@ -5369,7 +5242,7 @@
         s.useLocale(Locale.ENGLISH);
         assertEquals(-123.4, s.nextDouble());
     }
-    
+
     /**
      * @throws IOException
      * @tests java.util.Scanner#nextBigDecimal()
@@ -5383,9 +5256,8 @@
         assertEquals(new BigDecimal("0.123"), s.nextBigDecimal());
         try {
             s.nextBigDecimal();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // Expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
 
         s = new Scanner("+123.4 -456.7 123,456.789 0.1\u06623,4");
@@ -5395,9 +5267,8 @@
         assertEquals(new BigDecimal("123456.789"), s.nextBigDecimal());
         try {
             s.nextBigDecimal();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         // Scientific notation
@@ -5410,9 +5281,8 @@
         s = new Scanner("NaN");
         try {
             s.nextBigDecimal();
-            fail("Should throw InputMismatchException");
-        } catch (InputMismatchException e) {
-            // Expected
+            fail();
+        } catch (InputMismatchException expected) {
         }
 
         /*
@@ -5456,11 +5326,10 @@
         s.close();
         try {
             s.nextLine();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         s = new Scanner("test\r\ntest");
         String result = s.nextLine();
         assertEquals("test", result);
@@ -5474,33 +5343,31 @@
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1, matchResult.end());
-        
+
         s = new Scanner("\u2028");
         result = s.nextLine();
         assertEquals("", result);
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1, matchResult.end());
-        
+
         s = new Scanner("\u2029");
         result = s.nextLine();
         assertEquals("", result);
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1, matchResult.end());
-        
+
         s = new Scanner("");
         try {
             result = s.nextLine();
-            fail("Should throw NoSuchElementException");
-        } catch (NoSuchElementException e) {
-            // expected
+            fail();
+        } catch (NoSuchElementException expected) {
         }
         try {
             s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
 
         s = new Scanner("Ttest");
@@ -5570,14 +5437,14 @@
         matchResult = s.match();
         assertEquals(1, matchResult.start());
         assertEquals(2, matchResult.end());
-        
+
         s = new Scanner("123 test\n   ");
         int value = s.nextInt();
         assertEquals(123, value);
-        
+
         result = s.nextLine();
         assertEquals(" test", result);
-        
+
         s = new Scanner("test\n ");
         result = s.nextLine();
         assertEquals("test", result);
@@ -5597,9 +5464,9 @@
         // We expect read() to be called only once, otherwise we see the problem
         // when reading from System.in described in Harmony-4774
         assertEquals(1, cr.counter);
-        assertEquals("hello", result);        
+        assertEquals("hello", result);
     }
-    
+
     /**
      * @tests java.util.Scanner#hasNextLine()
      */
@@ -5608,11 +5475,10 @@
         s.close();
         try {
             s.hasNextLine();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
+            fail();
+        } catch (IllegalStateException expected) {
         }
-        
+
         s = new Scanner("test\r\ntest");
         boolean result = s.hasNextLine();
         assertTrue(result);
@@ -5626,21 +5492,21 @@
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1, matchResult.end());
-        
+
         s = new Scanner("\u2028");
         result = s.hasNextLine();
         assertTrue(result);
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1, matchResult.end());
-        
+
         s = new Scanner("\u2029");
         result = s.hasNextLine();
         assertTrue(result);
         matchResult = s.match();
         assertEquals(0, matchResult.start());
         assertEquals(1, matchResult.end());
-        
+
         s = new Scanner("test\n");
         assertTrue(s.hasNextLine());
         matchResult = s.match();
@@ -5681,18 +5547,19 @@
         Thread thread = new Thread(new Runnable() {
             public void run() {
                 while (scanner.hasNextLine()) {
-                    result.add(scanner.nextLine());
+                    String line = scanner.nextLine();
+                    result.add(line);
                 }
             }
         });
         thread.start();
         for (int index = 0; index < 5; index++) {
-            pos.write(("line" + index + "\n").getBytes());
+            String line = "line" + index + "\n";
+            pos.write(line.getBytes());
             pos.flush();
             try {
                 Thread.sleep(1000);
-            } catch (InterruptedException e) {
-                // Ignored
+            } catch (InterruptedException ignored) {
             }
             assertEquals(index + 1, result.size());
         }
@@ -5700,8 +5567,7 @@
         pos.close();
         try {
             thread.join(1000);
-        } catch (InterruptedException e) {
-            // Ignored
+        } catch (InterruptedException ignored) {
         }
         assertFalse(scanner.hasNextLine());
     }
@@ -5724,18 +5590,45 @@
 
         try {
             serverSocket.close();
-        } catch (Exception e) {
-
+        } catch (Exception ignored) {
         }
         try {
             client.close();
-        } catch (Exception e) {
-            // do nothing
+        } catch (Exception ignored) {
         }
         try {
             server.close();
-        } catch (Exception e) {
-            // do nothing
+        } catch (Exception ignored) {
+        }
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=57050
+    public void testPerformance() throws Exception {
+        int count = 100000;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(baos));
+        for (int i = 0; i < count; ++i) {
+            out.write(Integer.toString(123) + " ");
+        }
+        out.close();
+
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        bais.mark(-1);
+
+        Scanner s = new Scanner(new BufferedReader(new InputStreamReader(bais)));
+        for (int i = 0; i < count; ++i) {
+            if (s.nextInt() != 123) {
+                fail();
+            }
+        }
+
+        bais.reset();
+        s = new Scanner(new BufferedReader(new InputStreamReader(bais)));
+        for (int i = 0; i < count; ++i) {
+            if (s.nextFloat() != 123.0) {
+                fail();
+            }
         }
     }
 }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java
index 6f59190..a85576c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/ASCIICharsetEncoderTest.java
@@ -4,9 +4,9 @@
  * 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.
@@ -33,7 +33,7 @@
 	// charset for ascii
 	private static final Charset cs = Charset.forName("ascii");
     private static final CharsetEncoder encoder = cs.newEncoder();
-    private static final int MAXCODEPOINT = 0x7F; 
+    private static final int MAXCODEPOINT = 0x7F;
 	/*
 	 * @see CharsetEncoderTest#setUp()
 	 */
@@ -96,21 +96,21 @@
 
     public void testEncodeMapping() throws CharacterCodingException {
         encoder.reset();
-        
+
         for (int i =0; i <= MAXCODEPOINT; i++) {
             char[] chars = Character.toChars(i);
             CharBuffer cb = CharBuffer.wrap(chars);
             ByteBuffer bb = encoder.encode(cb);
             assertEquals(i, bb.get(0));
         }
-        
+
         CharBuffer cb = CharBuffer.wrap("\u0080");
         try {
             encoder.encode(cb);
         } catch (UnmappableCharacterException e) {
             //expected
         }
-        
+
         cb = CharBuffer.wrap("\ud800");
         try {
             encoder.encode(cb);
@@ -128,11 +128,11 @@
             //expected
         }
     }
-    
+
     public void testInternalState() {
         CharBuffer in = CharBuffer.wrap("A");
         ByteBuffer out = ByteBuffer.allocate(0x10);
-        
+
         //normal encoding process
         encoder.reset();
         encoder.encode(in, out, false);
@@ -140,13 +140,13 @@
         encoder.encode(in, out, true);
         encoder.flush(out);
     }
-    
+
     //reset could be called at any time
     public void testInternalState_Reset() {
         CharsetEncoder newEncoder = cs.newEncoder();
         //Init - > reset
         newEncoder.reset();
-        
+
         //reset - > reset
         newEncoder.reset();
 
@@ -174,7 +174,7 @@
             newEncoder.reset();
         }
     }
-    
+
     public void testInternalState_Encoding() {
         CharsetEncoder newEncoder = cs.newEncoder();
         //Init - > encoding
@@ -183,27 +183,27 @@
             ByteBuffer out = ByteBuffer.allocate(0x10);
             newEncoder.encode(in, out, false);
         }
-        
+
         //reset - > encoding
         {
             CharBuffer in = CharBuffer.wrap("A");
             ByteBuffer out = ByteBuffer.allocate(0x10);
-            newEncoder.reset();            
+            newEncoder.reset();
             newEncoder.encode(in, out, false);
         }
         //reset - > encoding - > encoding
         {
-            newEncoder.reset();            
+            newEncoder.reset();
             CharBuffer in = CharBuffer.wrap("A");
             ByteBuffer out = ByteBuffer.allocate(0x10);
             newEncoder.encode(in, out, false);
             in = CharBuffer.wrap("BC");
             newEncoder.encode(in, out, false);
         }
-        
+
         //encoding_end - > encoding
         {
-            newEncoder.reset();            
+            newEncoder.reset();
             CharBuffer in = CharBuffer.wrap("A");
             ByteBuffer out = ByteBuffer.allocate(0x10);
             newEncoder.encode(in, out, true);
@@ -217,7 +217,7 @@
         }
         //flushed - > encoding
         {
-            newEncoder.reset();            
+            newEncoder.reset();
             CharBuffer in = CharBuffer.wrap("A");
             ByteBuffer out = ByteBuffer.allocate(0x10);
             newEncoder.encode(in, out, true);
@@ -231,7 +231,7 @@
             }
         }
     }
-    
+
     public void testInternalState_Encoding_END() {
         CharsetEncoder newEncoder = cs.newEncoder();
 
@@ -241,7 +241,7 @@
             ByteBuffer out = ByteBuffer.allocate(0x10);
             newEncoder.encode(in, out, true);
         }
-        
+
         //Reset -> encoding_end
         {
             CharBuffer in = CharBuffer.wrap("A");
@@ -259,7 +259,7 @@
             in = CharBuffer.wrap("BC");
             newEncoder.encode(in, out, true);
         }
-        
+
         //Reset -> encoding_end
         {
             newEncoder.reset();
@@ -269,7 +269,7 @@
             in = CharBuffer.wrap("BC");
             newEncoder.encode(in, out, true);
         }
-        
+
         //Flushed -> encoding_end
         {
             newEncoder.reset();
@@ -286,10 +286,10 @@
             }
         }
     }
-    
+
     public void testInternalState_Flushed() {
         CharsetEncoder newEncoder = cs.newEncoder();
-        
+
         // init -> flushed
 		{
 			ByteBuffer out = ByteBuffer.allocate(0x10);
@@ -316,7 +316,7 @@
                 //expected
             }
         }
-        
+
         //encoding - > flushed
         {
             newEncoder.reset();
@@ -331,7 +331,7 @@
                 // expected
             }
         }
-        
+
         //encoding_end -> flushed
         {
             newEncoder.reset();
@@ -340,7 +340,7 @@
             newEncoder.encode(in, out, true);
             newEncoder.flush(out);
         }
-        
+
         //flushd - > flushed
         {
             newEncoder.reset();
@@ -348,15 +348,10 @@
             ByteBuffer out = ByteBuffer.allocate(0x10);
             newEncoder.encode(in, out, true);
             newEncoder.flush(out);
-            try {
-                newEncoder.flush(out);
-                fail("Should throw IllegalStateException");
-            } catch (IllegalStateException e) {
-                // expected
-            }
+            newEncoder.flush(out);
         }
     }
-    
+
     public void testInternalState_Encode() throws CharacterCodingException {
         CharsetEncoder newEncoder = cs.newEncoder();
         //Init - > encode
@@ -364,14 +359,14 @@
             CharBuffer in = CharBuffer.wrap("A");
             newEncoder.encode(in);
         }
-        
+
         //Reset - > encode
         {
             newEncoder.reset();
             CharBuffer in = CharBuffer.wrap("A");
             newEncoder.encode(in);
         }
-        
+
         //Encoding -> encode
         {
             newEncoder.reset();
@@ -381,7 +376,7 @@
             in = CharBuffer.wrap("BC");
             newEncoder.encode(in);
         }
-        
+
         //Encoding_end -> encode
         {
             newEncoder.reset();
@@ -391,7 +386,7 @@
             in = CharBuffer.wrap("BC");
             newEncoder.encode(in);
         }
-        
+
         //Flushed -> reset
         {
             newEncoder.reset();
@@ -403,17 +398,17 @@
             out = newEncoder.encode(in);
         }
     }
-    
+
     public void testInternalState_from_Encode() throws CharacterCodingException {
         CharsetEncoder newEncoder = cs.newEncoder();
-        
+
         //Encode -> Reset
         {
             CharBuffer in = CharBuffer.wrap("A");
             newEncoder.encode(in);
             newEncoder.reset();
         }
-        
+
         // Encode -> encoding
         {
             CharBuffer in = CharBuffer.wrap("A");
@@ -426,12 +421,13 @@
                 // expected
             }
         }
-        
+
         //Encode -> Encoding_end
         {
             CharBuffer in = CharBuffer.wrap("A");
-            newEncoder.encode(in);
             ByteBuffer out = ByteBuffer.allocate(0x10);
+            newEncoder.reset();
+            newEncoder.encode(in, out, false);
             newEncoder.encode(in, out, true);
         }
 
@@ -441,7 +437,7 @@
             ByteBuffer out = newEncoder.encode(in);
             newEncoder.flush(out);
         }
-        
+
         //Encode - > encode
         {
             CharBuffer in = CharBuffer.wrap("A");
diff --git a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java
index 4ed4ab9..1a7b984 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetDecoderTest.java
@@ -4,9 +4,9 @@
  * 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.
@@ -62,7 +62,7 @@
 			return null;
 		}
 	}
- 
+
 	/**
 	 * @tests java.nio.charset.CharsetDecoder#decode(java.nio.ByteBuffer)
 	 */
@@ -86,7 +86,7 @@
 //
 //		charbuf = Charset.forName("UTF-16LE").decode(buf);
 //		assertEquals("Assert 2: charset UTF16LE", 0, charbuf.length());
-		
+
 		// Regression for HARMONY-99
 		CharsetDecoder decoder2 = Charset.forName("UTF-16").newDecoder();
 		decoder2.onMalformedInput(CodingErrorAction.REPORT);
@@ -97,9 +97,9 @@
 			fail("Assert 3: MalformedInputException should have thrown");
 		} catch (MalformedInputException e) {
 			//expected
-		} 
+		}
 	}
-	
+
     /*
      * Test malfunction decode(ByteBuffer)
      */
@@ -124,7 +124,7 @@
 			// expected
 		}
 	}
-    
+
 	/*
 	 * Mock charset class with malfunction decode & encode.
 	 */
@@ -173,8 +173,8 @@
 		protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
 			throw new BufferOverflowException();
 		}
-	} 
-	
+	}
+
 	/*
 	 * Test the method decode(ByteBuffer) .
 	 */
@@ -249,16 +249,16 @@
 
         CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
         decoder.onMalformedInput(CodingErrorAction.REPORT);
+        decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
 
         /*
          * When bytebuffer has a backing array...
          */
         for (byte[] bytes : invalidSequences) {
             try {
-                decoder.decode(ByteBuffer.wrap(bytes));
-                fail("No exception thrown on " + Arrays.toString(bytes));
-            } catch (MalformedInputException e) {
-                // expected
+                CharBuffer cb = decoder.decode(ByteBuffer.wrap(bytes));
+                fail("No exception thrown on " + Arrays.toString(bytes) + " '" + cb + "'");
+            } catch (MalformedInputException expected) {
             }
         }
 
@@ -269,10 +269,9 @@
             try {
                 ByteBuffer bb = ByteBuffer.allocateDirect(8);
                 bb.put(bytes).flip();
-                decoder.decode(bb);
-                fail("No exception thrown on " + Arrays.toString(bytes));
-            } catch (MalformedInputException e) {
-                // expected
+                CharBuffer cb = decoder.decode(bb);
+                fail("No exception thrown on " + Arrays.toString(bytes) + " '" + cb + "'");
+            } catch (MalformedInputException expected) {
             }
         }
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java
index dd514da..c3f1a8d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetEncoderTest.java
@@ -157,26 +157,36 @@
     public void test_EncodeLjava_nio_CharBufferLjava_nio_ByteBufferB() throws Exception {
         Charset utf8 = Charset.forName("utf-8");
         CharsetEncoder encoder = utf8.newEncoder();
-        CharBuffer in1 = CharBuffer.wrap("\ud800");
-        CharBuffer in2 = CharBuffer.wrap("\udc00");
-        ByteBuffer out = ByteBuffer.allocate(4);
+        CharBuffer char1 = CharBuffer.wrap("\ud800");
+        CharBuffer char2 = CharBuffer.wrap("\udc00");
+        ByteBuffer bytes = ByteBuffer.allocate(4);
         encoder.reset();
 
         // If we supply just the high surrogate...
-        CoderResult result = encoder.encode(in1, out, false);
+        CoderResult result = encoder.encode(char1, bytes, false);
         // ...we're not done...
         assertTrue(result.isUnderflow());
-        assertEquals(4, out.remaining());
+        assertEquals(4, bytes.remaining());
         // ...but if we then supply the low surrogate...
-        result = encoder.encode(in2, out, true);
+        result = encoder.encode(char2, bytes, true);
+        assertTrue(result.isUnderflow());
         // ...we're done. Note that the RI loses its state in
         // between the two characters, so it can't do this.
-        assertEquals(0, out.remaining());
+        assertEquals(0, bytes.remaining());
+
+        // Did we get the UTF-8 for U+10000?
+        assertEquals(4, bytes.limit());
+        assertEquals((byte) 0xf0, bytes.get(0));
+        assertEquals((byte) 0x90, bytes.get(1));
+        assertEquals((byte) 0x80, bytes.get(2));
+        assertEquals((byte) 0x80, bytes.get(3));
 
         // See what we got in the output buffer by decoding and checking that we
         // get back the same surrogate pair.
-        out.flip();
-        CharBuffer chars = utf8.newDecoder().decode(out);
+        bytes.flip();
+        CharBuffer chars = utf8.newDecoder().decode(bytes);
+        assertEquals(0, bytes.remaining());
+        assertEquals(2, chars.limit());
         assertEquals(0xd800, chars.get(0));
         assertEquals(0xdc00, chars.get(1));
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetTest.java b/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetTest.java
deleted file mode 100644
index 134bea6..0000000
--- a/harmony-tests/src/test/java/org/apache/harmony/nio_char/tests/java/nio/charset/CharsetTest.java
+++ /dev/null
@@ -1,245 +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 org.apache.harmony.nio_char.tests.java.nio.charset;
-
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.IllegalCharsetNameException;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.Set;
-
-import junit.framework.TestCase;
-
-public class CharsetTest extends TestCase {
-
-	// Will contain names of charsets registered with IANA
-	Set knownRegisteredCharsets = new HashSet();
-
-	// Will contain names of charsets not known to be registered with IANA
-	Set unknownRegisteredCharsets = new HashSet();
-
-	/**
-	 * JUnit set-up method
-	 */
-	public void setUp() {
-		// Populate the known charset vars
-		Set names = Charset.availableCharsets().keySet();
-		for (Iterator nameItr = names.iterator(); nameItr.hasNext();) {
-			String name = (String) nameItr.next();
-			if (name.toLowerCase(Locale.ROOT).startsWith("x-"))
-				unknownRegisteredCharsets.add(name);
-			else
-				knownRegisteredCharsets.add(name);
-		}
-	}
-
-	/**
-	 * @tests java.nio.charset.Charset#isRegistered()
-	 */
-	public void test_isRegistered() {
-		// Regression for HARMONY-45
-		for (Iterator nameItr = knownRegisteredCharsets.iterator(); nameItr.hasNext();) {
-			String name = (String) nameItr.next();
-			assertTrue("Assert 0: isRegistered() failed for " + name,
-					Charset.forName(name).isRegistered());
-		}
-		for (Iterator nameItr = unknownRegisteredCharsets.iterator(); nameItr.hasNext();) {
-			String name = (String) nameItr.next();
-			assertFalse("Assert 0: isRegistered() failed for " + name,
-					Charset.forName(name).isRegistered());
-		}
-	}
-
-	/**
-	 * @tests java.nio.charset.Charset#isSupported(String)
-	 */
-	public void testIsSupported_EmptyString() {
-		// Regression for HARMONY-113
-		try {
-			Charset.isSupported("");
-			fail("Assert 0: Should throw IllegalCharsetNameException");
-		} catch (IllegalCharsetNameException e) {
-			// Expected
-		}
-	}
-
-    public void test_defaultCharset() {
-        assertEquals("UTF-8", Charset.defaultCharset().name());
-    }
-
-	public void test_forNameLjava_lang_String() {
-		/*
-		 * invoke forName two times with the same canonical name, it
-		 * should return the same reference.
-		 */
-		Charset cs1 = Charset.forName("UTF-8");
-		Charset cs2 = Charset.forName("UTF-8");
-		assertSame(cs1, cs2);
-
-		/*
-		 * test forName: invoke forName two times for the same Charset using
-		 * canonical name and alias, it should return the same reference.
-		 */
-		Charset cs3 = Charset.forName("ASCII");
-		Charset cs4 = Charset.forName("US-ASCII");
-		assertSame(cs3, cs4);
-	}
-
-	/*
-     * test cached decoder
-     */
-//    public void test_DecodeLjava_nio_ByteBuffer() throws Exception{
-//            MockCharsetForDecoder cs1 = new MockCharsetForDecoder("CachedCharset",null);
-//            MockCharsetForDecoder cs2 = new MockCharsetForDecoder("CachedCharset",null);
-//            ByteBuffer in = ByteBuffer.wrap(new byte[]{0x00});
-//            cs1.decode(in);
-//            in.flip();
-//            cs2.decode(in);
-//            in.flip();
-//    }
-    /*
-     * Mock Charset for cached decoder test
-     */
-    static class MockCharsetForDecoder extends Charset{
-
-            public MockCharsetForDecoder(String canonicalName, String[] aliases){
-                    super(canonicalName, aliases);
-            }
-
-            public boolean contains(Charset charset) {
-                    return false;
-            }
-
-            public CharsetEncoder newEncoder() {
-                    return null;
-            }
-
-            public CharsetDecoder newDecoder() {
-                    return new MockCachedDecoder(this);
-            }
-
-
-    }
-    /*
-     * Mock decoder. Only one caller is permitted.
-     */
-    static class MockCachedDecoder extends CharsetDecoder {
-            static MockCachedDecoder caller = null;
-
-            public MockCachedDecoder(Charset cs) {
-                    super(cs, 1, 10);
-            }
-
-            /*
-             * Only one caller is permitted.
-             * If there's another caller, throw RuntimeException.
-             */
-            protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
-                    if(null == caller){
-                            caller = this;
-                    }else{
-                            if(caller != this){
-                                // Another instance
-                            	fail("should use the same instance");
-                            }
-                    }
-                    return CoderResult.UNDERFLOW;
-            }
-    }
-
-    /*
-     * test cached encoder
-     */
-//    public void test_EncodeLjava_nio_CharBuffer() throws Exception {
-//            MockCharsetForEncoder cs1 = new MockCharsetForEncoder("CachedCharset", null);
-//            MockCharsetForEncoder cs2 = new MockCharsetForEncoder("CachedCharset", null);
-//            CharBuffer in = CharBuffer.wrap("A");
-//            cs1.encode(in);
-//            in.flip();
-//            cs2.encode(in);
-//    }
-
-    /*
-     * Mock Charset for cached encoder test
-     */
-    static class MockCharsetForEncoder extends Charset {
-
-            public MockCharsetForEncoder(String canonicalName, String[] aliases) {
-                    super(canonicalName, aliases);
-            }
-
-            public boolean contains(Charset charset) {
-                    return false;
-            }
-
-            public CharsetDecoder newDecoder() {
-                    return new MockDecoderForEncoder(this);
-            }
-
-            public CharsetEncoder newEncoder() {
-                    return new MockCachedEncoder(this);
-            }
-    }
-
-    /*
-     * Mock encoder. Only one caller is permitted.
-     */
-    static class MockCachedEncoder extends CharsetEncoder {
-            static MockCachedEncoder caller = null;
-
-            public MockCachedEncoder(Charset cs) {
-                    super(cs, 1, 10);
-            }
-
-            /*
-             * Only one caller is permitted.
-             */
-            protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
-                    if (null == caller) {
-                            caller = this;
-                    } else {
-                            if (caller != this) {
-                                    // Another instance
-                                    fail("should use the same instance");
-                            }
-                    }
-                    return CoderResult.UNDERFLOW;
-            }
-    }
-
-    /*
-     * Mock decoder for MockCachedEncoder.
-     */
-    static class MockDecoderForEncoder extends CharsetDecoder {
-            public MockDecoderForEncoder(Charset cs) {
-                    super(cs, 1, 10);
-            }
-
-            protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
-                    in.position(in.limit());
-                    return CoderResult.UNDERFLOW;
-            }
-    }
-
-}
diff --git a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java
index 9cd59ea..7fbe9e7 100644
--- a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java
+++ b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetDecoderTest.java
@@ -4,9 +4,9 @@
  * 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.
@@ -600,6 +600,15 @@
     public void testFlushIllegalState() throws CharacterCodingException {
         ByteBuffer in = ByteBuffer.wrap(new byte[] { 98, 98 });
         CharBuffer out = CharBuffer.allocate(5);
+
+        // Illegal state: after reset.
+        decoder.reset();
+        try {
+            decoder.flush(out);
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+
         // Normal case: after decode with endOfInput is true
         decoder.reset();
         decoder.decode(in, out, true);
@@ -607,23 +616,19 @@
         CoderResult result = decoder.flush(out);
         assertSame(result, CoderResult.UNDERFLOW);
 
-        // Illegal state: flush twice
-        try {
-            decoder.flush(out);
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-        }
+        // Good state: flush twice
+        decoder.flush(out);
 
         // Illegal state: flush after decode with endOfInput is false
         decoder.reset();
         decoder.decode(in, out, false);
         try {
             decoder.flush(out);
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
+            fail();
+        } catch (IllegalStateException expected) {
         }
     }
-    
+
     // test illegal states for decode facade
     public void testDecodeFacadeIllegalState() throws CharacterCodingException {
         // decode facade can be execute in anywhere
diff --git a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java
index ef219be..b63f4d9 100644
--- a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java
+++ b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetEncoderTest.java
@@ -278,34 +278,36 @@
 		assertSame(encoder, encoder.reset());
 	}
 
-	public void testFlushIllegalState() throws CharacterCodingException {
-		CharBuffer in = CharBuffer.wrap("aaa");
-		ByteBuffer out = ByteBuffer.allocate(5);
+  public void testFlushIllegalState() throws CharacterCodingException {
+    CharBuffer in = CharBuffer.wrap("aaa");
+    ByteBuffer out = ByteBuffer.allocate(5);
 
-		// Normal case: after encode with endOfInput is true
-		assertSame(encoder, encoder.reset());
-		encoder.encode(in, out, true);
-		out.rewind();
-		CoderResult result = encoder.flush(out);
+    // Illegal state: after reset.
+    encoder.reset();
+    try {
+      encoder.flush(out);
+      fail();
+    } catch (IllegalStateException expected) {
+    }
 
-		// Illegal state: flush twice
-		try {
-			encoder.flush(out);
-			fail("should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-		    // Expected
-		}
+    // Normal case: after encode with endOfInput is true
+    assertSame(encoder, encoder.reset());
+    encoder.encode(in, out, true);
+    out.rewind();
+    CoderResult result = encoder.flush(out);
 
-		// Illegal state: flush after encode with endOfInput is false
-		assertSame(encoder, encoder.reset());
-		encoder.encode(in, out, false);
-		try {
-			encoder.flush(out);
-			fail("should throw IllegalStateException");
-		} catch (IllegalStateException e) {
-		    // Expected
-		}
-	}
+    // Good state: flush twice
+    encoder.flush(out);
+
+    // Illegal state: flush after encode with endOfInput is false
+    assertSame(encoder, encoder.reset());
+    encoder.encode(in, out, false);
+    try {
+      encoder.flush(out);
+      fail();
+    } catch (IllegalStateException expected) {
+    }
+  }
 
 	public void testFlushAfterConstructing() {
 		ByteBuffer out = ByteBuffer.allocate(5);
diff --git a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java
deleted file mode 100644
index b38728a..0000000
--- a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java
+++ /dev/null
@@ -1,406 +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.api.java.nio.charset;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import java.nio.charset.UnsupportedCharsetException;
-import java.nio.charset.spi.CharsetProvider;
-import java.util.Iterator;
-import java.util.Vector;
-
-import junit.framework.TestCase;
-import tests.api.java.nio.charset.CharsetTest.MockCharset;
-
-/**
- * Test charset providers managed by Charset.
- */
-public class CharsetProviderTest extends TestCase {
-
-	static String CONFIG_FILE1 = null;
-
-
-	static MockCharset charset1 = new MockCharset("mockCharset00",
-			new String[] { "mockCharset01", "mockCharset02" });
-
-	static MockCharset charset2 = new MockCharset("mockCharset10",
-			new String[] { "mockCharset11", "mockCharset12" });
-
-	/**
-	 * @param arg0
-	 */
-	public CharsetProviderTest(String arg0) {
-		super(arg0);
-                CONFIG_FILE1 = System.getProperty("user.dir") + "/resources";
-
-		String sep = System.getProperty("file.separator");
-
-		if (!CONFIG_FILE1.endsWith(sep)) {
-			CONFIG_FILE1 += sep;
-		}
-		CONFIG_FILE1 += "META-INF" + sep + "services" + sep
-				+ "java.nio.charset.spi.CharsetProvider";
-	}
-
-	/*
-	 * Write the string to the config file.
-	 */
-	private void setupFile(String path, String content) throws Exception {
-		String sep = System.getProperty("file.separator");
-		int sepIndex = path.lastIndexOf(sep);
-		File f = new File(path.substring(0, sepIndex));
-		f.mkdirs();
-
-		FileOutputStream fos = new FileOutputStream(path);
-		OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
-		try {
-			writer.write(content);
-		} finally {
-			writer.close();
-		}
-	}
-
-	/*
-	 * Write the string to the config file.
-	 */
-	private void cleanupFile(String path) throws Exception {
-		File f = new File(path);
-		f.delete();
-	}
-
-	/*
-	 * Test the method isSupported(String) with charset supported by some
-	 * providers (multiple).
-	 */
-	public void testIsSupported_And_ForName_NormalProvider() throws Exception {
-		try {
-			assertFalse(Charset.isSupported("mockCharset10"));
-			assertFalse(Charset.isSupported("mockCharset11"));
-			assertFalse(Charset.isSupported("mockCharset12"));
-            try {
-                Charset.forName("mockCharset10");
-                fail("Should throw UnsupportedCharsetException!");
-            } catch (UnsupportedCharsetException e) {
-                // expected
-            }
-            try {
-                Charset.forName("mockCharset11");
-                fail("Should throw UnsupportedCharsetException!");
-            } catch (UnsupportedCharsetException e) {
-                // expected
-            }
-            try {
-                Charset.forName("mockCharset12");
-                fail("Should throw UnsupportedCharsetException!");
-            } catch (UnsupportedCharsetException e) {
-                // expected
-            }
-
-			StringBuffer sb = new StringBuffer();
-			sb.append("#comment\r");
-			sb.append("\n");
-			sb.append("\r\n");
-			sb
-					.append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t\n\r");
-			sb
-					.append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			sb = new StringBuffer();
-			sb.append(" #comment\r");
-			sb.append("\n");
-			sb.append("\r\n");
-			sb
-					.append(" \ttests.api.java.nio.charset.CharsetProviderTest$MockCharsetProvider \t\n\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			assertTrue(Charset.isSupported("mockCharset10"));
-			// ignore case problem in mock, intended
-			assertTrue(Charset.isSupported("MockCharset11"));
-			assertTrue(Charset.isSupported("MockCharset12"));
-			assertTrue(Charset.isSupported("MOCKCharset10"));
-			// intended case problem in mock
-			assertTrue(Charset.isSupported("MOCKCharset11"));
-			assertTrue(Charset.isSupported("MOCKCharset12"));
-
-            assertTrue(Charset.forName("mockCharset10") instanceof MockCharset);
-            assertTrue(Charset.forName("mockCharset11") instanceof MockCharset);
-            assertTrue(Charset.forName("mockCharset12") instanceof MockCharset);
-
-            assertTrue(Charset.forName("mockCharset10") == charset2);
-            // intended case problem in mock
-            Charset.forName("mockCharset11");
-            assertTrue(Charset.forName("mockCharset12") == charset2);
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method isSupported(String) when the configuration file contains
-	 * a non-existing class name.
-	 */
-	public void testIsSupported_NonExistingClass() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("impossible\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.isSupported("impossible");
-			fail("Should throw Error!");
-		} catch (Error e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method isSupported(String) when the configuration file contains
-	 * a non-CharsetProvider class name.
-	 */
-	public void testIsSupported_NotCharsetProviderClass() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("java.lang.String\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.isSupported("impossible");
-			fail("Should throw ClassCastException!");
-		} catch (ClassCastException e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method forName(String) when the charset provider supports a
-	 * built-in charset.
-	 */
-	public void testForName_DuplicateWithBuiltInCharset() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb
-					.append("tests.api.java.nio.charset.CharsetProviderTest$MockCharsetProviderACSII\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			assertFalse(Charset.forName("us-ascii") instanceof MockCharset);
-			assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset);
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method forName(String) when the configuration file contains a
-	 * non-existing class name.
-	 */
-	public void testForName_NonExistingClass() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("impossible\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.forName("impossible");
-			fail("Should throw Error!");
-		} catch (Error e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method forName(String) when the configuration file contains a
-	 * non-CharsetProvider class name.
-	 */
-	public void testForName_NotCharsetProviderClass() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("java.lang.String\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.forName("impossible");
-			fail("Should throw ClassCastException!");
-		} catch (ClassCastException e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method availableCharsets() with charset supported by some
-	 * providers (multiple).
-	 */
-	public void testAvailableCharsets_NormalProvider() throws Exception {
-		try {
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset10"));
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset11"));
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset12"));
-
-			StringBuffer sb = new StringBuffer();
-			sb.append("#comment\r");
-			sb.append("\n");
-			sb.append("\r\n");
-			sb
-					.append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t\n\r");
-			sb
-					.append(" \ttests.api.java.nio.charset.CharsetTest$MockCharsetProvider \t");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			sb = new StringBuffer();
-			sb.append("#comment\r");
-			sb.append("\n");
-			sb.append("\r\n");
-			sb
-					.append(" \ttests.api.java.nio.charset.CharsetProviderTest$MockCharsetProvider \t\n\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			assertTrue(Charset.availableCharsets().containsKey("mockCharset00"));
-			assertTrue(Charset.availableCharsets().containsKey("MOCKCharset00"));
-			assertTrue(Charset.availableCharsets().get("mockCharset00") instanceof MockCharset);
-			assertTrue(Charset.availableCharsets().get("MOCKCharset00") instanceof MockCharset);
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset01"));
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset02"));
-
-			assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2);
-			assertTrue(Charset.availableCharsets().get("MOCKCharset10") == charset2);
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset11"));
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset12"));
-
-			assertTrue(Charset.availableCharsets().containsKey("mockCharset10"));
-			assertTrue(Charset.availableCharsets().containsKey("MOCKCharset10"));
-			assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2);
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset11"));
-			assertFalse(Charset.availableCharsets()
-					.containsKey("mockCharset12"));
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method availableCharsets(String) when the configuration file
-	 * contains a non-existing class name.
-	 */
-	public void testAvailableCharsets_NonExistingClass() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("impossible\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.availableCharsets();
-			fail("Should throw Error!");
-		} catch (Error e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method availableCharsets(String) when the configuration file
-	 * contains a non-CharsetProvider class name.
-	 */
-	public void testAvailableCharsets_NotCharsetProviderClass()
-			throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("java.lang.String\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.availableCharsets();
-			fail("Should throw ClassCastException!");
-		} catch (ClassCastException e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-	/*
-	 * Test the method availableCharsets(String) when the configuration file
-	 * contains an illegal string.
-	 */
-	public void testAvailableCharsets_IllegalString() throws Exception {
-		try {
-			StringBuffer sb = new StringBuffer();
-			sb.append("java String\r");
-			setupFile(CONFIG_FILE1, sb.toString());
-
-			Charset.availableCharsets();
-			fail("Should throw Error!");
-		} catch (Error e) {
-			// expected
-		} finally {
-			cleanupFile(CONFIG_FILE1);
-		}
-	}
-
-    /*
-     * Mock charset provider.
-     */
-    public static class MockCharsetProvider extends CharsetProvider {
-
-        public Charset charsetForName(String charsetName) {
-            if ("MockCharset10".equalsIgnoreCase(charsetName)
-                    || "MockCharset11".equalsIgnoreCase(charsetName)
-                    || "MockCharset12".equalsIgnoreCase(charsetName)) {
-                return charset2;
-            }
-            return null;
-        }
-
-        public Iterator charsets() {
-            Vector v = new Vector();
-            v.add(charset2);
-            return v.iterator();
-        }
-    }
-
-    /*
-     * Another mock charset provider providing build-in charset "ascii".
-     */
-    public static class MockCharsetProviderACSII extends CharsetProvider {
-
-        public Charset charsetForName(String charsetName) {
-            if ("US-ASCII".equalsIgnoreCase(charsetName)
-                    || "ASCII".equalsIgnoreCase(charsetName)) {
-                return new MockCharset("US-ASCII", new String[] { "ASCII" });
-            }
-            return null;
-        }
-
-        public Iterator charsets() {
-            Vector v = new Vector();
-            v.add(new MockCharset("US-ASCII", new String[] { "ASCII" }));
-            return v.iterator();
-        }
-    }
-
-}
diff --git a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetTest.java b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetTest.java
index c658c48..92f230e 100644
--- a/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetTest.java
+++ b/harmony-tests/src/test/java/tests/api/java/nio/charset/CharsetTest.java
@@ -23,11 +23,15 @@
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.UnsupportedCharsetException;
 import java.nio.charset.spi.CharsetProvider;
+import java.nio.charset.UnsupportedCharsetException;
 import java.security.Permission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
+import java.util.Set;
 import java.util.SortedMap;
 import java.util.Vector;
 
@@ -38,802 +42,865 @@
  */
 public class CharsetTest extends TestCase {
 
-	static MockCharset charset1 = new MockCharset("mockCharset00",
-			new String[] { "mockCharset01", "mockCharset02" });
+  public void test_allAvailableCharsets() throws Exception {
+    // Check that we can instantiate every Charset, CharsetDecoder, and CharsetEncoder.
+    for (String charsetName : Charset.availableCharsets().keySet()) {
+      if (charsetName.equals("UTF-32")) {
+        // Our UTF-32 is broken. http://b/2702411
+        // TODO: remove this hack when UTF-32 is fixed.
+        continue;
+      }
 
-	static MockCharset charset2 = new MockCharset("mockCharset10",
-			new String[] { "mockCharset11", "mockCharset12" });
+      Charset cs = Charset.forName(charsetName);
+      assertNotNull(cs.newDecoder());
+      if (cs.canEncode()) {
+        CharsetEncoder enc = cs.newEncoder();
+        assertNotNull(enc);
+        assertNotNull(enc.replacement());
+      }
+    }
+  }
 
-	/*
-	 * @see TestCase#setUp()
-	 */
-	protected void setUp() throws Exception {
-		super.setUp();
-	}
+  public void test_defaultCharset() {
+    assertEquals("UTF-8", Charset.defaultCharset().name());
+  }
 
-	/*
-	 * @see TestCase#tearDown()
-	 */
-	protected void tearDown() throws Exception {
-		super.tearDown();
-	}
+  public void test_isRegistered() {
+    // Regression for HARMONY-45
 
-	/*
-	 * Test the required 6 charsets are supported.
-	 */
-	public void testRequiredCharsetSupported() {
-		assertTrue(Charset.isSupported("US-ASCII"));
-		assertTrue(Charset.isSupported("ASCII"));
-		assertTrue(Charset.isSupported("ISO-8859-1"));
-		assertTrue(Charset.isSupported("ISO8859_1"));
-		assertTrue(Charset.isSupported("UTF-8"));
-		assertTrue(Charset.isSupported("UTF8"));
-		assertTrue(Charset.isSupported("UTF-16"));
-		assertTrue(Charset.isSupported("UTF-16BE"));
-		assertTrue(Charset.isSupported("UTF-16LE"));
+    // Will contain names of charsets registered with IANA
+    Set<String> knownRegisteredCharsets = new HashSet<String>();
 
-		Charset c1 = Charset.forName("US-ASCII");
-		assertEquals("US-ASCII", Charset.forName("US-ASCII").name());
-		assertEquals("US-ASCII", Charset.forName("ASCII").name());
-		assertEquals("ISO-8859-1", Charset.forName("ISO-8859-1").name());
-		assertEquals("ISO-8859-1", Charset.forName("ISO8859_1").name());
-		assertEquals("UTF-8", Charset.forName("UTF-8").name());
-		assertEquals("UTF-8", Charset.forName("UTF8").name());
-		assertEquals("UTF-16", Charset.forName("UTF-16").name());
-		assertEquals("UTF-16BE", Charset.forName("UTF-16BE").name());
-		assertEquals("UTF-16LE", Charset.forName("UTF-16LE").name());
+    // Will contain names of charsets not known to be registered with IANA
+    Set<String> unknownRegisteredCharsets = new HashSet<String>();
 
-		assertNotSame(Charset.availableCharsets(), Charset.availableCharsets());
-		// assertSame(Charset.forName("US-ASCII"), Charset.availableCharsets()
-		// .get("US-ASCII"));
-		// assertSame(Charset.forName("US-ASCII"), c1);
-		assertTrue(Charset.availableCharsets().containsKey("US-ASCII"));
-		assertTrue(Charset.availableCharsets().containsKey("ISO-8859-1"));
-		assertTrue(Charset.availableCharsets().containsKey("UTF-8"));
-		assertTrue(Charset.availableCharsets().containsKey("UTF-16"));
-		assertTrue(Charset.availableCharsets().containsKey("UTF-16BE"));
-		assertTrue(Charset.availableCharsets().containsKey("UTF-16LE"));
-	}
-
-	/*
-	 * Test the method isSupported(String) with null.
-	 */
-	public void testIsSupported_Null() {
-		try {
-			Charset.isSupported(null);
-			fail("Should throw IllegalArgumentException!");
-		} catch (IllegalArgumentException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method isSupported(String) with empty string.
-	 *
-	 */
-	public void testIsSupported_EmptyString() {
-		try {
-			Charset.isSupported("");
-		} catch (IllegalArgumentException e) {
-                        // FIXME: Commented out since RI does throw IAE
-                        // fail("Should not throw IllegalArgumentException!");
-		}
-	}
-
-	/*
-	 * Test the method isSupported(String) with a string starting with ".".
-	 *
-	 */
-	public void testIsSupported_InvalidInitialCharacter() {
-		try {
-			Charset.isSupported(".char");
-		} catch (IllegalArgumentException e) {
-			fail("Should not throw IllegalArgumentException!");
-		}
-	}
-
-	/*
-	 * Test the method isSupported(String) with illegal charset name.
-	 */
-	public void testIsSupported_IllegalName() {
-		try {
-			Charset.isSupported(" ///#$$");
-			fail("Should throw IllegalCharsetNameException!");
-		} catch (IllegalCharsetNameException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method isSupported(String) with not supported charset name.
-	 */
-	public void testIsSupported_NotSupported() {
-		assertFalse(Charset.isSupported("impossible"));
-	}
-
-	/*
-	 * Test the method forName(String) with null.
-	 */
-	public void testForName_Null() {
-		try {
-			Charset.forName(null);
-			fail("Should throw IllegalArgumentException!");
-		} catch (IllegalArgumentException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method forName(String) with empty string.
-	 */
-	public void testForName_EmptyString() {
-		try {
-			Charset.forName("");
-			fail("Should throw IllegalArgumentException!");
-		} catch (IllegalArgumentException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method forName(String) with a string starting with ".".
-	 */
-	public void testForName_InvalidInitialCharacter() {
-		try {
-			Charset.forName(".char");
-			fail("Should throw IllegalArgumentException!");
-		} catch (IllegalArgumentException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method forName(String) with illegal charset name.
-	 */
-	public void testForName_IllegalName() {
-		try {
-			Charset.forName(" ///#$$");
-			fail("Should throw IllegalCharsetNameException!");
-		} catch (IllegalCharsetNameException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method forName(String) with not supported charset name.
-	 */
-	public void testForName_NotSupported() {
-		try {
-			Charset.forName("impossible");
-			fail("Should throw UnsupportedCharsetException!");
-		} catch (UnsupportedCharsetException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the constructor with normal parameter values.
-	 */
-	public void testConstructor_Normal() {
-		final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
-		MockCharset c = new MockCharset(mockName, new String[] { "mock" });
-		assertEquals(mockName, c.name());
-		assertEquals(mockName, c.displayName());
-		assertEquals(mockName, c.displayName(Locale.getDefault()));
-		assertEquals("mock", c.aliases().toArray()[0]);
-		assertEquals(1, c.aliases().toArray().length);
-	}
-
-	/*
-	 * Test the constructor with empty canonical name.
-	 *
-	 */
-	public void testConstructor_EmptyCanonicalName() {
-		try {
-			new MockCharset("", new String[0]);
-		} catch (IllegalCharsetNameException e) {
-                        // FIXME: Commented out since RI does throw IAE
-                        // fail("Should not throw IllegalArgumentException!");
-		}
-	}
-
-	/*
-	 * Test the constructor with illegal canonical name: starting with neither a
-	 * digit nor a letter.
-	 *
-	 */
-	public void testConstructor_IllegalCanonicalName_Initial() {
-		try {
-			new MockCharset("-123", new String[] { "mock" });
-		} catch (IllegalCharsetNameException e) {
-			fail("Should not throw IllegalArgumentException!");
-		}
-	}
-
-	/*
-	 * Test the constructor with illegal canonical name, illegal character in
-	 * the middle.
-	 */
-	public void testConstructor_IllegalCanonicalName_Middle() {
-		try {
-			new MockCharset("1%%23", new String[] { "mock" });
-			fail("Should throw IllegalCharsetNameException!");
-		} catch (IllegalCharsetNameException e) {
-			// expected
-		}
-		try {
-			new MockCharset("1//23", new String[] { "mock" });
-			fail("Should throw IllegalCharsetNameException!");
-		} catch (IllegalCharsetNameException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the constructor with null canonical name.
-	 */
-	public void testConstructor_NullCanonicalName() {
-		try {
-			MockCharset c = new MockCharset(null, new String[] { "mock" });
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the constructor with null aliases.
-	 */
-	public void testConstructor_NullAliases() {
-		MockCharset c = new MockCharset("mockChar", null);
-		assertEquals("mockChar", c.name());
-		assertEquals("mockChar", c.displayName());
-		assertEquals("mockChar", c.displayName(Locale.getDefault()));
-		assertEquals(0, c.aliases().toArray().length);
-	}
-
-	/*
-	 * Test the constructor with a null aliases.
-	 */
-	public void testConstructor_NullAliase() {
-		try {
-			new MockCharset("mockChar", new String[] { "mock", null });
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the constructor with no aliases.
-	 */
-	public void testConstructor_NoAliases() {
-		MockCharset c = new MockCharset("mockChar", new String[0]);
-		assertEquals("mockChar", c.name());
-		assertEquals("mockChar", c.displayName());
-		assertEquals("mockChar", c.displayName(Locale.getDefault()));
-		assertEquals(0, c.aliases().toArray().length);
-	}
-
-	/*
-	 * Test the constructor with empty aliases.
-	 *
-	 */
-	public void testConstructor_EmptyAliases() {
-		try {
-			new MockCharset("mockChar", new String[] { "" });
-		} catch (IllegalCharsetNameException e) {
-                        // FIXME: Commented out since RI does throw IAE
-			// fail("Should not throw IllegalArgumentException!");
-		}
-	}
-
-	/*
-	 * Test the constructor with illegal aliases: starting with neither a digit
-	 * nor a letter.
-	 *
-	 */
-	public void testConstructor_IllegalAliases_Initial() {
-		try {
-			new MockCharset("mockChar", new String[] { "mock", "-123" });
-		} catch (IllegalCharsetNameException e) {
-			fail("Should not throw IllegalArgumentException!");
-		}
-	}
-
-	/*
-	 * Test the constructor with illegal aliase, illegal character in the
-	 * middle.
-	 */
-	public void testConstructor_IllegalAliases_Middle() {
-		try {
-			new MockCharset("mockChar", new String[] { "mock", "22##ab" });
-			fail("Should throw IllegalCharsetNameException!");
-		} catch (IllegalCharsetNameException e) {
-			// expected
-		}
-		try {
-			new MockCharset("mockChar", new String[] { "mock", "22%%ab" });
-			fail("Should throw IllegalCharsetNameException!");
-		} catch (IllegalCharsetNameException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method aliases() with multiple aliases. Most conditions have
-	 * been tested in the testcases for the constructors.
-	 */
-	public void testAliases_Multiple() {
-		final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
-		MockCharset c = new MockCharset("mockChar", new String[] { "mock",
-				mockName, "mock2" });
-		assertEquals("mockChar", c.name());
-		assertEquals(3, c.aliases().size());
-		assertTrue(c.aliases().contains("mock"));
-		assertTrue(c.aliases().contains(mockName));
-		assertTrue(c.aliases().contains("mock2"));
-
-		try {
-			c.aliases().clear();
-			fail("Should throw UnsupportedOperationException!");
-		} catch (UnsupportedOperationException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method aliases() with duplicate aliases, one same with its
-	 * canonical name.
-	 */
-	public void testAliases_Duplicate() {
-		final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
-		MockCharset c = new MockCharset("mockChar", new String[] { "mockChar",
-				"mock", mockName, "mock", "mockChar", "mock", "mock2" });
-		assertEquals("mockChar", c.name());
-		assertEquals(4, c.aliases().size());
-		assertTrue(c.aliases().contains("mockChar"));
-		assertTrue(c.aliases().contains("mock"));
-		assertTrue(c.aliases().contains(mockName));
-		assertTrue(c.aliases().contains("mock2"));
-	}
-
-	/*
-	 * Test the method canEncode(). Test the default return value.
-	 */
-	public void testCanEncode() {
-		MockCharset c = new MockCharset("mock", null);
-		assertTrue(c.canEncode());
-	}
-
-	/*
-	 * Test the method isRegistered(). Test the default return value.
-	 */
-	public void testIsRegistered() {
-		MockCharset c = new MockCharset("mock", null);
-		assertTrue(c.isRegistered());
-	}
-
-	/*
-	 * The name() method has been tested by the testcases for the constructor.
-	 */
-	public void testName() {
-		// already covered by testConstructor_XXX series
-	}
-
-	/*
-	 * The displayName() method have been tested by the testcases for the
-	 * constructor.
-	 */
-	public void testDisplayName() {
-		// already covered by testConstructor_XXX series
-	}
-
-	/*
-	 * Test displayName(Locale) with null.
-	 */
-	public void testDisplayName_Locale_Null() {
-		MockCharset c = new MockCharset("mock", null);
-		assertEquals("mock", c.displayName(null));
-	}
-
-	/*
-	 * Test the method compareTo(Object) with normal conditions.
-	 */
-	public void testCompareTo_Normal() {
-		MockCharset c1 = new MockCharset("mock", null);
-		assertEquals(0, c1.compareTo(c1));
-
-		MockCharset c2 = new MockCharset("Mock", null);
-		assertEquals(0, c1.compareTo(c2));
-
-		c2 = new MockCharset("mock2", null);
-		assertTrue(c1.compareTo(c2) < 0);
-		assertTrue(c2.compareTo(c1) > 0);
-
-		c2 = new MockCharset("mack", null);
-		assertTrue(c1.compareTo(c2) > 0);
-		assertTrue(c2.compareTo(c1) < 0);
-
-		c2 = new MockCharset("m.", null);
-		assertTrue(c1.compareTo(c2) > 0);
-		assertTrue(c2.compareTo(c1) < 0);
-
-		c2 = new MockCharset("m:", null);
-		assertEquals("mock".compareToIgnoreCase("m:"), c1.compareTo(c2));
-		assertEquals("m:".compareToIgnoreCase("mock"), c2.compareTo(c1));
-
-		c2 = new MockCharset("m-", null);
-		assertTrue(c1.compareTo(c2) > 0);
-		assertTrue(c2.compareTo(c1) < 0);
-
-		c2 = new MockCharset("m_", null);
-		assertTrue(c1.compareTo(c2) > 0);
-		assertTrue(c2.compareTo(c1) < 0);
-	}
-
-	/*
-	 * Test the method compareTo(Object) with null param.
-	 */
-	public void testCompareTo_Null() {
-		MockCharset c1 = new MockCharset("mock", null);
-		try {
-			c1.compareTo(null);
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method compareTo(Object) with another kind of charset object.
-	 */
-	public void testCompareTo_DiffCharsetClass() {
-		MockCharset c1 = new MockCharset("mock", null);
-		MockCharset2 c2 = new MockCharset2("Mock", new String[] { "myname" });
-		assertEquals(0, c1.compareTo(c2));
-		assertEquals(0, c2.compareTo(c1));
-	}
-
-	/*
-	 * Test the method equals(Object) with null param.
-	 */
-	public void testEquals_Normal() {
-		MockCharset c1 = new MockCharset("mock", null);
-		MockCharset2 c2 = new MockCharset2("mock", null);
-		assertTrue(c1.equals(c2));
-		assertTrue(c2.equals(c1));
-
-		c2 = new MockCharset2("Mock", null);
-		assertFalse(c1.equals(c2));
-		assertFalse(c2.equals(c1));
-	}
-
-	/*
-	 * Test the method equals(Object) with normal conditions.
-	 */
-	public void testEquals_Null() {
-		MockCharset c1 = new MockCharset("mock", null);
-		assertFalse(c1.equals(null));
-	}
-
-	/*
-	 * Test the method equals(Object) with another kind of charset object.
-	 */
-	public void testEquals_NonCharsetObject() {
-		MockCharset c1 = new MockCharset("mock", null);
-		assertFalse(c1.equals("test"));
-	}
-
-	/*
-	 * Test the method equals(Object) with another kind of charset object.
-	 */
-	public void testEquals_DiffCharsetClass() {
-		MockCharset c1 = new MockCharset("mock", null);
-		MockCharset2 c2 = new MockCharset2("mock", null);
-		assertTrue(c1.equals(c2));
-		assertTrue(c2.equals(c1));
-	}
-
-	/*
-	 * Test the method hashCode().
-	 */
-	public void testHashCode_DiffCharsetClass() {
-		MockCharset c1 = new MockCharset("mock", null);
-		assertEquals(c1.hashCode(), "mock".hashCode());
-
-		final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
-		c1 = new MockCharset(mockName, new String[] { "mockChar", "mock",
-				mockName, "mock", "mockChar", "mock", "mock2" });
-		assertEquals(mockName.hashCode(), c1.hashCode());
-	}
-
-	/*
-	 * Test the method encode(CharBuffer) under normal condition.
-	 */
-	public void testEncode_CharBuffer_Normal() throws Exception {
-		MockCharset c1 = new MockCharset("testEncode_CharBuffer_Normal_mock", null);
-		ByteBuffer bb = c1.encode(CharBuffer.wrap("abcdefg"));
-		assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
-		bb = c1.encode(CharBuffer.wrap(""));
-		assertEquals("", new String(bb.array(), "iso8859-1"));
-	}
-
-	/*
-	 * Test the method encode(CharBuffer) with an unmappable char.
-	 */
-	public void testEncode_CharBuffer_Unmappable() throws Exception {
-		Charset c1 = Charset.forName("iso8859-1");
-		ByteBuffer bb = c1.encode(CharBuffer.wrap("abcd\u5D14efg"));
-		assertEquals(new String(bb.array(), "iso8859-1"), "abcd"
-				+ new String(c1.newEncoder().replacement(), "iso8859-1")
-				+ "efg");
-	}
-
-	/*
-	 * Test the method encode(CharBuffer) with null CharBuffer.
-	 */
-	public void testEncode_CharBuffer_NullCharBuffer() {
-		MockCharset c = new MockCharset("mock", null);
-		try {
-			c.encode((CharBuffer) null);
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method encode(CharBuffer) with null encoder.
-	 */
-	public void testEncode_CharBuffer_NullEncoder() {
-		MockCharset2 c = new MockCharset2("mock2", null);
-		try {
-			c.encode(CharBuffer.wrap("hehe"));
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method encode(String) under normal condition.
-	 */
-	public void testEncode_String_Normal() throws Exception {
-		MockCharset c1 = new MockCharset("testEncode_String_Normal_mock", null);
-		ByteBuffer bb = c1.encode("abcdefg");
-		assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
-		bb = c1.encode("");
-		assertEquals("", new String(bb.array(), "iso8859-1"));
-	}
-
-	/*
-	 * Test the method encode(String) with an unmappable char.
-	 */
-	public void testEncode_String_Unmappable() throws Exception {
-		Charset c1 = Charset.forName("iso8859-1");
-		ByteBuffer bb = c1.encode("abcd\u5D14efg");
-		assertEquals(new String(bb.array(), "iso8859-1"), "abcd"
-				+ new String(c1.newEncoder().replacement(), "iso8859-1")
-				+ "efg");
-	}
-
-	/*
-	 * Test the method encode(String) with null CharBuffer.
-	 */
-	public void testEncode_String_NullString() {
-		MockCharset c = new MockCharset("mock", null);
-		try {
-			c.encode((String) null);
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method encode(String) with null encoder.
-	 */
-	public void testEncode_String_NullEncoder() {
-
-		MockCharset2 c = new MockCharset2("mock2", null);
-		try {
-			c.encode("hehe");
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method decode(ByteBuffer) under normal condition.
-	 */
-	public void testDecode_Normal() throws Exception {
-		MockCharset c1 = new MockCharset("mock", null);
-		CharBuffer cb = c1.decode(ByteBuffer.wrap("abcdefg"
-				.getBytes("iso8859-1")));
-		assertEquals("abcdefg", new String(cb.array()));
-		cb = c1.decode(ByteBuffer.wrap("".getBytes("iso8859-1")));
-		assertEquals("", new String(cb.array()));
-	}
-
-	/*
-	 * Test the method decode(ByteBuffer) with a malformed input.
-	 */
-	public void testDecode_Malformed() throws Exception {
-		Charset c1 = Charset.forName("iso8859-1");
-		CharBuffer cb = c1.decode(ByteBuffer.wrap("abcd\u5D14efg"
-				.getBytes("iso8859-1")));
-		byte[] replacement = c1.newEncoder().replacement();
-		assertEquals(new String(cb.array()).trim(), "abcd" + new String(replacement, "iso8859-1")
-				+ "efg");
-	}
-
-	/*
-	 * Test the method decode(ByteBuffer) with null CharBuffer.
-	 */
-	public void testDecode_NullByteBuffer() {
-		MockCharset c = new MockCharset("mock", null);
-		try {
-			c.decode(null);
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method decode(ByteBuffer) with null encoder.
-	 */
-	public void testDecode_NullDecoder() {
-		MockCharset2 c = new MockCharset2("mock2", null);
-		try {
-			c.decode(ByteBuffer.wrap("hehe".getBytes()));
-			fail("Should throw NullPointerException!");
-		} catch (NullPointerException e) {
-			// expected
-		}
-	}
-
-	/*
-	 * Test the method toString().
-	 */
-	public void testToString() {
-		MockCharset c1 = new MockCharset("mock", null);
-		assertTrue(-1 != c1.toString().indexOf("mock"));
-	}
-
-    /**
-     * @tests java.nio.charset.Charset#availableCharsets()
-     */
-    public void test_availableCharsets() throws Exception {
-        // regression test for Harmony-1051
-        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(null);
-            SortedMap<String, Charset> charsets = Charset.availableCharsets();
-            // make sure "mockCharset00" is loaded by MockCharsetProvider
-            assertTrue(charsets.containsKey("mockCharset00"));
-        } finally {
-            Thread.currentThread().setContextClassLoader(originalClassLoader);
-        }
+    Set<String> names = Charset.availableCharsets().keySet();
+    for (Iterator nameItr = names.iterator(); nameItr.hasNext();) {
+      String name = (String) nameItr.next();
+      if (name.toLowerCase(Locale.ROOT).startsWith("x-")) {
+        unknownRegisteredCharsets.add(name);
+      } else {
+        knownRegisteredCharsets.add(name);
+      }
     }
 
-    /**
-     * @tests java.nio.charset.Charset#availableCharsets()
-     */
-    public void test_forNameLString() throws Exception {
-        // regression test for Harmony-1051
-        ClassLoader originalClassLoader = Thread.currentThread()
-                .getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(null);
-            // make sure "mockCharset00" is loaded by MockCharsetProvider
-            assertNotNull(Charset.forName("mockCharset00"));
-        } finally {
-            Thread.currentThread().setContextClassLoader(originalClassLoader);
+    for (Iterator nameItr = knownRegisteredCharsets.iterator(); nameItr.hasNext();) {
+      String name = (String) nameItr.next();
+      Charset cs = Charset.forName(name);
+      if (!cs.isRegistered()) {
+        System.err.println("isRegistered was false for " + name + " " + cs.name() + " " + cs.aliases());
+      }
+      assertTrue("isRegistered was false for " + name + " " + cs.name() + " " + cs.aliases(), cs.isRegistered());
+    }
+    for (Iterator nameItr = unknownRegisteredCharsets.iterator(); nameItr.hasNext();) {
+      String name = (String) nameItr.next();
+      Charset cs = Charset.forName(name);
+      assertFalse("isRegistered was true for " + name + " " + cs.name() + " " + cs.aliases(), cs.isRegistered());
+    }
+  }
+
+  public void test_guaranteedCharsetsAvailable() throws Exception {
+    // All Java implementations must support these charsets.
+    assertNotNull(Charset.forName("ISO-8859-1"));
+    assertNotNull(Charset.forName("US-ASCII"));
+    assertNotNull(Charset.forName("UTF-16"));
+    assertNotNull(Charset.forName("UTF-16BE"));
+    assertNotNull(Charset.forName("UTF-16LE"));
+    assertNotNull(Charset.forName("UTF-8"));
+  }
+
+  // http://code.google.com/p/android/issues/detail?id=42769
+  public void test_42769() throws Exception {
+    ArrayList<Thread> threads = new ArrayList<Thread>();
+    for (int i = 0; i < 10; ++i) {
+      Thread t = new Thread(new Runnable() {
+        public void run() {
+          for (int i = 0; i < 50; ++i) {
+            Charset.availableCharsets();
+          }
         }
+      });
+      threads.add(t);
     }
 
-	/*
-	 * Mock charset class.
-	 */
-	static final class MockCharset extends Charset {
+    for (Thread t : threads) {
+      t.start();
+    }
+    for (Thread t : threads) {
+      t.join();
+    }
+  }
 
-		public MockCharset(String canonicalName, String[] aliases) {
-			super(canonicalName, aliases);
-		}
+  public void test_have_canonical_EUC_JP() throws Exception {
+    assertEquals("EUC-JP", Charset.forName("EUC-JP").name());
+  }
 
-		public boolean contains(Charset cs) {
-			return false;
-		}
+  public void test_EUC_JP_replacement_character() throws Exception {
+    // We have text either side of the replacement character, because all kinds of errors
+    // could lead to a replacement character being returned.
+    assertEncodes(Charset.forName("EUC-JP"), " \ufffd ", ' ', 0xf4, 0xfe, ' ');
+    assertDecodes(Charset.forName("EUC-JP"), " \ufffd ", ' ', 0xf4, 0xfe, ' ');
+  }
 
-		public CharsetDecoder newDecoder() {
-			return new MockDecoder(this);
-		}
+  public void test_SCSU_replacement_character() throws Exception {
+    // We have text either side of the replacement character, because all kinds of errors
+    // could lead to a replacement character being returned.
+    assertEncodes(Charset.forName("SCSU"), " \ufffd ", ' ', 14, 0xff, 0xfd, ' ');
+    assertDecodes(Charset.forName("SCSU"), " \ufffd ", ' ', 14, 0xff, 0xfd, ' ');
+  }
 
-		public CharsetEncoder newEncoder() {
-			return new MockEncoder(this);
-		}
-	}
+  public void test_Shift_JIS_replacement_character() throws Exception {
+    // We have text either side of the replacement character, because all kinds of errors
+    // could lead to a replacement character being returned.
+    assertEncodes(Charset.forName("Shift_JIS"), " \ufffd ", ' ', 0xfc, 0xfc, ' ');
+    assertDecodes(Charset.forName("Shift_JIS"), " \ufffd ", ' ', 0xfc, 0xfc, ' ');
+  }
 
-	/*
-	 * Another mock charset class.
-	 */
-	static class MockCharset2 extends Charset {
+  public void test_UTF_16() throws Exception {
+    Charset cs = Charset.forName("UTF-16");
+    // Writes big-endian, with a big-endian BOM.
+    assertEncodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
+    // Reads whatever the BOM tells it to read...
+    assertDecodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
+    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
+    // ...and defaults to reading big-endian if there's no BOM.
+    assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
+  }
 
-		public MockCharset2(String canonicalName, String[] aliases) {
-			super(canonicalName, aliases);
-		}
+  public void test_UTF_16BE() throws Exception {
+    Charset cs = Charset.forName("UTF-16BE");
+    // Writes big-endian, with no BOM.
+    assertEncodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
+    // Treats a little-endian BOM as an error and continues to read big-endian.
+    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
+    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 'a', 0x06, 0x66);
+    // Accepts a big-endian BOM and includes U+FEFF in the decoded output.
+    assertDecodes(cs, "\ufeffa\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
+    // Defaults to reading big-endian.
+    assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
+  }
 
-		public boolean contains(Charset cs) {
-			return false;
-		}
+  public void test_UTF_16LE() throws Exception {
+    Charset cs = Charset.forName("UTF-16LE");
+    // Writes little-endian, with no BOM.
+    assertEncodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
+    // Accepts a little-endian BOM and includes U+FEFF in the decoded output.
+    assertDecodes(cs, "\ufeffa\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
+    // Treats a big-endian BOM as an error and continues to read little-endian.
+    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
+    assertDecodes(cs, "\ufffda\u0666", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
+    // Defaults to reading little-endian.
+    assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
+  }
 
-		public CharsetDecoder newDecoder() {
-			return null;
-		}
+  public void test_x_UTF_16LE_BOM() throws Exception {
+    Charset cs = Charset.forName("x-UTF-16LE-BOM");
+    // Writes little-endian, with a BOM.
+    assertEncodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
+    // Accepts a little-endian BOM and swallows the BOM.
+    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
+    // Swallows a big-endian BOM, but continues to read little-endian!
+    assertDecodes(cs, "\u6100\u6606", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
+    // Defaults to reading little-endian.
+    assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
+  }
 
-		public CharsetEncoder newEncoder() {
-			return null;
-		}
-	}
+  public void test_UTF_32() throws Exception {
+    Charset cs = Charset.forName("UTF-32");
+    // Writes big-endian, with no BOM.
+    assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Reads whatever the BOM tells it to read...
+    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // ...and defaults to reading big-endian if there's no BOM.
+    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+  }
 
-	/*
-	 * Mock encoder.
-	 */
-	static class MockEncoder extends java.nio.charset.CharsetEncoder {
+  public void test_UTF_32BE() throws Exception {
+    Charset cs = Charset.forName("UTF-32BE");
+    // Writes big-endian, with no BOM.
+    assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Treats a little-endian BOM as an error and continues to read big-endian.
+    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
+    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Accepts a big-endian BOM and swallows the BOM.
+    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Defaults to reading big-endian.
+    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+  }
 
-		public MockEncoder(Charset cs) {
-			super(cs, 1, 3, new byte[] { (byte) '?' });
-		}
+  public void test_UTF_32LE() throws Exception {
+    Charset cs = Charset.forName("UTF-32LE");
+    // Writes little-endian, with no BOM.
+    assertEncodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // Accepts a little-endian BOM and swallows the BOM.
+    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // Treats a big-endian BOM as an error and continues to read little-endian.
+    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
+    assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // Defaults to reading little-endian.
+    assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+  }
 
-		protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
-			while (in.remaining() > 0) {
-				out.put((byte) in.get());
-				// out.put((byte) '!');
-			}
-			return CoderResult.UNDERFLOW;
-		}
-	}
+  public void test_X_UTF_32BE_BOM() throws Exception {
+    Charset cs = Charset.forName("X-UTF-32BE-BOM");
+    // Writes big-endian, with a big-endian BOM.
+    assertEncodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Treats a little-endian BOM as an error and continues to read big-endian.
+    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
+    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Swallows a big-endian BOM, and continues to read big-endian.
+    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+    // Defaults to reading big-endian.
+    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
+  }
 
-	/*
-	 * Mock decoder.
-	 */
-	static class MockDecoder extends java.nio.charset.CharsetDecoder {
+  public void test_X_UTF_32LE_BOM() throws Exception {
+    Charset cs = Charset.forName("X-UTF-32LE-BOM");
+    // Writes little-endian, with a little-endian BOM.
+    assertEncodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // Accepts a little-endian BOM and swallows the BOM.
+    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // Treats a big-endian BOM as an error and continues to read little-endian.
+    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
+    assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+    // Defaults to reading little-endian.
+    assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
+  }
 
-		public MockDecoder(Charset cs) {
-			super(cs, 1, 10);
-		}
+  private byte[] toByteArray(int[] ints) {
+    byte[] result = new byte[ints.length];
+    for (int i = 0; i < ints.length; ++i) {
+      result[i] = (byte) ints[i];
+    }
+    return result;
+  }
 
-		protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
-			while (in.remaining() > 0) {
-				out.put((char) in.get());
-			}
-			return CoderResult.UNDERFLOW;
-		}
-	}
+  private void assertEncodes(Charset cs, String s, int... expectedByteInts) throws Exception {
+    ByteBuffer out = cs.encode(s);
+    byte[] bytes = new byte[out.remaining()];
+    out.get(bytes);
+    assertEquals(Arrays.toString(toByteArray(expectedByteInts)), Arrays.toString(bytes));
+  }
 
-	/*
-	 * Mock charset provider.
-	 */
-	public static class MockCharsetProvider extends CharsetProvider {
+  private void assertDecodes(Charset cs, String s, int... byteInts) throws Exception {
+    ByteBuffer in = ByteBuffer.wrap(toByteArray(byteInts));
+    CharBuffer out = cs.decode(in);
+    assertEquals(s, out.toString());
+  }
 
-		public Charset charsetForName(String charsetName) {
-			if ("MockCharset00".equalsIgnoreCase(charsetName)
-					|| "MockCharset01".equalsIgnoreCase(charsetName)
-					|| "MockCharset02".equalsIgnoreCase(charsetName)) {
-				return new MockCharset("mockCharset00", new String[] {
-						"mockCharset01", "mockCharset02" });
-			}
-			return null;
-		}
+  public void test_forNameLjava_lang_String() {
+    // Invoke forName two times with the same canonical name.
+    // It should return the same reference.
+    Charset cs1 = Charset.forName("UTF-8");
+    Charset cs2 = Charset.forName("UTF-8");
+    assertSame(cs1, cs2);
 
-		public Iterator charsets() {
-			Vector v = new Vector();
-			v.add(new MockCharset("mockCharset00", new String[] {
-					"mockCharset01", "mockCharset02" }));
-			return v.iterator();
-		}
-	}
+    // test forName: invoke forName two times for the same Charset using
+    // canonical name and alias, it should return the same reference.
+    Charset cs3 = Charset.forName("ASCII");
+    Charset cs4 = Charset.forName("US-ASCII");
+    assertSame(cs3, cs4);
+  }
+
+  static MockCharset charset1 = new MockCharset("mockCharset00",
+                                                new String[] { "mockCharset01", "mockCharset02" });
+
+  static MockCharset charset2 = new MockCharset("mockCharset10",
+                                                new String[] { "mockCharset11", "mockCharset12" });
+
+  // Test the required 6 charsets are supported.
+  public void testRequiredCharsetSupported() {
+    assertTrue(Charset.isSupported("US-ASCII"));
+    assertTrue(Charset.isSupported("ASCII"));
+    assertTrue(Charset.isSupported("ISO-8859-1"));
+    assertTrue(Charset.isSupported("ISO8859_1"));
+    assertTrue(Charset.isSupported("UTF-8"));
+    assertTrue(Charset.isSupported("UTF8"));
+    assertTrue(Charset.isSupported("UTF-16"));
+    assertTrue(Charset.isSupported("UTF-16BE"));
+    assertTrue(Charset.isSupported("UTF-16LE"));
+
+    Charset c1 = Charset.forName("US-ASCII");
+    assertEquals("US-ASCII", Charset.forName("US-ASCII").name());
+    assertEquals("US-ASCII", Charset.forName("ASCII").name());
+    assertEquals("ISO-8859-1", Charset.forName("ISO-8859-1").name());
+    assertEquals("ISO-8859-1", Charset.forName("ISO8859_1").name());
+    assertEquals("UTF-8", Charset.forName("UTF-8").name());
+    assertEquals("UTF-8", Charset.forName("UTF8").name());
+    assertEquals("UTF-16", Charset.forName("UTF-16").name());
+    assertEquals("UTF-16BE", Charset.forName("UTF-16BE").name());
+    assertEquals("UTF-16LE", Charset.forName("UTF-16LE").name());
+
+    assertNotSame(Charset.availableCharsets(), Charset.availableCharsets());
+    // assertSame(Charset.forName("US-ASCII"), Charset.availableCharsets().get("US-ASCII"));
+    // assertSame(Charset.forName("US-ASCII"), c1);
+    assertTrue(Charset.availableCharsets().containsKey("US-ASCII"));
+    assertTrue(Charset.availableCharsets().containsKey("ISO-8859-1"));
+    assertTrue(Charset.availableCharsets().containsKey("UTF-8"));
+    assertTrue(Charset.availableCharsets().containsKey("UTF-16"));
+    assertTrue(Charset.availableCharsets().containsKey("UTF-16BE"));
+    assertTrue(Charset.availableCharsets().containsKey("UTF-16LE"));
+  }
+
+  public void testIsSupported_Null() {
+    try {
+      Charset.isSupported(null);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testIsSupported_EmptyString() {
+    try {
+      Charset.isSupported("");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testIsSupported_InvalidInitialCharacter() {
+    try {
+      Charset.isSupported(".char");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testIsSupported_IllegalName() {
+    try {
+      Charset.isSupported(" ///#$$");
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testIsSupported_NotSupported() {
+    assertFalse(Charset.isSupported("well-formed-name-of-a-charset-that-does-not-exist"));
+  }
+
+  public void testForName_Null() {
+    try {
+      Charset.forName(null);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testForName_EmptyString() {
+    try {
+      Charset.forName("");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testForName_InvalidInitialCharacter() {
+    try {
+      Charset.forName(".char");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testForName_IllegalName() {
+    try {
+      Charset.forName(" ///#$$");
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testForName_NotSupported() {
+    try {
+      Charset.forName("impossible");
+      fail();
+    } catch (UnsupportedCharsetException expected) {
+    }
+  }
+
+  public void testConstructor_Normal() {
+    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
+    MockCharset c = new MockCharset(mockName, new String[] { "mock" });
+    assertEquals(mockName, c.name());
+    assertEquals(mockName, c.displayName());
+    assertEquals(mockName, c.displayName(Locale.getDefault()));
+    assertEquals("mock", c.aliases().toArray()[0]);
+    assertEquals(1, c.aliases().toArray().length);
+  }
+
+  public void testConstructor_EmptyCanonicalName() {
+    try {
+      new MockCharset("", new String[0]);
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testConstructor_IllegalCanonicalName_Initial() {
+    try {
+      new MockCharset("-123", new String[] { "mock" });
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testConstructor_IllegalCanonicalName_Middle() {
+    try {
+      new MockCharset("1%%23", new String[] { "mock" });
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+    try {
+      new MockCharset("1//23", new String[] { "mock" });
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testConstructor_NullCanonicalName() {
+    try {
+      MockCharset c = new MockCharset(null, new String[] { "mock" });
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testConstructor_NullAliases() {
+    MockCharset c = new MockCharset("mockChar", null);
+    assertEquals("mockChar", c.name());
+    assertEquals("mockChar", c.displayName());
+    assertEquals("mockChar", c.displayName(Locale.getDefault()));
+    assertEquals(0, c.aliases().toArray().length);
+  }
+
+  public void testConstructor_NullAliase() {
+    try {
+      new MockCharset("mockChar", new String[] { "mock", null });
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testConstructor_NoAliases() {
+    MockCharset c = new MockCharset("mockChar", new String[0]);
+    assertEquals("mockChar", c.name());
+    assertEquals("mockChar", c.displayName());
+    assertEquals("mockChar", c.displayName(Locale.getDefault()));
+    assertEquals(0, c.aliases().toArray().length);
+  }
+
+  public void testConstructor_EmptyAliases() {
+    try {
+      new MockCharset("mockChar", new String[] { "" });
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  // Test the constructor with illegal aliases: starting with neither a digit nor a letter.
+  public void testConstructor_IllegalAliases_Initial() {
+    try {
+      new MockCharset("mockChar", new String[] { "mock", "-123" });
+      fail();
+    } catch (IllegalCharsetNameException e) {
+    }
+  }
+
+  public void testConstructor_IllegalAliases_Middle() {
+    try {
+      new MockCharset("mockChar", new String[] { "mock", "22##ab" });
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+    try {
+      new MockCharset("mockChar", new String[] { "mock", "22%%ab" });
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testAliases_Multiple() {
+    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
+    MockCharset c = new MockCharset("mockChar", new String[] { "mock", mockName, "mock2" });
+    assertEquals("mockChar", c.name());
+    assertEquals(3, c.aliases().size());
+    assertTrue(c.aliases().contains("mock"));
+    assertTrue(c.aliases().contains(mockName));
+    assertTrue(c.aliases().contains("mock2"));
+
+    try {
+      c.aliases().clear();
+      fail();
+    } catch (UnsupportedOperationException expected) {
+    }
+  }
+
+  public void testAliases_Duplicate() {
+    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
+    MockCharset c = new MockCharset("mockChar", new String[] { "mockChar",
+                                                                  "mock", mockName, "mock", "mockChar", "mock", "mock2" });
+    assertEquals("mockChar", c.name());
+    assertEquals(4, c.aliases().size());
+    assertTrue(c.aliases().contains("mockChar"));
+    assertTrue(c.aliases().contains("mock"));
+    assertTrue(c.aliases().contains(mockName));
+    assertTrue(c.aliases().contains("mock2"));
+  }
+
+  public void testCanEncode() {
+    MockCharset c = new MockCharset("mock", null);
+    assertTrue(c.canEncode());
+  }
+
+  public void testIsRegistered() {
+    MockCharset c = new MockCharset("mock", null);
+    assertTrue(c.isRegistered());
+  }
+
+  public void testDisplayName_Locale_Null() {
+    MockCharset c = new MockCharset("mock", null);
+    assertEquals("mock", c.displayName(null));
+  }
+
+  public void testCompareTo_Normal() {
+    MockCharset c1 = new MockCharset("mock", null);
+    assertEquals(0, c1.compareTo(c1));
+
+    MockCharset c2 = new MockCharset("Mock", null);
+    assertEquals(0, c1.compareTo(c2));
+
+    c2 = new MockCharset("mock2", null);
+    assertTrue(c1.compareTo(c2) < 0);
+    assertTrue(c2.compareTo(c1) > 0);
+
+    c2 = new MockCharset("mack", null);
+    assertTrue(c1.compareTo(c2) > 0);
+    assertTrue(c2.compareTo(c1) < 0);
+
+    c2 = new MockCharset("m.", null);
+    assertTrue(c1.compareTo(c2) > 0);
+    assertTrue(c2.compareTo(c1) < 0);
+
+    c2 = new MockCharset("m:", null);
+    assertEquals("mock".compareToIgnoreCase("m:"), c1.compareTo(c2));
+    assertEquals("m:".compareToIgnoreCase("mock"), c2.compareTo(c1));
+
+    c2 = new MockCharset("m-", null);
+    assertTrue(c1.compareTo(c2) > 0);
+    assertTrue(c2.compareTo(c1) < 0);
+
+    c2 = new MockCharset("m_", null);
+    assertTrue(c1.compareTo(c2) > 0);
+    assertTrue(c2.compareTo(c1) < 0);
+  }
+
+  public void testCompareTo_Null() {
+    MockCharset c1 = new MockCharset("mock", null);
+    try {
+      c1.compareTo(null);
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testCompareTo_DiffCharsetClass() {
+    MockCharset c1 = new MockCharset("mock", null);
+    MockCharset2 c2 = new MockCharset2("Mock", new String[] { "myname" });
+    assertEquals(0, c1.compareTo(c2));
+    assertEquals(0, c2.compareTo(c1));
+  }
+
+  public void testEquals_Normal() {
+    MockCharset c1 = new MockCharset("mock", null);
+    MockCharset2 c2 = new MockCharset2("mock", null);
+    assertTrue(c1.equals(c2));
+    assertTrue(c2.equals(c1));
+
+    c2 = new MockCharset2("Mock", null);
+    assertFalse(c1.equals(c2));
+    assertFalse(c2.equals(c1));
+  }
+
+  public void testEquals_Null() {
+    MockCharset c1 = new MockCharset("mock", null);
+    assertFalse(c1.equals(null));
+  }
+
+  public void testEquals_NonCharsetObject() {
+    MockCharset c1 = new MockCharset("mock", null);
+    assertFalse(c1.equals("test"));
+  }
+
+  public void testEquals_DiffCharsetClass() {
+    MockCharset c1 = new MockCharset("mock", null);
+    MockCharset2 c2 = new MockCharset2("mock", null);
+    assertTrue(c1.equals(c2));
+    assertTrue(c2.equals(c1));
+  }
+
+  public void testHashCode_DiffCharsetClass() {
+    MockCharset c1 = new MockCharset("mock", null);
+    assertEquals(c1.hashCode(), "mock".hashCode());
+
+    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
+    c1 = new MockCharset(mockName, new String[] { "mockChar", "mock",
+                                                     mockName, "mock", "mockChar", "mock", "mock2" });
+    assertEquals(mockName.hashCode(), c1.hashCode());
+  }
+
+  public void testEncode_CharBuffer_Normal() throws Exception {
+    MockCharset c1 = new MockCharset("testEncode_CharBuffer_Normal_mock", null);
+    ByteBuffer bb = c1.encode(CharBuffer.wrap("abcdefg"));
+    assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
+    bb = c1.encode(CharBuffer.wrap(""));
+    assertEquals("", new String(bb.array(), "iso8859-1"));
+  }
+
+  public void testEncode_CharBuffer_Unmappable() throws Exception {
+    Charset c1 = Charset.forName("iso8859-1");
+    ByteBuffer bb = c1.encode(CharBuffer.wrap("abcd\u5D14efg"));
+    assertEquals(new String(bb.array(), "iso8859-1"),
+                 "abcd" + new String(c1.newEncoder().replacement(), "iso8859-1") + "efg");
+  }
+
+  public void testEncode_CharBuffer_NullCharBuffer() {
+    MockCharset c = new MockCharset("mock", null);
+    try {
+      c.encode((CharBuffer) null);
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testEncode_CharBuffer_NullEncoder() {
+    MockCharset2 c = new MockCharset2("mock2", null);
+    try {
+      c.encode(CharBuffer.wrap("hehe"));
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testEncode_String_Normal() throws Exception {
+    MockCharset c1 = new MockCharset("testEncode_String_Normal_mock", null);
+    ByteBuffer bb = c1.encode("abcdefg");
+    assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
+    bb = c1.encode("");
+    assertEquals("", new String(bb.array(), "iso8859-1"));
+  }
+
+  public void testEncode_String_Unmappable() throws Exception {
+    Charset c1 = Charset.forName("iso8859-1");
+    ByteBuffer bb = c1.encode("abcd\u5D14efg");
+    assertEquals(new String(bb.array(), "iso8859-1"),
+                 "abcd" + new String(c1.newEncoder().replacement(), "iso8859-1") + "efg");
+  }
+
+  public void testEncode_String_NullString() {
+    MockCharset c = new MockCharset("mock", null);
+    try {
+      c.encode((String) null);
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testEncode_String_NullEncoder() {
+    MockCharset2 c = new MockCharset2("mock2", null);
+    try {
+      c.encode("hehe");
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testDecode_Normal() throws Exception {
+    MockCharset c1 = new MockCharset("mock", null);
+    CharBuffer cb = c1.decode(ByteBuffer.wrap("abcdefg".getBytes("iso8859-1")));
+    assertEquals("abcdefg", new String(cb.array()));
+    cb = c1.decode(ByteBuffer.wrap("".getBytes("iso8859-1")));
+    assertEquals("", new String(cb.array()));
+  }
+
+  public void testDecode_Malformed() throws Exception {
+    Charset c1 = Charset.forName("iso8859-1");
+    CharBuffer cb = c1.decode(ByteBuffer.wrap("abcd\u5D14efg".getBytes("iso8859-1")));
+    byte[] replacement = c1.newEncoder().replacement();
+    assertEquals(new String(cb.array()).trim(), "abcd" + new String(replacement, "iso8859-1") + "efg");
+  }
+
+  public void testDecode_NullByteBuffer() {
+    MockCharset c = new MockCharset("mock", null);
+    try {
+      c.decode(null);
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testDecode_NullDecoder() {
+    MockCharset2 c = new MockCharset2("mock2", null);
+    try {
+      c.decode(ByteBuffer.wrap("hehe".getBytes()));
+      fail();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testToString() {
+    MockCharset c1 = new MockCharset("mock", null);
+    assertTrue(-1 != c1.toString().indexOf("mock"));
+  }
+
+  static final class MockCharset extends Charset {
+    public MockCharset(String canonicalName, String[] aliases) {
+      super(canonicalName, aliases);
+    }
+
+    public boolean contains(Charset cs) {
+      return false;
+    }
+
+    public CharsetDecoder newDecoder() {
+      return new MockDecoder(this);
+    }
+
+    public CharsetEncoder newEncoder() {
+      return new MockEncoder(this);
+    }
+  }
+
+  static class MockCharset2 extends Charset {
+    public MockCharset2(String canonicalName, String[] aliases) {
+      super(canonicalName, aliases);
+    }
+
+    public boolean contains(Charset cs) {
+      return false;
+    }
+
+    public CharsetDecoder newDecoder() {
+      return null;
+    }
+
+    public CharsetEncoder newEncoder() {
+      return null;
+    }
+  }
+
+  static class MockEncoder extends java.nio.charset.CharsetEncoder {
+    public MockEncoder(Charset cs) {
+      super(cs, 1, 3, new byte[] { (byte) '?' });
+    }
+
+    protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+      while (in.remaining() > 0) {
+        out.put((byte) in.get());
+        // out.put((byte) '!');
+      }
+      return CoderResult.UNDERFLOW;
+    }
+  }
+
+  static class MockDecoder extends java.nio.charset.CharsetDecoder {
+    public MockDecoder(Charset cs) {
+      super(cs, 1, 10);
+    }
+
+    protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+      while (in.remaining() > 0) {
+        out.put((char) in.get());
+      }
+      return CoderResult.UNDERFLOW;
+    }
+  }
+
+
+  // Test the method isSupported(String) with charset supported by multiple providers.
+  public void testIsSupported_And_ForName_NormalProvider() throws Exception {
+    assertTrue(Charset.isSupported("mockCharset10"));
+    // ignore case problem in mock, intended
+    assertTrue(Charset.isSupported("MockCharset11"));
+    assertTrue(Charset.isSupported("MockCharset12"));
+    assertTrue(Charset.isSupported("MOCKCharset10"));
+    // intended case problem in mock
+    assertTrue(Charset.isSupported("MOCKCharset11"));
+    assertTrue(Charset.isSupported("MOCKCharset12"));
+
+    assertTrue(Charset.forName("mockCharset10") instanceof MockCharset);
+    assertTrue(Charset.forName("mockCharset11") instanceof MockCharset);
+    assertTrue(Charset.forName("mockCharset12") instanceof MockCharset);
+
+    assertTrue(Charset.forName("mockCharset10") == charset2);
+    // intended case problem in mock
+    Charset.forName("mockCharset11");
+    assertTrue(Charset.forName("mockCharset12") == charset2);
+  }
+
+  // Test the method availableCharsets() with charset supported by multiple providers.
+  public void testAvailableCharsets_NormalProvider() throws Exception {
+    assertTrue(Charset.availableCharsets().containsKey("mockCharset00"));
+    assertTrue(Charset.availableCharsets().containsKey("MOCKCharset00"));
+    assertTrue(Charset.availableCharsets().get("mockCharset00") instanceof MockCharset);
+    assertTrue(Charset.availableCharsets().get("MOCKCharset00") instanceof MockCharset);
+    assertFalse(Charset.availableCharsets().containsKey("mockCharset01"));
+    assertFalse(Charset.availableCharsets().containsKey("mockCharset02"));
+
+    assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2);
+    assertTrue(Charset.availableCharsets().get("MOCKCharset10") == charset2);
+    assertFalse(Charset.availableCharsets().containsKey("mockCharset11"));
+    assertFalse(Charset.availableCharsets().containsKey("mockCharset12"));
+
+    assertTrue(Charset.availableCharsets().containsKey("mockCharset10"));
+    assertTrue(Charset.availableCharsets().containsKey("MOCKCharset10"));
+    assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2);
+    assertFalse(Charset.availableCharsets().containsKey("mockCharset11"));
+    assertFalse(Charset.availableCharsets().containsKey("mockCharset12"));
+  }
+
+  // Test the method forName(String) when the charset provider supports a
+  // built-in charset.
+  public void testForName_DuplicateWithBuiltInCharset() throws Exception {
+    assertFalse(Charset.forName("us-ascii") instanceof MockCharset);
+    assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset);
+  }
+
+  public static class MockCharsetProvider extends CharsetProvider {
+    public Charset charsetForName(String charsetName) {
+      if ("MockCharset00".equalsIgnoreCase(charsetName) ||
+          "MockCharset01".equalsIgnoreCase(charsetName) ||
+          "MockCharset02".equalsIgnoreCase(charsetName)) {
+        return charset1;
+      } else if ("MockCharset10".equalsIgnoreCase(charsetName) ||
+          "MockCharset11".equalsIgnoreCase(charsetName) ||
+          "MockCharset12".equalsIgnoreCase(charsetName)) {
+        return charset2;
+      }
+      return null;
+    }
+
+    public Iterator charsets() {
+      Vector v = new Vector();
+      v.add(charset1);
+      v.add(charset2);
+      return v.iterator();
+    }
+  }
+
+  // Another mock charset provider attempting to provide the built-in charset "ascii" again.
+  public static class MockCharsetProviderASCII extends CharsetProvider {
+    public Charset charsetForName(String charsetName) {
+      if ("US-ASCII".equalsIgnoreCase(charsetName) || "ASCII".equalsIgnoreCase(charsetName)) {
+        return new MockCharset("US-ASCII", new String[] { "ASCII" });
+      }
+      return null;
+    }
+
+    public Iterator charsets() {
+      Vector v = new Vector();
+      v.add(new MockCharset("US-ASCII", new String[] { "ASCII" }));
+      return v.iterator();
+    }
+  }
 }
diff --git a/harmony-tests/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java b/harmony-tests/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java
index 798a048..d483fcb 100644
--- a/harmony-tests/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java
+++ b/harmony-tests/src/test/java/tests/api/java/nio/charset/UTFCharsetEncoderTest.java
@@ -71,10 +71,8 @@
 	}
 
 	public void testSpecificDefaultValue() {
-		assertEquals(1.1, encoder.averageBytesPerChar(), 0.0001);
-		// assertEquals(2, encoder.averageBytesPerChar(), 0.0001);
-		assertEquals(4, encoder.maxBytesPerChar(), 0);
-		// assertEquals(3, encoder.maxBytesPerChar(), 0);
+		assertEquals(2, encoder.averageBytesPerChar(), 0);
+		assertEquals(3, encoder.maxBytesPerChar(), 0);
 	}
 
 	CharBuffer getMalformedCharBuffer() {
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
new file mode 100644
index 0000000..8f19e3a
--- /dev/null
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2007 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 dalvik.system;
+
+/**
+ * Provides an interface to VM-global, Dalvik-specific features.
+ * An application cannot create its own Runtime instance, and must obtain
+ * one from the getRuntime method.
+ *
+ * @hide
+ */
+public final class VMRuntime {
+
+    /**
+     * Holds the VMRuntime singleton.
+     */
+    private static final VMRuntime THE_ONE = new VMRuntime();
+
+    /**
+     * Prevents this class from being instantiated.
+     */
+    private VMRuntime() {
+    }
+
+    /**
+     * Returns the object that represents the VM instance's Dalvik-specific
+     * runtime environment.
+     *
+     * @return the runtime object
+     */
+    public static VMRuntime getRuntime() {
+        return THE_ONE;
+    }
+
+    /**
+     * Returns a copy of the VM's command-line property settings.
+     * These are in the form "name=value" rather than "-Dname=value".
+     */
+    public native String[] properties();
+
+    /**
+     * Returns the VM's boot class path.
+     */
+    public native String bootClassPath();
+
+    /**
+     * Returns the VM's class path.
+     */
+    public native String classPath();
+
+    /**
+     * Returns the VM's version.
+     */
+    public native String vmVersion();
+
+    /**
+     * Returns the name of the shared library providing the VM implementation.
+     */
+    public native String vmLibrary();
+
+    /**
+     * Gets the current ideal heap utilization, represented as a number
+     * between zero and one.  After a GC happens, the Dalvik heap may
+     * be resized so that (size of live objects) / (size of heap) is
+     * equal to this number.
+     *
+     * @return the current ideal heap utilization
+     */
+    public native float getTargetHeapUtilization();
+
+    /**
+     * Sets the current ideal heap utilization, represented as a number
+     * between zero and one.  After a GC happens, the Dalvik heap may
+     * be resized so that (size of live objects) / (size of heap) is
+     * equal to this number.
+     *
+     * <p>This is only a hint to the garbage collector and may be ignored.
+     *
+     * @param newTarget the new suggested ideal heap utilization.
+     *                  This value may be adjusted internally.
+     * @return the previous ideal heap utilization
+     * @throws IllegalArgumentException if newTarget is &lt;= 0.0 or &gt;= 1.0
+     */
+    public float setTargetHeapUtilization(float newTarget) {
+        if (newTarget <= 0.0f || newTarget >= 1.0f) {
+            throw new IllegalArgumentException(newTarget +
+                    " out of range (0,1)");
+        }
+        /* Synchronize to make sure that only one thread gets
+         * a given "old" value if both update at the same time.
+         * Allows for reliable save-and-restore semantics.
+         */
+        synchronized (this) {
+            float oldTarget = getTargetHeapUtilization();
+            nativeSetTargetHeapUtilization(newTarget);
+            return oldTarget;
+        }
+    }
+
+    /**
+     * Sets the target SDK version. Should only be called before the
+     * app starts to run, because it may change the VM's behavior in
+     * dangerous ways. Use 0 to mean "current" (since callers won't
+     * necessarily know the actual current SDK version, and the
+     * allocated version numbers start at 1).
+     */
+    public native void setTargetSdkVersion(int targetSdkVersion);
+
+    /**
+     * This method exists for binary compatibility.  It was part of a
+     * heap sizing API which was removed in Android 3.0 (Honeycomb).
+     */
+    @Deprecated
+    public long getMinimumHeapSize() {
+        return 0;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It was part of a
+     * heap sizing API which was removed in Android 3.0 (Honeycomb).
+     */
+    @Deprecated
+    public long setMinimumHeapSize(long size) {
+        return 0;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It used to
+     * perform a garbage collection that cleared SoftReferences.
+     */
+    @Deprecated
+    public void gcSoftReferences() {}
+
+    /**
+     * This method exists for binary compatibility.  It is equivalent
+     * to {@link System#runFinalization}.
+     */
+    @Deprecated
+    public void runFinalizationSync() {
+        System.runFinalization();
+    }
+
+    /**
+     * Implements setTargetHeapUtilization().
+     *
+     * @param newTarget the new suggested ideal heap utilization.
+     *                  This value may be adjusted internally.
+     */
+    private native void nativeSetTargetHeapUtilization(float newTarget);
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the external allocation API which was removed in Android 3.0 (Honeycomb).
+     */
+    @Deprecated
+    public boolean trackExternalAllocation(long size) {
+        return true;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the external allocation API which was removed in Android 3.0 (Honeycomb).
+     */
+    @Deprecated
+    public void trackExternalFree(long size) {}
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the external allocation API which was removed in Android 3.0 (Honeycomb).
+     */
+    @Deprecated
+    public long getExternalBytesAllocated() {
+        return 0;
+    }
+
+    /**
+     * Tells the VM to enable the JIT compiler. If the VM does not have a JIT
+     * implementation, calling this method should have no effect.
+     */
+    public native void startJitCompilation();
+
+    /**
+     * Tells the VM to disable the JIT compiler. If the VM does not have a JIT
+     * implementation, calling this method should have no effect.
+     */
+    public native void disableJitCompilation();
+
+    /**
+     * Returns an array allocated in an area of the Java heap where it will never be moved.
+     * This is used to implement native allocations on the Java heap, such as DirectByteBuffers
+     * and Bitmaps.
+     */
+    public native Object newNonMovableArray(Class<?> componentType, int length);
+
+    /**
+     * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
+     * give you the address of a copy of the array when in forcecopy mode.
+     */
+    public native long addressOf(Object array);
+
+    /**
+     * Removes any growth limits, allowing the application to allocate
+     * up to the maximum heap size.
+     */
+    public native void clearGrowthLimit();
+
+    /**
+     * Returns true if either a Java debugger or native debugger is active.
+     */
+    public native boolean isDebuggerActive();
+
+    /**
+     * Registers a native allocation so that the heap knows about it and performs GC as required.
+     * If the number of native allocated bytes exceeds the native allocation watermark, the
+     * function requests a concurrent GC. If the native bytes allocated exceeds a second higher
+     * watermark, it is determined that the application is registering native allocations at an
+     * unusually high rate and a GC is performed inside of the function to prevent memory usage
+     * from excessively increasing.
+     */
+    public native void registerNativeAllocation(int bytes);
+
+    /**
+     * Registers a native free by reducing the number of native bytes accounted for.
+     */
+    public native void registerNativeFree(int bytes);
+
+    public native void trimHeap();
+    public native void concurrentGC();
+
+    public void preloadDexCaches() {
+        // Do nothing with ART, image generation already does this.
+    }
+}
diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java
new file mode 100644
index 0000000..ee0a0db
--- /dev/null
+++ b/libart/src/main/java/dalvik/system/VMStack.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 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 dalvik.system;
+
+/**
+ * Provides a limited interface to the Dalvik VM stack. This class is mostly
+ * used for implementing security checks.
+ *
+ * @hide
+ */
+public final class VMStack {
+    /**
+     * Returns the defining class loader of the caller's caller.
+     *
+     * @return the requested class loader, or {@code null} if this is the
+     *         bootstrap class loader.
+     */
+    native public static ClassLoader getCallingClassLoader();
+
+    /**
+     * Returns the class of the caller's caller.
+     *
+     * @return the requested class, or {@code null}.
+     */
+    public static Class<?> getStackClass1() {
+        return getStackClass2();
+    }
+
+    /**
+     * Returns the class of the caller's caller's caller.
+     *
+     * @return the requested class, or {@code null}.
+     */
+    native public static Class<?> getStackClass2();
+
+    /**
+     * Returns the first ClassLoader on the call stack that isn't either of
+     * the passed-in ClassLoaders.
+     */
+    public native static ClassLoader getClosestUserClassLoader(ClassLoader bootstrap,
+                                                               ClassLoader system);
+
+    /**
+     * Retrieves the stack trace from the specified thread.
+     *
+     * @param t
+     *      thread of interest
+     * @return an array of stack trace elements, or null if the thread
+     *      doesn't have a stack trace (e.g. because it exited)
+     */
+    native public static StackTraceElement[] getThreadStackTrace(Thread t);
+
+    /**
+     * Retrieves a partial stack trace from the specified thread into
+     * the provided array.
+     *
+     * @param t
+     *      thread of interest
+     * @param stackTraceElements
+     *      preallocated array for use when only the top of stack is
+     *      desired. Unused elements will be filled with null values.
+     * @return the number of elements filled
+     */
+    native public static int fillStackTraceElements(Thread t,
+        StackTraceElement[] stackTraceElements);
+}
diff --git a/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java
new file mode 100644
index 0000000..d1b9a18
--- /dev/null
+++ b/libart/src/main/java/java/lang/Class.java
@@ -0,0 +1,1756 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2006-2007 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.lang;
+
+import com.android.dex.Dex;
+import dalvik.system.VMStack;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AbstractMethod;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.ArtField;
+import java.lang.reflect.ArtMethod;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.GenericSignatureParser;
+import libcore.reflect.InternalNames;
+import libcore.reflect.Types;
+import libcore.util.BasicLruCache;
+import libcore.util.CollectionUtils;
+import libcore.util.EmptyArray;
+
+/**
+ * The in-memory representation of a Java class. This representation serves as
+ * the starting point for querying class-related information, a process usually
+ * called "reflection". There are basically three types of {@code Class}
+ * instances: those representing real classes and interfaces, those representing
+ * primitive types, and those representing array classes.
+ *
+ * <h4>Class instances representing object types (classes or interfaces)</h4>
+ * <p>
+ * These represent an ordinary class or interface as found in the class
+ * hierarchy. The name associated with these {@code Class} instances is simply
+ * the fully qualified class name of the class or interface that it represents.
+ * In addition to this human-readable name, each class is also associated by a
+ * so-called <em>descriptor</em>, which is the letter "L", followed by the
+ * class name and a semicolon (";"). The descriptor is what the runtime system
+ * uses internally for identifying the class (for example in a DEX file).
+ * </p>
+ * <h4>Classes representing primitive types</h4>
+ * <p>
+ * These represent the standard Java primitive types and hence share their
+ * names (for example "int" for the {@code int} primitive type). Although it is
+ * not possible to create new instances based on these {@code Class} instances,
+ * they are still useful for providing reflection information, and as the
+ * component type of array classes. There is one {@code Class} instance for each
+ * primitive type, and their descriptors are:
+ * </p>
+ * <ul>
+ * <li>{@code B} representing the {@code byte} primitive type</li>
+ * <li>{@code S} representing the {@code short} primitive type</li>
+ * <li>{@code I} representing the {@code int} primitive type</li>
+ * <li>{@code J} representing the {@code long} primitive type</li>
+ * <li>{@code F} representing the {@code float} primitive type</li>
+ * <li>{@code D} representing the {@code double} primitive type</li>
+ * <li>{@code C} representing the {@code char} primitive type</li>
+ * <li>{@code Z} representing the {@code boolean} primitive type</li>
+ * <li>{@code V} representing void function return values</li>
+ * </ul>
+ * <p>
+ * <h4>Classes representing array classes</h4>
+ * <p>
+ * These represent the classes of Java arrays. There is one such {@code Class}
+ * instance per combination of array leaf component type and arity (number of
+ * dimensions). In this case, the name associated with the {@code Class}
+ * consists of one or more left square brackets (one per dimension in the array)
+ * followed by the descriptor of the class representing the leaf component type,
+ * which can be either an object type or a primitive type. The descriptor of a
+ * {@code Class} representing an array type is the same as its name. Examples
+ * of array class descriptors are:
+ * </p>
+ * <ul>
+ * <li>{@code [I} representing the {@code int[]} type</li>
+ * <li>{@code [Ljava/lang/String;} representing the {@code String[]} type</li>
+ * <li>{@code [[[C} representing the {@code char[][][]} type (three dimensions!)</li>
+ * </ul>
+ */
+public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type {
+
+    private static final long serialVersionUID = 3206093459760846163L;
+
+    /** defining class loader, or NULL for the "bootstrap" system loader. */
+    private transient ClassLoader classLoader;
+
+    /**
+     * For array classes, the component class object for instanceof/checkcast (for String[][][],
+     * this will be String[][]). NULL for non-array classes.
+     */
+    private transient Class<?> componentType;
+    /**
+     * DexCache of resolved constant pool entries. Will be null for certain VM-generated classes
+     * e.g. arrays and primitive classes.
+     */
+    private transient DexCache dexCache;
+
+    /** static, private, and &lt;init&gt; methods. */
+    private transient ArtMethod[] directMethods;
+
+    /**
+     * Instance fields. These describe the layout of the contents of an Object. Note that only the
+     * fields directly declared by this class are listed in iFields; fields declared by a
+     * superclass are listed in the superclass's Class.iFields.
+     *
+     * All instance fields that refer to objects are guaranteed to be at the beginning of the field
+     * list.  {@link Class#numReferenceInstanceFields} specifies the number of reference fields.
+     */
+    private transient ArtField[] iFields;
+
+    /**
+     * The interface table (iftable_) contains pairs of a interface class and an array of the
+     * interface methods. There is one pair per interface supported by this class.  That
+     * means one pair for each interface we support directly, indirectly via superclass, or
+     * indirectly via a superinterface.  This will be null if neither we nor our superclass
+     * implement any interfaces.
+     *
+     * Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()".
+     * Invoke faceObj.blah(), where "blah" is part of the Face interface.  We can't easily use a
+     * single vtable.
+     *
+     * For every interface a concrete class implements, we create an array of the concrete vtable_
+     * methods for the methods in the interface.
+     */
+    private transient Object[] ifTable;
+
+    /** Lazily computed name of this class; always prefer calling getName(). */
+    private transient String name;
+
+    /** Static fields */
+    private transient ArtField[] sFields;
+
+    /** The superclass, or NULL if this is java.lang.Object, an interface or primitive type. */
+    private transient Class<? super T> superClass;
+
+    /** If class verify fails, we must return same error on subsequent tries. */
+    private transient Class<?> verifyErrorClass;
+
+    /** Virtual methods defined in this class; invoked through vtable. */
+    private transient ArtMethod[] virtualMethods;
+
+    /**
+     * Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass
+     * is copied in, and virtual methods from our class either replace those from the super or are
+     * appended. For abstract classes, methods may be created in the vtable that aren't in
+     * virtual_ methods_ for miranda methods.
+     */
+    private transient ArtMethod[] vtable;
+
+    /** access flags; low 16 bits are defined by VM spec */
+    private transient int accessFlags;
+
+    /**
+     * Total size of the Class instance; used when allocating storage on GC heap.
+     * See also {@link Class#objectSize}.
+     */
+    private transient int classSize;
+
+    /**
+     * tid used to check for recursive static initializer invocation.
+     */
+    private transient int clinitThreadId;
+
+    /**
+     * Class def index from dex file. An index of 65535 indicates that there is no class definition,
+     * for example for an array type.
+     * TODO: really 16bits as type indices are 16bit.
+     */
+    private transient int dexClassDefIndex;
+
+    /**
+     * Class type index from dex file, lazily computed. An index of 65535 indicates that the type
+     * index isn't known. Volatile to avoid double-checked locking bugs.
+     * TODO: really 16bits as type indices are 16bit.
+     */
+    private transient volatile int dexTypeIndex;
+
+    /** Number of instance fields that are object references. */
+    private transient int numReferenceInstanceFields;
+
+    /** Number of static fields that are object references. */
+    private transient int numReferenceStaticFields;
+
+    /**
+     * Total object size; used when allocating storage on GC heap. For interfaces and abstract
+     * classes this will be zero. See also {@link Class#classSize}.
+     */
+    private transient int objectSize;
+
+    /** Primitive type value, or 0 if not a primitive type; set for generated primitive classes. */
+    private transient int primitiveType;
+
+    /** Bitmap of offsets of iFields. */
+    private transient int referenceInstanceOffsets;
+
+    /** Bitmap of offsets of sFields. */
+    private transient int referenceStaticOffsets;
+
+    /** State of class initialization */
+    private transient int status;
+
+    private Class() {
+        // Prevent this class to be instantiated, instance should be created by JVM only
+    }
+
+    /**
+     * Returns a {@code Class} object which represents the class with
+     * the given name. The name should be the name of a non-primitive
+     * class, as described in the {@link Class class definition}.
+     * Primitive types can not be found using this method; use {@code
+     * int.class} or {@code Integer.TYPE} instead.
+     *
+     * <p>If the class has not yet been loaded, it is loaded and initialized
+     * first. This is done through either the class loader of the calling class
+     * or one of its parent class loaders. It is possible that a static initializer is run as
+     * a result of this call.
+     *
+     * @throws ClassNotFoundException
+     *             if the requested class cannot be found.
+     * @throws LinkageError
+     *             if an error occurs during linkage
+     * @throws ExceptionInInitializerError
+     *             if an exception occurs during static initialization of a
+     *             class.
+     */
+    public static Class<?> forName(String className) throws ClassNotFoundException {
+        return forName(className, true, VMStack.getCallingClassLoader());
+    }
+
+    /**
+     * Returns a {@code Class} object which represents the class with
+     * the given name. The name should be the name of a non-primitive
+     * class, as described in the {@link Class class definition}.
+     * Primitive types can not be found using this method; use {@code
+     * int.class} or {@code Integer.TYPE} instead.
+     *
+     * <p>If the class has not yet been loaded, it is loaded first, using the given class loader.
+     * If the class has not yet been initialized and {@code shouldInitialize} is true,
+     * the class will be initialized.
+     *
+     * @throws ClassNotFoundException
+     *             if the requested class cannot be found.
+     * @throws LinkageError
+     *             if an error occurs during linkage
+     * @throws ExceptionInInitializerError
+     *             if an exception occurs during static initialization of a
+     *             class.
+     */
+    public static Class<?> forName(String className, boolean shouldInitialize,
+            ClassLoader classLoader) throws ClassNotFoundException {
+
+        if (classLoader == null) {
+            classLoader = ClassLoader.getSystemClassLoader();
+        }
+        // Catch an Exception thrown by the underlying native code. It wraps
+        // up everything inside a ClassNotFoundException, even if e.g. an
+        // Error occurred during initialization. This as a workaround for
+        // an ExceptionInInitializerError that's also wrapped. It is actually
+        // expected to be thrown. Maybe the same goes for other errors.
+        // Not wrapping up all the errors will break android though.
+        Class<?> result;
+        try {
+            result = classForName(className, shouldInitialize, classLoader);
+        } catch (ClassNotFoundException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof LinkageError) {
+                throw (LinkageError) cause;
+            }
+            throw e;
+        }
+        return result;
+    }
+
+    static native Class<?> classForName(String className, boolean shouldInitialize,
+            ClassLoader classLoader) throws ClassNotFoundException;
+
+    /**
+     * Returns an array containing {@code Class} objects for all
+     * public classes, interfaces, enums and annotations that are
+     * members of this class and its superclasses. This does not
+     * include classes of implemented interfaces.  If there are no
+     * such class members or if this object represents a primitive
+     * type then an array of length 0 is returned.
+     */
+    public Class<?>[] getClasses() {
+        List<Class<?>> result = new ArrayList<Class<?>>();
+        for (Class<?> c = this; c != null; c = c.superClass) {
+            for (Class<?> member : c.getDeclaredClasses()) {
+                if (Modifier.isPublic(member.getModifiers())) {
+                    result.add(member);
+                }
+            }
+        }
+        return result.toArray(new Class[result.size()]);
+    }
+
+    @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+        return AnnotationAccess.getAnnotation(this, annotationType);
+    }
+
+    /**
+     * Returns an array containing all the annotations of this class. If there are no annotations
+     * then an empty array is returned.
+     *
+     * @see #getDeclaredAnnotations()
+     */
+    @Override public Annotation[] getAnnotations() {
+        return AnnotationAccess.getAnnotations(this);
+    }
+
+    /**
+     * Returns the canonical name of this class. If this class does not have a
+     * canonical name as defined in the Java Language Specification, then the
+     * method returns {@code null}.
+     */
+    public String getCanonicalName() {
+        if (isLocalClass() || isAnonymousClass())
+            return null;
+
+        if (isArray()) {
+            /*
+             * The canonical name of an array type depends on the (existence of)
+             * the component type's canonical name.
+             */
+            String name = getComponentType().getCanonicalName();
+            if (name != null) {
+                return name + "[]";
+            }
+        } else if (isMemberClass()) {
+            /*
+             * The canonical name of an inner class depends on the (existence
+             * of) the declaring class' canonical name.
+             */
+            String name = getDeclaringClass().getCanonicalName();
+            if (name != null) {
+                return name + "." + getSimpleName();
+            }
+        } else {
+            /*
+             * The canonical name of a top-level class or primitive type is
+             * equal to the fully qualified name.
+             */
+            return getName();
+        }
+
+        /*
+         * Other classes don't have a canonical name.
+         */
+        return null;
+    }
+
+    /**
+     * Returns the class loader which was used to load the class represented by
+     * this {@code Class}. Implementations are free to return {@code null} for
+     * classes that were loaded by the bootstrap class loader. The Android
+     * reference implementation, though, always returns a reference to an actual
+     * class loader.
+     */
+    public ClassLoader getClassLoader() {
+        if (this.isPrimitive()) {
+            return null;
+        }
+
+        ClassLoader loader = getClassLoaderImpl();
+        if (loader == null) {
+            loader = BootClassLoader.getInstance();
+        }
+        return loader;
+    }
+
+    /**
+     * This must be provided by the VM vendor, as it is used by other provided
+     * class implementations in this package. Outside of this class, it is used
+     * by SecurityManager.classLoaderDepth(),
+     * currentClassLoader() and currentLoadedClass(). Return the ClassLoader for
+     * this Class without doing any security checks. The bootstrap ClassLoader
+     * is returned, unlike getClassLoader() which returns null in place of the
+     * bootstrap ClassLoader.
+     */
+    ClassLoader getClassLoaderImpl() {
+        ClassLoader loader = classLoader;
+        return loader == null ? BootClassLoader.getInstance() : loader;
+    }
+
+    /**
+     * Returns a {@code Class} object which represents the component type if
+     * this class represents an array type. Returns {@code null} if this class
+     * does not represent an array type. The component type of an array type is
+     * the type of the elements of the array.
+     */
+    public Class<?> getComponentType() {
+      return componentType;
+    }
+
+    /**
+     * Returns the dex file from which this class was loaded.
+     *
+     * @hide
+     */
+    public Dex getDex() {
+        if (dexCache == null) {
+            return null;
+        }
+        return dexCache.getDex();
+    }
+
+    /**
+     * Returns a string from the dex cache, computing the string from the dex file if necessary.
+     *
+     * @hide
+     */
+    public String getDexCacheString(Dex dex, int dexStringIndex) {
+        String[] dexCacheStrings = dexCache.strings;
+        String s = dexCacheStrings[dexStringIndex];
+        if (s == null) {
+            s = dex.strings().get(dexStringIndex).intern();
+            dexCacheStrings[dexStringIndex] = s;
+        }
+        return s;
+    }
+
+    /**
+     * Returns a resolved type from the dex cache, computing the type from the dex file if
+     * necessary.
+     *
+     * @hide
+     */
+    public Class<?> getDexCacheType(Dex dex, int dexTypeIndex) {
+        Class<?>[] dexCacheResolvedTypes = dexCache.resolvedTypes;
+        Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex];
+        if (resolvedType == null) {
+            int descriptorIndex = dex.typeIds().get(dexTypeIndex);
+            String descriptor = getDexCacheString(dex, descriptorIndex);
+            resolvedType = InternalNames.getClass(getClassLoader(), descriptor);
+            dexCacheResolvedTypes[dexTypeIndex] = resolvedType;
+        }
+        return resolvedType;
+    }
+
+    /**
+     * Returns a {@code Constructor} object which represents the public
+     * constructor matching the given parameter types.
+     * {@code (Class[]) null} is equivalent to the empty array.
+     *
+     * @throws NoSuchMethodException
+     *             if the constructor cannot be found.
+     * @see #getDeclaredConstructor(Class[])
+     */
+    public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException {
+        return getConstructor(parameterTypes, true);
+    }
+
+    /**
+     * Returns a {@code Constructor} object which represents the constructor
+     * matching the specified parameter types that is declared by the class
+     * represented by this {@code Class}.
+     * {@code (Class[]) null} is equivalent to the empty array.
+     *
+     * @throws NoSuchMethodException
+     *             if the requested constructor cannot be found.
+     * @see #getConstructor(Class[])
+     */
+    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
+            throws NoSuchMethodException {
+        return getConstructor(parameterTypes, false);
+    }
+
+    /**
+     * Returns a constructor with the given parameters.
+     *
+     * @param publicOnly true to only return public constructores.
+     * @param parameterTypes argument types to match the constructor's.
+     */
+    private Constructor<T> getConstructor(Class<?>[] parameterTypes, boolean publicOnly)
+            throws NoSuchMethodException {
+        if (parameterTypes == null) {
+            parameterTypes = EmptyArray.CLASS;
+        }
+        for (Class<?> c : parameterTypes) {
+            if (c == null) {
+                throw new NoSuchMethodException("parameter type is null");
+            }
+        }
+        Constructor<T> result = getDeclaredConstructorInternal(parameterTypes);
+        if (result == null || publicOnly && !Modifier.isPublic(result.getAccessFlags())) {
+            throw new NoSuchMethodException("<init> " + Arrays.toString(parameterTypes));
+        }
+        return result;
+    }
+
+    /**
+     * Returns the constructor with the given parameters if it is defined by this class; null
+     * otherwise. This may return a non-public member.
+     *
+     * @param args the types of the parameters to the constructor.
+     */
+    private Constructor<T> getDeclaredConstructorInternal(Class<?>[] args) {
+        if (directMethods != null) {
+            for (ArtMethod m : directMethods) {
+                int modifiers = m.getAccessFlags();
+                if (Modifier.isStatic(modifiers)) {
+                    // skip <clinit> which is a static constructor
+                    continue;
+                }
+                if (!Modifier.isConstructor(modifiers)) {
+                    continue;
+                }
+                if (!ArtMethod.equalConstructorParameters(m, args)) {
+                    continue;
+                }
+                return new Constructor<T>(m);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an array containing {@code Constructor} objects for all public
+     * constructors for this {@code Class}. If there
+     * are no public constructors or if this {@code Class} represents an array
+     * class, a primitive type or void then an empty array is returned.
+     *
+     * @see #getDeclaredConstructors()
+     */
+    public Constructor<?>[] getConstructors() {
+        ArrayList<Constructor<T>> constructors = new ArrayList();
+        getDeclaredConstructors(true, constructors);
+        return constructors.toArray(new Constructor[constructors.size()]);
+    }
+
+    /**
+     * Returns an array containing {@code Constructor} objects for all
+     * constructors declared in the class represented by this {@code Class}. If
+     * there are no constructors or if this {@code Class} represents an array
+     * class, a primitive type or void then an empty array is returned.
+     *
+     * @see #getConstructors()
+     */
+    public Constructor<?>[] getDeclaredConstructors() {
+        ArrayList<Constructor<T>> constructors = new ArrayList();
+        getDeclaredConstructors(false, constructors);
+        return constructors.toArray(new Constructor[constructors.size()]);
+    }
+
+    private void getDeclaredConstructors(boolean publicOnly, List<Constructor<T>> constructors) {
+        if (directMethods != null) {
+            for (ArtMethod m : directMethods) {
+                int modifiers = m.getAccessFlags();
+                if (!publicOnly || Modifier.isPublic(modifiers)) {
+                    if (Modifier.isStatic(modifiers)) {
+                        // skip <clinit> which is a static constructor
+                        continue;
+                    }
+                    if (Modifier.isConstructor(modifiers)) {
+                        constructors.add(new Constructor<T>(m));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code Method} object which represents the method matching the
+     * specified name and parameter types that is declared by the class
+     * represented by this {@code Class}.
+     *
+     * @param name
+     *            the requested method's name.
+     * @param parameterTypes
+     *            the parameter types of the requested method.
+     *            {@code (Class[]) null} is equivalent to the empty array.
+     * @return the method described by {@code name} and {@code parameterTypes}.
+     * @throws NoSuchMethodException
+     *             if the requested constructor cannot be found.
+     * @throws NullPointerException
+     *             if {@code name} is {@code null}.
+     * @see #getMethod(String, Class[])
+     */
+    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
+            throws NoSuchMethodException {
+        return getMethod(name, parameterTypes, false);
+    }
+
+    /**
+     * Returns a {@code Method} object which represents the public method with
+     * the specified name and parameter types.
+     * {@code (Class[]) null} is equivalent to the empty array.
+     * This method first searches the
+     * class C represented by this {@code Class}, then the superclasses of C and
+     * finally the interfaces implemented by C and finally the superclasses of C
+     * for a method with matching name.
+     *
+     * @throws NoSuchMethodException
+     *             if the method cannot be found.
+     * @see #getDeclaredMethod(String, Class[])
+     */
+    public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException {
+        return getMethod(name, parameterTypes, true);
+    }
+
+    private Method getMethod(String name, Class<?>[] parameterTypes, boolean recursivePublicMethods)
+            throws NoSuchMethodException {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        if (parameterTypes == null) {
+            parameterTypes = EmptyArray.CLASS;
+        }
+        for (Class<?> c : parameterTypes) {
+            if (c == null) {
+                throw new NoSuchMethodException("parameter type is null");
+            }
+        }
+        Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes)
+                                               : getDeclaredMethodInternal(name, parameterTypes);
+        // Fail if we didn't find the method or it was expected to be public.
+        if (result == null ||
+            (recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) {
+            throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes));
+        }
+        return result;
+    }
+
+    private Method getPublicMethodRecursive(String name, Class<?>[] parameterTypes) {
+        // search superclasses
+        for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+            Method result = c.getDeclaredMethodInternal(name, parameterTypes);
+            if (result != null && Modifier.isPublic(result.getAccessFlags())) {
+                return result;
+            }
+        }
+        // search iftable which has a flattened and uniqued list of interfaces
+        Object[] iftable = ifTable;
+        if (iftable != null) {
+            for (int i = 0; i < iftable.length; i += 2) {
+                Class<?> ifc = (Class<?>) iftable[i];
+                Method result = ifc.getPublicMethodRecursive(name, parameterTypes);
+                if (result != null && Modifier.isPublic(result.getAccessFlags())) {
+                    return result;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the method if it is defined by this class; null otherwise. This may return a
+     * non-public member.
+     *
+     * @param name the method name
+     * @param args the method's parameter types
+     */
+    private Method getDeclaredMethodInternal(String name, Class<?>[] args) {
+        // Covariant return types permit the class to define multiple
+        // methods with the same name and parameter types. Prefer to
+        // return a non-synthetic method in such situations. We may
+        // still return a synthetic method to handle situations like
+        // escalated visibility. We never return miranda methods that
+        // were synthesized by the VM.
+        int skipModifiers = Modifier.MIRANDA | Modifier.SYNTHETIC;
+        ArtMethod artMethodResult = null;
+        if (virtualMethods != null) {
+            for (ArtMethod m : virtualMethods) {
+                String methodName = ArtMethod.getMethodName(m);
+                if (!name.equals(methodName)) {
+                    continue;
+                }
+                if (!ArtMethod.equalMethodParameters(m, args)) {
+                    continue;
+                }
+                int modifiers = m.getAccessFlags();
+                if ((modifiers & skipModifiers) == 0) {
+                    return new Method(m);
+                }
+                if ((modifiers & Modifier.MIRANDA) == 0) {
+                    // Remember as potential result if it's not a miranda method.
+                    artMethodResult = m;
+                }
+            }
+        }
+        if (artMethodResult == null) {
+            if (directMethods != null) {
+                for (ArtMethod m : directMethods) {
+                    int modifiers = m.getAccessFlags();
+                    if (Modifier.isConstructor(modifiers)) {
+                        continue;
+                    }
+                    String methodName = ArtMethod.getMethodName(m);
+                    if (!name.equals(methodName)) {
+                        continue;
+                    }
+                    if (!ArtMethod.equalMethodParameters(m, args)) {
+                        continue;
+                    }
+                    if ((modifiers & skipModifiers) == 0) {
+                        return new Method(m);
+                    }
+                    // Direct methods cannot be miranda methods,
+                    // so this potential result must be synthetic.
+                    artMethodResult = m;
+                }
+            }
+        }
+        if (artMethodResult == null) {
+            return null;
+        }
+        return new Method(artMethodResult);
+    }
+
+    /**
+     * Returns an array containing {@code Method} objects for all methods
+     * declared in the class represented by this {@code Class}. If there are no
+     * methods or if this {@code Class} represents an array class, a primitive
+     * type or void then an empty array is returned.
+     *
+     * @see #getMethods()
+     */
+    public Method[] getDeclaredMethods() {
+        int initial_size = virtualMethods == null ? 0 : virtualMethods.length;
+        initial_size += directMethods == null ? 0 : directMethods.length;
+        ArrayList<Method> methods = new ArrayList<Method>(initial_size);
+        getDeclaredMethods(false, methods);
+        Method[] result = methods.toArray(new Method[methods.size()]);
+        for (Method m : result) {
+            // Throw NoClassDefFoundError if types cannot be resolved.
+            m.getReturnType();
+            m.getParameterTypes();
+        }
+        return result;
+
+    }
+
+    /**
+     * Returns the list of methods without performing any security checks
+     * first. If no methods exist, an empty array is returned.
+     */
+    private void getDeclaredMethods(boolean publicOnly, List<Method> methods) {
+        if (virtualMethods != null) {
+            for (ArtMethod m : virtualMethods) {
+                int modifiers = m.getAccessFlags();
+                if (!publicOnly || Modifier.isPublic(modifiers)) {
+                    // Add non-miranda virtual methods.
+                    if ((modifiers & Modifier.MIRANDA) == 0) {
+                        methods.add(new Method(m));
+                    }
+                }
+            }
+        }
+        if (directMethods != null) {
+            for (ArtMethod m : directMethods) {
+                int modifiers = m.getAccessFlags();
+                if (!publicOnly || Modifier.isPublic(modifiers)) {
+                    // Add non-constructor direct/static methods.
+                    if (!Modifier.isConstructor(modifiers)) {
+                        methods.add(new Method(m));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns an array containing {@code Method} objects for all public methods
+     * for the class C represented by this {@code Class}. Methods may be
+     * declared in C, the interfaces it implements or in the superclasses of C.
+     * The elements in the returned array are in no particular order.
+     *
+     * <p>If there are no public methods or if this {@code Class} represents a
+     * primitive type or {@code void} then an empty array is returned.
+     *
+     * @see #getDeclaredMethods()
+     */
+    public Method[] getMethods() {
+        List<Method> methods = new ArrayList<Method>();
+        getPublicMethodsInternal(methods);
+        /*
+         * Remove duplicate methods defined by superclasses and
+         * interfaces, preferring to keep methods declared by derived
+         * types.
+         */
+        CollectionUtils.removeDuplicates(methods, Method.ORDER_BY_SIGNATURE);
+        return methods.toArray(new Method[methods.size()]);
+    }
+
+    /**
+     * Populates {@code result} with public methods defined by this class, its
+     * superclasses, and all implemented interfaces, including overridden methods.
+     */
+    private void getPublicMethodsInternal(List<Method> result) {
+        getDeclaredMethods(true, result);
+        if (!isInterface()) {
+            // Search superclasses, for interfaces don't search java.lang.Object.
+            for (Class<?> c = superClass; c != null; c = c.superClass) {
+                c.getDeclaredMethods(true, result);
+            }
+        }
+        // Search iftable which has a flattened and uniqued list of interfaces.
+        Object[] iftable = ifTable;
+        if (iftable != null) {
+            for (int i = 0; i < iftable.length; i += 2) {
+                Class<?> ifc = (Class<?>) iftable[i];
+                ifc.getDeclaredMethods(true, result);
+            }
+        }
+    }
+
+    /**
+     * Returns the annotations that are directly defined on the class
+     * represented by this {@code Class}. Annotations that are inherited are not
+     * included in the result. If there are no annotations at all, an empty
+     * array is returned.
+     *
+     * @see #getAnnotations()
+     */
+    @Override public Annotation[] getDeclaredAnnotations() {
+        List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+        return result.toArray(new Annotation[result.size()]);
+    }
+
+    /**
+     * Returns an array containing {@code Class} objects for all classes,
+     * interfaces, enums and annotations that are members of this class.
+     */
+    public Class<?>[] getDeclaredClasses() {
+        return AnnotationAccess.getMemberClasses(this);
+    }
+
+    /**
+     * Returns a {@code Field} object for the field with the given name
+     * which is declared in the class represented by this {@code Class}.
+     *
+     * @throws NoSuchFieldException if the requested field can not be found.
+     * @see #getField(String)
+     */
+    public Field getDeclaredField(String name) throws NoSuchFieldException {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        Field result = getDeclaredFieldInternal(name);
+        if (result == null) {
+            throw new NoSuchFieldException(name);
+        } else {
+            result.getType();  // Throw NoClassDefFoundError if type cannot be resolved.
+        }
+        return result;
+    }
+
+    /**
+     * Returns an array containing {@code Field} objects for all fields declared
+     * in the class represented by this {@code Class}. If there are no fields or
+     * if this {@code Class} represents an array class, a primitive type or void
+     * then an empty array is returned.
+     *
+     * @see #getFields()
+     */
+    public Field[] getDeclaredFields() {
+        int initial_size = sFields == null ? 0 : sFields.length;
+        initial_size += iFields == null ? 0 : iFields.length;
+        ArrayList<Field> fields = new ArrayList(initial_size);
+        getDeclaredFields(false, fields);
+        Field[] result = fields.toArray(new Field[fields.size()]);
+        for (Field f : result) {
+            f.getType();  // Throw NoClassDefFoundError if type cannot be resolved.
+        }
+        return result;
+    }
+
+    private void getDeclaredFields(boolean publicOnly, List<Field> fields) {
+        if (iFields != null) {
+            for (ArtField f : iFields) {
+                if (!publicOnly || Modifier.isPublic(f.getAccessFlags())) {
+                    fields.add(new Field(f));
+                }
+            }
+        }
+        if (sFields != null) {
+            for (ArtField f : sFields) {
+                if (!publicOnly || Modifier.isPublic(f.getAccessFlags())) {
+                    fields.add(new Field(f));
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the field if it is defined by this class; null otherwise. This
+     * may return a non-public member.
+     */
+    private Field getDeclaredFieldInternal(String name) {
+        if (iFields != null) {
+            for (ArtField f : iFields) {
+                if (f.getName().equals(name)) {
+                    return new Field(f);
+                }
+            }
+        }
+        if (sFields != null) {
+            for (ArtField f : sFields) {
+                if (f.getName().equals(name)) {
+                    return new Field(f);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the class that this class is a member of, or {@code null} if this
+     * class is a top-level class, a primitive, an array, or defined within a
+     * method or constructor.
+     */
+    public Class<?> getDeclaringClass() {
+        return AnnotationAccess.getDeclaringClass(this);
+    }
+
+    /**
+     * Returns the class enclosing this class. For most classes this is the same
+     * as the {@link #getDeclaringClass() declaring class}. For classes defined
+     * within a method or constructor (typically anonymous inner classes), this
+     * is the declaring class of that member.
+     */
+    public Class<?> getEnclosingClass() {
+        Class<?> declaringClass = getDeclaringClass();
+        if (declaringClass != null) {
+            return declaringClass;
+        }
+        AccessibleObject member = AnnotationAccess.getEnclosingMethodOrConstructor(this);
+        return member != null
+                ? ((Member) member).getDeclaringClass()
+                : null;
+    }
+
+    /**
+     * Returns the enclosing {@code Constructor} of this {@code Class}, if it is an
+     * anonymous or local/automatic class; otherwise {@code null}.
+     */
+    public Constructor<?> getEnclosingConstructor() {
+        if (classNameImpliesTopLevel()) {
+            return null;
+        }
+        AccessibleObject result = AnnotationAccess.getEnclosingMethodOrConstructor(this);
+        return result instanceof Constructor ? (Constructor<?>) result : null;
+    }
+
+    /**
+     * Returns the enclosing {@code Method} of this {@code Class}, if it is an
+     * anonymous or local/automatic class; otherwise {@code null}.
+     */
+    public Method getEnclosingMethod() {
+        if (classNameImpliesTopLevel()) {
+            return null;
+        }
+        AccessibleObject result = AnnotationAccess.getEnclosingMethodOrConstructor(this);
+        return result instanceof Method ? (Method) result : null;
+    }
+
+    /**
+     * Returns true if this class is definitely a top level class, or false if
+     * a more expensive check like {@link #getEnclosingClass()} is necessary.
+     *
+     * <p>This is a hack that exploits an implementation detail of all Java
+     * language compilers: generated names always contain "$". As it is possible
+     * for a top level class to be named with a "$", a false result <strong>does
+     * not</strong> indicate that this isn't a top-level class.
+     */
+    private boolean classNameImpliesTopLevel() {
+        return !getName().contains("$");
+    }
+
+    /**
+     * Returns the {@code enum} constants associated with this {@code Class}.
+     * Returns {@code null} if this {@code Class} does not represent an {@code
+     * enum} type.
+     */
+    @SuppressWarnings("unchecked") // we only cast after confirming that this class is an enum
+    public T[] getEnumConstants() {
+        if (!isEnum()) {
+            return null;
+        }
+        return (T[]) Enum.getSharedConstants((Class) this).clone();
+    }
+
+    /**
+     * Returns a {@code Field} object which represents the public field with the
+     * given name. This method first searches the class C represented by
+     * this {@code Class}, then the interfaces implemented by C and finally the
+     * superclasses of C.
+     *
+     * @throws NoSuchFieldException
+     *             if the field cannot be found.
+     * @see #getDeclaredField(String)
+     */
+    public Field getField(String name) throws NoSuchFieldException {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        Field result = getPublicFieldRecursive(name);
+        if (result == null) {
+            throw new NoSuchFieldException(name);
+        } else {
+            result.getType();  // Throw NoClassDefFoundError if type cannot be resolved.
+        }
+        return result;
+    }
+
+    private Field getPublicFieldRecursive(String name) {
+        // search superclasses
+        for (Class<?> c = this; c != null; c = c.superClass) {
+            Field result = c.getDeclaredFieldInternal(name);
+            if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+                return result;
+            }
+        }
+
+        // search iftable which has a flattened and uniqued list of interfaces
+        if (ifTable != null) {
+            for (int i = 0; i < ifTable.length; i += 2) {
+                Class<?> ifc = (Class<?>) ifTable[i];
+                Field result = ifc.getPublicFieldRecursive(name);
+                if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+                    return result;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns an array containing {@code Field} objects for all public fields
+     * for the class C represented by this {@code Class}. Fields may be declared
+     * in C, the interfaces it implements or in the superclasses of C. The
+     * elements in the returned array are in no particular order.
+     *
+     * <p>If there are no public fields or if this class represents an array class,
+     * a primitive type or {@code void} then an empty array is returned.
+     *
+     * @see #getDeclaredFields()
+     */
+    public Field[] getFields() {
+        List<Field> fields = new ArrayList<Field>();
+        getPublicFieldsRecursive(fields);
+        Field[] result = fields.toArray(new Field[fields.size()]);
+        for (Field f : result) {
+            f.getType();  // Throw NoClassDefFoundError if type cannot be resolved.
+        }
+        return result;
+    }
+
+    /**
+     * Populates {@code result} with public fields defined by this class, its
+     * superclasses, and all implemented interfaces.
+     */
+    private void getPublicFieldsRecursive(List<Field> result) {
+        // search superclasses
+        for (Class<?> c = this; c != null; c = c.superClass) {
+            c.getDeclaredFields(true, result);
+        }
+
+        // search iftable which has a flattened and uniqued list of interfaces
+        Object[] iftable = ifTable;
+        if (iftable != null) {
+            for (int i = 0; i < iftable.length; i += 2) {
+                Class<?> ifc = (Class<?>) iftable[i];
+                ifc.getDeclaredFields(true, result);
+            }
+        }
+    }
+
+    /**
+     * Returns the {@link Type}s of the interfaces that this {@code Class} directly
+     * implements. If the {@code Class} represents a primitive type or {@code
+     * void} then an empty array is returned.
+     */
+    public Type[] getGenericInterfaces() {
+        Type[] result;
+        synchronized (Caches.genericInterfaces) {
+            result = Caches.genericInterfaces.get(this);
+            if (result == null) {
+                String annotationSignature = AnnotationAccess.getSignature(this);
+                if (annotationSignature == null) {
+                    result = getInterfaces();
+                } else {
+                    GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+                    parser.parseForClass(this, annotationSignature);
+                    result = Types.getTypeArray(parser.interfaceTypes, false);
+                }
+                Caches.genericInterfaces.put(this, result);
+            }
+        }
+        return (result.length == 0) ? result : result.clone();
+    }
+
+    /**
+     * Returns the {@code Type} that represents the superclass of this {@code
+     * class}.
+     */
+    public Type getGenericSuperclass() {
+        Type genericSuperclass = getSuperclass();
+        String annotationSignature = AnnotationAccess.getSignature(this);
+        if (annotationSignature != null) {
+            GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+            parser.parseForClass(this, annotationSignature);
+            genericSuperclass = parser.superclassType;
+        }
+        return Types.getType(genericSuperclass);
+    }
+
+    /**
+     * Returns an array of {@code Class} objects that match the interfaces
+     * in the {@code implements} declaration of the class represented
+     * by this {@code Class}. The order of the elements in the array is
+     * identical to the order in the original class declaration. If the class
+     * does not implement any interfaces, an empty array is returned.
+     *
+     * <p>This method only returns directly-implemented interfaces, and does not
+     * include interfaces implemented by superclasses or superinterfaces of any
+     * implemented interfaces.
+     */
+    public Class<?>[] getInterfaces() {
+        if (isArray()) {
+            return new Class<?>[] { Cloneable.class, Serializable.class };
+        } else if (isProxy()) {
+            return getProxyInterfaces();
+        }
+        Dex dex = getDex();
+        if (dex == null) {
+            return EmptyArray.CLASS;
+        }
+        short[] interfaces = dex.interfaceTypeIndicesFromClassDefIndex(dexClassDefIndex);
+        Class<?>[] result = new Class<?>[interfaces.length];
+        for (int i = 0; i < interfaces.length; i++) {
+            result[i] = getDexCacheType(dex, interfaces[i]);
+        }
+        return result;
+    }
+
+    // Returns the interfaces that this proxy class directly implements.
+    private native Class<?>[] getProxyInterfaces();
+
+    /**
+     * Returns an integer that represents the modifiers of the class represented
+     * by this {@code Class}. The returned value is a combination of bits
+     * defined by constants in the {@link Modifier} class.
+     */
+    public int getModifiers() {
+        // Array classes inherit modifiers from their component types, but in the case of arrays
+        // of an inner class, the class file may contain "fake" access flags because it's not valid
+        // for a top-level class to private, say. The real access flags are stored in the InnerClass
+        // attribute, so we need to make sure we drill down to the inner class: the accessFlags
+        // field is not the value we want to return, and the synthesized array class does not itself
+        // have an InnerClass attribute. https://code.google.com/p/android/issues/detail?id=56267
+        if (isArray()) {
+            int componentModifiers = getComponentType().getModifiers();
+            if ((componentModifiers & Modifier.INTERFACE) != 0) {
+                componentModifiers &= ~(Modifier.INTERFACE | Modifier.STATIC);
+            }
+            return Modifier.ABSTRACT | Modifier.FINAL | componentModifiers;
+        }
+        int JAVA_FLAGS_MASK = 0xffff;
+        int modifiers = AnnotationAccess.getInnerClassFlags(this, accessFlags & JAVA_FLAGS_MASK);
+        return modifiers & JAVA_FLAGS_MASK;
+    }
+
+    /**
+     * Returns the name of the class represented by this {@code Class}. For a
+     * description of the format which is used, see the class definition of
+     * {@link Class}.
+     */
+    public String getName() {
+        String result = name;
+        return (result == null) ? (name = getNameNative()) : result;
+    }
+
+    private native String getNameNative();
+
+    /**
+     * Returns the simple name of the class represented by this {@code Class} as
+     * defined in the source code. If there is no name (that is, the class is
+     * anonymous) then an empty string is returned. If the receiver is an array
+     * then the name of the underlying type with square braces appended (for
+     * example {@code "Integer[]"}) is returned.
+     *
+     * @return the simple name of the class represented by this {@code Class}.
+     */
+    public String getSimpleName() {
+        if (isArray()) {
+            return getComponentType().getSimpleName() + "[]";
+        }
+
+        if (isAnonymousClass()) {
+            return "";
+        }
+
+        if (isMemberClass() || isLocalClass()) {
+            return getInnerClassName();
+        }
+
+        String name = getName();
+        int dot = name.lastIndexOf('.');
+        if (dot != -1) {
+            return name.substring(dot + 1);
+        }
+
+        return name;
+    }
+
+    /**
+     * Returns the simple name of a member or local class, or null otherwise.
+     */
+    private String getInnerClassName() {
+        return AnnotationAccess.getInnerClassName(this);
+    }
+
+    /**
+     * Returns null.
+     */
+    public ProtectionDomain getProtectionDomain() {
+        return null;
+    }
+
+    /**
+     * Returns the URL of the given resource, or null if the resource is not found.
+     * The mapping between the resource name and the URL is managed by the class' class loader.
+     *
+     * @see ClassLoader
+     */
+    public URL getResource(String resourceName) {
+        // Get absolute resource name, but without the leading slash
+        if (resourceName.startsWith("/")) {
+            resourceName = resourceName.substring(1);
+        } else {
+            String pkg = getName();
+            int dot = pkg.lastIndexOf('.');
+            if (dot != -1) {
+                pkg = pkg.substring(0, dot).replace('.', '/');
+            } else {
+                pkg = "";
+            }
+
+            resourceName = pkg + "/" + resourceName;
+        }
+
+        // Delegate to proper class loader
+        ClassLoader loader = getClassLoader();
+        if (loader != null) {
+            return loader.getResource(resourceName);
+        } else {
+            return ClassLoader.getSystemResource(resourceName);
+        }
+    }
+
+    /**
+     * Returns a read-only stream for the contents of the given resource, or null if the resource
+     * is not found.
+     * The mapping between the resource name and the stream is managed by the class' class loader.
+     *
+     * @see ClassLoader
+     */
+    public InputStream getResourceAsStream(String resourceName) {
+        // Get absolute resource name, but without the leading slash
+        if (resourceName.startsWith("/")) {
+            resourceName = resourceName.substring(1);
+        } else {
+            String pkg = getName();
+            int dot = pkg.lastIndexOf('.');
+            if (dot != -1) {
+                pkg = pkg.substring(0, dot).replace('.', '/');
+            } else {
+                pkg = "";
+            }
+
+            resourceName = pkg + "/" + resourceName;
+        }
+
+        // Delegate to proper class loader
+        ClassLoader loader = getClassLoader();
+        if (loader != null) {
+            return loader.getResourceAsStream(resourceName);
+        } else {
+            return ClassLoader.getSystemResourceAsStream(resourceName);
+        }
+    }
+
+    /**
+     * Returns null. (On Android, a {@code ClassLoader} can load classes from multiple dex files.
+     * All classes from any given dex file will have the same signers, but different dex
+     * files may have different signers. This does not fit well with the original
+     * {@code ClassLoader}-based model of {@code getSigners}.)
+     */
+    public Object[] getSigners() {
+        // See http://code.google.com/p/android/issues/detail?id=1766.
+        return null;
+    }
+
+    /**
+     * Returns the {@code Class} object which represents the superclass of the
+     * class represented by this {@code Class}. If this {@code Class} represents
+     * the {@code Object} class, a primitive type, an interface or void then the
+     * method returns {@code null}. If this {@code Class} represents an array
+     * class then the {@code Object} class is returned.
+     */
+    public Class<? super T> getSuperclass() {
+      // For interfaces superClass is Object (which agrees with the JNI spec)
+      // but not with the expected behavior here.
+      if (isInterface()) {
+        return null;
+      } else {
+        return superClass;
+      }
+    }
+
+    /**
+     * Returns an array containing {@code TypeVariable} objects for type
+     * variables declared by the generic class represented by this {@code
+     * Class}. Returns an empty array if the class is not generic.
+     */
+    @SuppressWarnings("unchecked")
+    @Override public synchronized TypeVariable<Class<T>>[] getTypeParameters() {
+        String annotationSignature = AnnotationAccess.getSignature(this);
+        if (annotationSignature == null) {
+            return EmptyArray.TYPE_VARIABLE;
+        }
+        GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+        parser.parseForClass(this, annotationSignature);
+        return parser.formalTypeParameters;
+    }
+
+    /**
+     * Tests whether this {@code Class} represents an annotation class.
+     */
+    public boolean isAnnotation() {
+        final int ACC_ANNOTATION = 0x2000;  // not public in reflect.Modifier
+        return (accessFlags & ACC_ANNOTATION) != 0;
+    }
+
+    @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+        return AnnotationAccess.isAnnotationPresent(this, annotationType);
+    }
+
+    /**
+     * Tests whether the class represented by this {@code Class} is
+     * anonymous.
+     */
+    public boolean isAnonymousClass() {
+        return AnnotationAccess.isAnonymousClass(this);
+    }
+
+    /**
+     * Tests whether the class represented by this {@code Class} is an array class.
+     */
+    public boolean isArray() {
+        return getComponentType() != null;
+    }
+
+    /**
+     * Is this a runtime created proxy class?
+     *
+     * @hide
+     */
+    public boolean isProxy() {
+        return (accessFlags & 0x00040000) != 0;
+    }
+
+    /**
+     * Can {@code c}  be assigned to this class? For example, String can be assigned to Object
+     * (by an upcast), however, an Object cannot be assigned to a String as a potentially exception
+     * throwing downcast would be necessary. Similarly for interfaces, a class that implements (or
+     * an interface that extends) another can be assigned to its parent, but not vice-versa. All
+     * Classes may assign to themselves. Classes for primitive types may not assign to each other.
+     *
+     * @param c the class to check.
+     * @return {@code true} if {@code c} can be assigned to the class
+     *         represented by this {@code Class}; {@code false} otherwise.
+     * @throws NullPointerException if {@code c} is {@code null}.
+     */
+    public boolean isAssignableFrom(Class<?> c) {
+        if (this == c) {
+            return true;  // Can always assign to things of the same type.
+        } else if (this == Object.class) {
+            return !c.isPrimitive();  // Can assign any reference to java.lang.Object.
+        } else if (isArray()) {
+            return c.isArray() && componentType.isAssignableFrom(c.componentType);
+        } else if (isInterface()) {
+            // Search iftable which has a flattened and uniqued list of interfaces.
+            Object[] iftable = c.ifTable;
+            if (iftable != null) {
+                for (int i = 0; i < iftable.length; i += 2) {
+                    Class<?> ifc = (Class<?>) iftable[i];
+                    if (ifc == this) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        } else {
+            if (!c.isInterface()) {
+                for (c = c.superClass; c != null; c = c.superClass) {
+                    if (c == this) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Tests whether the class represented by this {@code Class} is an
+     * {@code enum}.
+     */
+    public boolean isEnum() {
+        return (getSuperclass() == Enum.class) && ((accessFlags & 0x4000) != 0);
+    }
+
+    /**
+     * Tests whether the given object can be cast to the class
+     * represented by this {@code Class}. This is the runtime version of the
+     * {@code instanceof} operator.
+     *
+     * @return {@code true} if {@code object} can be cast to the type
+     *         represented by this {@code Class}; {@code false} if {@code
+     *         object} is {@code null} or cannot be cast.
+     */
+    public boolean isInstance(Object object) {
+        if (object == null) {
+            return false;
+        }
+        return isAssignableFrom(object.getClass());
+    }
+
+    /**
+     * Tests whether this {@code Class} represents an interface.
+     */
+    public boolean isInterface() {
+      return (accessFlags & Modifier.INTERFACE) != 0;
+    }
+
+    /**
+     * Tests whether the class represented by this {@code Class} is defined
+     * locally.
+     */
+    public boolean isLocalClass() {
+        return !classNameImpliesTopLevel()
+                && AnnotationAccess.getEnclosingMethodOrConstructor(this) != null
+                && !isAnonymousClass();
+    }
+
+    /**
+     * Tests whether the class represented by this {@code Class} is a member
+     * class.
+     */
+    public boolean isMemberClass() {
+        return getDeclaringClass() != null;
+    }
+
+    /**
+     * Tests whether this {@code Class} represents a primitive type.
+     */
+    public boolean isPrimitive() {
+      return primitiveType != 0;
+    }
+
+    /**
+     * Tests whether this {@code Class} represents a synthetic type.
+     */
+    public boolean isSynthetic() {
+        final int ACC_SYNTHETIC = 0x1000;   // not public in reflect.Modifier
+        return (accessFlags & ACC_SYNTHETIC) != 0;
+    }
+
+    /**
+     * Indicates whether this {@code Class} or its parents override finalize.
+     *
+     * @hide
+     */
+    public boolean isFinalizable() {
+      final int ACC_CLASS_IS_FINALIZABLE = 0x80000000;  // not public in reflect.Modifier
+      return (accessFlags & ACC_CLASS_IS_FINALIZABLE) != 0;
+    }
+
+    /**
+     * Returns a new instance of the class represented by this {@code Class},
+     * created by invoking the default (that is, zero-argument) constructor. If
+     * there is no such constructor, or if the creation fails (either because of
+     * a lack of available memory or because an exception is thrown by the
+     * constructor), an {@code InstantiationException} is thrown. If the default
+     * constructor exists but is not accessible from the context where this
+     * method is invoked, an {@code IllegalAccessException} is thrown.
+     *
+     * @throws IllegalAccessException
+     *             if the default constructor is not visible.
+     * @throws InstantiationException
+     *             if the instance cannot be created.
+     */
+    public T newInstance() throws InstantiationException, IllegalAccessException {
+        if (isPrimitive() || isInterface() || isArray() || Modifier.isAbstract(accessFlags)) {
+            throw new InstantiationException(this + " cannot be instantiated");
+        }
+        Class<?> caller = VMStack.getStackClass1();
+        if (!caller.canAccess(this)) {
+            throw new IllegalAccessException(this + " is not accessible from " + caller);
+        }
+        Constructor<T> init;
+        try {
+            init = getDeclaredConstructor();
+        } catch (NoSuchMethodException e) {
+            InstantiationException t =
+                new InstantiationException(this + " has no zero argument constructor");
+            t.initCause(e);
+            throw t;
+        }
+        if (!caller.canAccessMember(this, init.getAccessFlags())) {
+            throw new IllegalAccessException(init + " is not accessible from " + caller);
+        }
+        try {
+            return init.newInstance();
+        } catch (InvocationTargetException e) {
+            InstantiationException t = new InstantiationException(this);
+            t.initCause(e);
+            throw t;
+        }
+    }
+
+    private boolean canAccess(Class<?> c) {
+        if(Modifier.isPublic(c.accessFlags)) {
+            return true;
+        }
+        return inSamePackage(c);
+    }
+
+    private boolean canAccessMember(Class<?> memberClass, int memberModifiers) {
+        if (memberClass == this || Modifier.isPublic(memberModifiers)) {
+            return true;
+        }
+        if (Modifier.isPrivate(memberModifiers)) {
+            return false;
+        }
+        if (Modifier.isProtected(memberModifiers)) {
+            for (Class<?> parent = this.superClass; parent != null; parent = parent.superClass) {
+                if (parent == memberClass) {
+                    return true;
+                }
+            }
+        }
+        return inSamePackage(memberClass);
+    }
+
+    private boolean inSamePackage(Class<?> c) {
+        if (classLoader != c.classLoader) {
+            return false;
+        }
+        String packageName1 = getPackageName$();
+        String packageName2 = c.getPackageName$();
+        if (packageName1 == null) {
+            return packageName2 == null;
+        } else if (packageName2 == null) {
+            return false;
+        } else {
+            return packageName1.equals(packageName2);
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (isPrimitive()) {
+            return getSimpleName();
+        } else {
+            return (isInterface() ? "interface " : "class ") + getName();
+        }
+    }
+
+    /**
+     * Returns the {@code Package} of which the class represented by this
+     * {@code Class} is a member. Returns {@code null} if no {@code Package}
+     * object was created by the class loader of the class.
+     */
+    public Package getPackage() {
+        // TODO This might be a hack, but the VM doesn't have the necessary info.
+        ClassLoader loader = getClassLoader();
+        if (loader != null) {
+            String packageName = getPackageName$();
+            return packageName != null ? loader.getPackage(packageName) : null;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the package name of this class. This returns null for classes in
+     * the default package.
+     *
+     * @hide
+     */
+    public String getPackageName$() {
+        String name = getName();
+        int last = name.lastIndexOf('.');
+        return last == -1 ? null : name.substring(0, last);
+    }
+
+    /**
+     * Returns the assertion status for the class represented by this {@code
+     * Class}. Assertion is enabled / disabled based on the class loader,
+     * package or class default at runtime.
+     */
+    public boolean desiredAssertionStatus() {
+      return false;
+    }
+
+    /**
+     * Casts this {@code Class} to represent a subclass of the given class.
+     * If successful, this {@code Class} is returned; otherwise a {@code
+     * ClassCastException} is thrown.
+     *
+     * @throws ClassCastException
+     *             if this {@code Class} cannot be cast to the given type.
+     */
+    @SuppressWarnings("unchecked")
+    public <U> Class<? extends U> asSubclass(Class<U> c) {
+        if (c.isAssignableFrom(this)) {
+            return (Class<? extends U>)this;
+        }
+        String actualClassName = this.getName();
+        String desiredClassName = c.getName();
+        throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
+    }
+
+    /**
+     * Casts the given object to the type represented by this {@code Class}.
+     * If the object is {@code null} then the result is also {@code null}.
+     *
+     * @throws ClassCastException
+     *             if the object cannot be cast to the given type.
+     */
+    @SuppressWarnings("unchecked")
+    public T cast(Object obj) {
+        if (obj == null) {
+            return null;
+        } else if (this.isInstance(obj)) {
+            return (T)obj;
+        }
+        String actualClassName = obj.getClass().getName();
+        String desiredClassName = this.getName();
+        throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
+    }
+
+    /**
+     * The class def of this class in its own Dex, or -1 if there is no class def.
+     *
+     * @hide
+     */
+    public int getDexClassDefIndex() {
+        return (dexClassDefIndex == 65535) ? -1 : dexClassDefIndex;
+    }
+
+    /**
+     * The type index of this class in its own Dex, or -1 if it is unknown. If a class is referenced
+     * by multiple Dex files, it will have a different type index in each. Dex files support 65534
+     * type indices, with 65535 representing no index.
+     *
+     * @hide
+     */
+    public int getDexTypeIndex() {
+        int typeIndex = dexTypeIndex;
+        if (typeIndex != 65535) {
+            return typeIndex;
+        }
+        synchronized (this) {
+            typeIndex = dexTypeIndex;
+            if (typeIndex == 65535) {
+                if (dexClassDefIndex >= 0) {
+                    typeIndex = getDex().typeIndexFromClassDefIndex(dexClassDefIndex);
+                } else {
+                    typeIndex = getDex().findTypeIndex(InternalNames.getInternalName(this));
+                    if (typeIndex < 0) {
+                        typeIndex = -1;
+                    }
+                }
+                dexTypeIndex = typeIndex;
+            }
+        }
+        return typeIndex;
+    }
+
+    /**
+     * The annotation directory offset of this class in its own Dex, or 0 if it
+     * is unknown.
+     *
+     * TODO: 0 is a sentinel that means 'no annotations directory'; this should be -1 if unknown
+     *
+     * @hide
+     */
+    public int getDexAnnotationDirectoryOffset() {
+        Dex dex = getDex();
+        if (dex == null) {
+            return 0;
+        }
+        int classDefIndex = getDexClassDefIndex();
+        if (classDefIndex < 0) {
+            return 0;
+        }
+        return dex.annotationDirectoryOffsetFromClassDefIndex(classDefIndex);
+    }
+
+    private static class Caches {
+        /**
+         * Cache to avoid frequent recalculation of generic interfaces, which is generally uncommon.
+         * Sized sufficient to allow ConcurrentHashMapTest to run without recalculating its generic
+         * interfaces (required to avoid time outs). Validated by running reflection heavy code
+         * such as applications using Guice-like frameworks.
+         */
+        private static final BasicLruCache<Class, Type[]> genericInterfaces
+            = new BasicLruCache<Class, Type[]>(8);
+    }
+}
diff --git a/libart/src/main/java/java/lang/ClassLoader.java b/libart/src/main/java/java/lang/ClassLoader.java
new file mode 100644
index 0000000..fb2eb8f
--- /dev/null
+++ b/libart/src/main/java/java/lang/ClassLoader.java
@@ -0,0 +1,856 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang;
+
+import dalvik.system.PathClassLoader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.security.ProtectionDomain;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Loads classes and resources from a repository. One or more class loaders are
+ * installed at runtime. These are consulted whenever the runtime system needs a
+ * specific class that is not yet available in-memory. Typically, class loaders
+ * are grouped into a tree where child class loaders delegate all requests to
+ * parent class loaders. Only if the parent class loader cannot satisfy the
+ * request, the child class loader itself tries to handle it.
+ * <p>
+ * {@code ClassLoader} is an abstract class that implements the common
+ * infrastructure required by all class loaders. Android provides several
+ * concrete implementations of the class, with
+ * {@link dalvik.system.PathClassLoader} being the one typically used. Other
+ * applications may implement subclasses of {@code ClassLoader} to provide
+ * special ways for loading classes.
+ * </p>
+ * @see Class
+ */
+public abstract class ClassLoader {
+
+    /**
+     * The 'System' ClassLoader - the one that is responsible for loading
+     * classes from the classpath. It is not equal to the bootstrap class loader -
+     * that one handles the built-in classes.
+     *
+     * Because of a potential class initialization race between ClassLoader and
+     * java.lang.System, reproducible when using JDWP with "suspend=y", we defer
+     * creation of the system class loader until first use. We use a static
+     * inner class to get synchronization at init time without having to sync on
+     * every access.
+     *
+     * @see #getSystemClassLoader()
+     */
+    static private class SystemClassLoader {
+        public static ClassLoader loader = ClassLoader.createSystemClassLoader();
+    }
+
+    /**
+     * The parent ClassLoader.
+     */
+    private ClassLoader parent;
+
+    /**
+     * The packages known to the class loader.
+     */
+    private Map<String, Package> packages = new HashMap<String, Package>();
+
+    /**
+     * To avoid unloading individual classes, {@link java.lang.reflect.Proxy}
+     * only generates one class for each set of interfaces. This maps sets of
+     * interfaces to the proxy class that implements all of them. It is declared
+     * here so that these generated classes can be unloaded with their class
+     * loader.
+     *
+     * @hide
+     */
+    public final Map<Set<Class<?>>, Class<?>> proxyCache
+            = Collections.synchronizedMap(new HashMap<Set<Class<?>>, Class<?>>());
+
+    /**
+     * Create the system class loader. Note this is NOT the bootstrap class
+     * loader (which is managed by the VM). We use a null value for the parent
+     * to indicate that the bootstrap loader is our parent.
+     */
+    private static ClassLoader createSystemClassLoader() {
+        String classPath = System.getProperty("java.class.path", ".");
+
+        // String[] paths = classPath.split(":");
+        // URL[] urls = new URL[paths.length];
+        // for (int i = 0; i < paths.length; i++) {
+        // try {
+        // urls[i] = new URL("file://" + paths[i]);
+        // }
+        // catch (Exception ex) {
+        // ex.printStackTrace();
+        // }
+        // }
+        //
+        // return new java.net.URLClassLoader(urls, null);
+
+        // TODO Make this a java.net.URLClassLoader once we have those?
+        return new PathClassLoader(classPath, BootClassLoader.getInstance());
+    }
+
+    /**
+     * Returns the system class loader. This is the parent for new
+     * {@code ClassLoader} instances and is typically the class loader used to
+     * start the application.
+     */
+    public static ClassLoader getSystemClassLoader() {
+        return SystemClassLoader.loader;
+    }
+
+    /**
+     * Finds the URL of the resource with the specified name. The system class
+     * loader's resource lookup algorithm is used to find the resource.
+     *
+     * @return the {@code URL} object for the requested resource or {@code null}
+     *         if the resource can not be found.
+     * @param resName
+     *            the name of the resource to find.
+     * @see Class#getResource
+     */
+    public static URL getSystemResource(String resName) {
+        return SystemClassLoader.loader.getResource(resName);
+    }
+
+    /**
+     * Returns an enumeration of URLs for the resource with the specified name.
+     * The system class loader's resource lookup algorithm is used to find the
+     * resource.
+     *
+     * @return an enumeration of {@code URL} objects containing the requested
+     *         resources.
+     * @param resName
+     *            the name of the resource to find.
+     * @throws IOException
+     *             if an I/O error occurs.
+     */
+    public static Enumeration<URL> getSystemResources(String resName) throws IOException {
+        return SystemClassLoader.loader.getResources(resName);
+    }
+
+    /**
+     * Returns a stream for the resource with the specified name. The system
+     * class loader's resource lookup algorithm is used to find the resource.
+     * Basically, the contents of the java.class.path are searched in order,
+     * looking for a path which matches the specified resource.
+     *
+     * @return a stream for the resource or {@code null}.
+     * @param resName
+     *            the name of the resource to find.
+     * @see Class#getResourceAsStream
+     */
+    public static InputStream getSystemResourceAsStream(String resName) {
+        return SystemClassLoader.loader.getResourceAsStream(resName);
+    }
+
+    /**
+     * Constructs a new instance of this class with the system class loader as
+     * its parent.
+     */
+    protected ClassLoader() {
+        this(getSystemClassLoader(), false);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified class loader
+     * as its parent.
+     *
+     * @param parentLoader
+     *            The {@code ClassLoader} to use as the new class loader's
+     *            parent.
+     */
+    protected ClassLoader(ClassLoader parentLoader) {
+        this(parentLoader, false);
+    }
+
+    /*
+     * constructor for the BootClassLoader which needs parent to be null.
+     */
+    ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
+        if (parentLoader == null && !nullAllowed) {
+            throw new NullPointerException("parentLoader == null && !nullAllowed");
+        }
+        parent = parentLoader;
+    }
+
+    /**
+     * Constructs a new class from an array of bytes containing a class
+     * definition in class file format.
+     *
+     * @param classRep
+     *            the memory image of a class file.
+     * @param offset
+     *            the offset into {@code classRep}.
+     * @param length
+     *            the length of the class file.
+     * @return the {@code Class} object created from the specified subset of
+     *         data in {@code classRep}.
+     * @throws ClassFormatError
+     *             if {@code classRep} does not contain a valid class.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0}, {@code length < 0} or if
+     *             {@code offset + length} is greater than the length of
+     *             {@code classRep}.
+     * @deprecated Use {@link #defineClass(String, byte[], int, int)}
+     */
+    @Deprecated
+    protected final Class<?> defineClass(byte[] classRep, int offset, int length)
+            throws ClassFormatError {
+        throw new UnsupportedOperationException("can't load this type of class file");
+    }
+
+    /**
+     * Constructs a new class from an array of bytes containing a class
+     * definition in class file format.
+     *
+     * @param className
+     *            the expected name of the new class, may be {@code null} if not
+     *            known.
+     * @param classRep
+     *            the memory image of a class file.
+     * @param offset
+     *            the offset into {@code classRep}.
+     * @param length
+     *            the length of the class file.
+     * @return the {@code Class} object created from the specified subset of
+     *         data in {@code classRep}.
+     * @throws ClassFormatError
+     *             if {@code classRep} does not contain a valid class.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0}, {@code length < 0} or if
+     *             {@code offset + length} is greater than the length of
+     *             {@code classRep}.
+     */
+    protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length)
+            throws ClassFormatError {
+        throw new UnsupportedOperationException("can't load this type of class file");
+    }
+
+    /**
+     * Constructs a new class from an array of bytes containing a class
+     * definition in class file format and assigns the specified protection
+     * domain to the new class. If the provided protection domain is
+     * {@code null} then a default protection domain is assigned to the class.
+     *
+     * @param className
+     *            the expected name of the new class, may be {@code null} if not
+     *            known.
+     * @param classRep
+     *            the memory image of a class file.
+     * @param offset
+     *            the offset into {@code classRep}.
+     * @param length
+     *            the length of the class file.
+     * @param protectionDomain
+     *            the protection domain to assign to the loaded class, may be
+     *            {@code null}.
+     * @return the {@code Class} object created from the specified subset of
+     *         data in {@code classRep}.
+     * @throws ClassFormatError
+     *             if {@code classRep} does not contain a valid class.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0}, {@code length < 0} or if
+     *             {@code offset + length} is greater than the length of
+     *             {@code classRep}.
+     * @throws NoClassDefFoundError
+     *             if {@code className} is not equal to the name of the class
+     *             contained in {@code classRep}.
+     */
+    protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length,
+            ProtectionDomain protectionDomain) throws java.lang.ClassFormatError {
+        throw new UnsupportedOperationException("can't load this type of class file");
+    }
+
+    /**
+     * Defines a new class with the specified name, byte code from the byte
+     * buffer and the optional protection domain. If the provided protection
+     * domain is {@code null} then a default protection domain is assigned to
+     * the class.
+     *
+     * @param name
+     *            the expected name of the new class, may be {@code null} if not
+     *            known.
+     * @param b
+     *            the byte buffer containing the byte code of the new class.
+     * @param protectionDomain
+     *            the protection domain to assign to the loaded class, may be
+     *            {@code null}.
+     * @return the {@code Class} object created from the data in {@code b}.
+     * @throws ClassFormatError
+     *             if {@code b} does not contain a valid class.
+     * @throws NoClassDefFoundError
+     *             if {@code className} is not equal to the name of the class
+     *             contained in {@code b}.
+     */
+    protected final Class<?> defineClass(String name, ByteBuffer b,
+            ProtectionDomain protectionDomain) throws ClassFormatError {
+
+        byte[] temp = new byte[b.remaining()];
+        b.get(temp);
+        return defineClass(name, temp, 0, temp.length, protectionDomain);
+    }
+
+    /**
+     * Overridden by subclasses, throws a {@code ClassNotFoundException} by
+     * default. This method is called by {@code loadClass} after the parent
+     * {@code ClassLoader} has failed to find a loaded class of the same name.
+     *
+     * @param className
+     *            the name of the class to look for.
+     * @return the {@code Class} object that is found.
+     * @throws ClassNotFoundException
+     *             if the class cannot be found.
+     */
+    protected Class<?> findClass(String className) throws ClassNotFoundException {
+        throw new ClassNotFoundException(className);
+    }
+
+    /**
+     * Returns the class with the specified name if it has already been loaded
+     * by the VM or {@code null} if it has not yet been loaded.
+     *
+     * @param className
+     *            the name of the class to look for.
+     * @return the {@code Class} object or {@code null} if the requested class
+     *         has not been loaded.
+     */
+    protected final Class<?> findLoadedClass(String className) {
+        ClassLoader loader;
+        if (this == BootClassLoader.getInstance())
+            loader = null;
+        else
+            loader = this;
+        return VMClassLoader.findLoadedClass(loader, className);
+    }
+
+    /**
+     * Finds the class with the specified name, loading it using the system
+     * class loader if necessary.
+     *
+     * @param className
+     *            the name of the class to look for.
+     * @return the {@code Class} object with the requested {@code className}.
+     * @throws ClassNotFoundException
+     *             if the class can not be found.
+     */
+    protected final Class<?> findSystemClass(String className) throws ClassNotFoundException {
+        return Class.forName(className, false, getSystemClassLoader());
+    }
+
+    /**
+     * Returns this class loader's parent.
+     *
+     * @return this class loader's parent or {@code null}.
+     */
+    public final ClassLoader getParent() {
+        return parent;
+    }
+
+    /**
+     * Returns the URL of the resource with the specified name. This
+     * implementation first tries to use the parent class loader to find the
+     * resource; if this fails then {@link #findResource(String)} is called to
+     * find the requested resource.
+     *
+     * @param resName
+     *            the name of the resource to find.
+     * @return the {@code URL} object for the requested resource or {@code null}
+     *         if the resource can not be found
+     * @see Class#getResource
+     */
+    public URL getResource(String resName) {
+        URL resource = parent.getResource(resName);
+        if (resource == null) {
+            resource = findResource(resName);
+        }
+        return resource;
+    }
+
+    /**
+     * Returns an enumeration of URLs for the resource with the specified name.
+     * This implementation first uses this class loader's parent to find the
+     * resource, then it calls {@link #findResources(String)} to get additional
+     * URLs. The returned enumeration contains the {@code URL} objects of both
+     * find operations.
+     *
+     * @return an enumeration of {@code URL} objects for the requested resource.
+     * @param resName
+     *            the name of the resource to find.
+     * @throws IOException
+     *             if an I/O error occurs.
+     */
+    @SuppressWarnings("unchecked")
+    public Enumeration<URL> getResources(String resName) throws IOException {
+
+        Enumeration<URL> first = parent.getResources(resName);
+        Enumeration<URL> second = findResources(resName);
+
+        return new TwoEnumerationsInOne(first, second);
+    }
+
+    /**
+     * Returns a stream for the resource with the specified name. See
+     * {@link #getResource(String)} for a description of the lookup algorithm
+     * used to find the resource.
+     *
+     * @return a stream for the resource or {@code null} if the resource can not be found
+     * @param resName
+     *            the name of the resource to find.
+     * @see Class#getResourceAsStream
+     */
+    public InputStream getResourceAsStream(String resName) {
+        try {
+            URL url = getResource(resName);
+            if (url != null) {
+                return url.openStream();
+            }
+        } catch (IOException ex) {
+            // Don't want to see the exception.
+        }
+
+        return null;
+    }
+
+    /**
+     * Loads the class with the specified name. Invoking this method is
+     * equivalent to calling {@code loadClass(className, false)}.
+     * <p>
+     * <strong>Note:</strong> In the Android reference implementation, the
+     * second parameter of {@link #loadClass(String, boolean)} is ignored
+     * anyway.
+     * </p>
+     *
+     * @return the {@code Class} object.
+     * @param className
+     *            the name of the class to look for.
+     * @throws ClassNotFoundException
+     *             if the class can not be found.
+     */
+    public Class<?> loadClass(String className) throws ClassNotFoundException {
+        return loadClass(className, false);
+    }
+
+    /**
+     * Loads the class with the specified name, optionally linking it after
+     * loading. The following steps are performed:
+     * <ol>
+     * <li> Call {@link #findLoadedClass(String)} to determine if the requested
+     * class has already been loaded.</li>
+     * <li>If the class has not yet been loaded: Invoke this method on the
+     * parent class loader.</li>
+     * <li>If the class has still not been loaded: Call
+     * {@link #findClass(String)} to find the class.</li>
+     * </ol>
+     * <p>
+     * <strong>Note:</strong> In the Android reference implementation, the
+     * {@code resolve} parameter is ignored; classes are never linked.
+     * </p>
+     *
+     * @return the {@code Class} object.
+     * @param className
+     *            the name of the class to look for.
+     * @param resolve
+     *            Indicates if the class should be resolved after loading. This
+     *            parameter is ignored on the Android reference implementation;
+     *            classes are not resolved.
+     * @throws ClassNotFoundException
+     *             if the class can not be found.
+     */
+    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+        Class<?> clazz = findLoadedClass(className);
+
+        if (clazz == null) {
+            ClassNotFoundException suppressed = null;
+            try {
+                clazz = parent.loadClass(className, false);
+            } catch (ClassNotFoundException e) {
+                suppressed = e;
+            }
+
+            if (clazz == null) {
+                try {
+                    clazz = findClass(className);
+                } catch (ClassNotFoundException e) {
+                    e.addSuppressed(suppressed);
+                    throw e;
+                }
+            }
+        }
+
+        return clazz;
+    }
+
+    /**
+     * Forces a class to be linked (initialized). If the class has already been
+     * linked this operation has no effect.
+     * <p>
+     * <strong>Note:</strong> In the Android reference implementation, this
+     * method has no effect.
+     * </p>
+     *
+     * @param clazz
+     *            the class to link.
+     */
+    protected final void resolveClass(Class<?> clazz) {
+        // no-op, doesn't make sense on android.
+    }
+
+    /**
+     * Finds the URL of the resource with the specified name. This
+     * implementation just returns {@code null}; it should be overridden in
+     * subclasses.
+     *
+     * @param resName
+     *            the name of the resource to find.
+     * @return the {@code URL} object for the requested resource.
+     */
+    protected URL findResource(String resName) {
+        return null;
+    }
+
+    /**
+     * Finds an enumeration of URLs for the resource with the specified name.
+     * This implementation just returns an empty {@code Enumeration}; it should
+     * be overridden in subclasses.
+     *
+     * @param resName
+     *            the name of the resource to find.
+     * @return an enumeration of {@code URL} objects for the requested resource.
+     * @throws IOException
+     *             if an I/O error occurs.
+     */
+    @SuppressWarnings( {
+            "unchecked", "unused"
+    })
+    protected Enumeration<URL> findResources(String resName) throws IOException {
+        return Collections.emptyEnumeration();
+    }
+
+    /**
+     * Returns the absolute path of the native library with the specified name,
+     * or {@code null}. If this method returns {@code null} then the virtual
+     * machine searches the directories specified by the system property
+     * "java.library.path".
+     * <p>
+     * This implementation always returns {@code null}.
+     * </p>
+     *
+     * @param libName
+     *            the name of the library to find.
+     * @return the absolute path of the library.
+     */
+    protected String findLibrary(String libName) {
+        return null;
+    }
+
+    /**
+     * Returns the package with the specified name. Package information is
+     * searched in this class loader.
+     *
+     * @param name
+     *            the name of the package to find.
+     * @return the package with the requested name; {@code null} if the package
+     *         can not be found.
+     */
+    protected Package getPackage(String name) {
+        synchronized (packages) {
+            return packages.get(name);
+        }
+    }
+
+    /**
+     * Returns all the packages known to this class loader.
+     *
+     * @return an array with all packages known to this class loader.
+     */
+    protected Package[] getPackages() {
+        synchronized (packages) {
+            Collection<Package> col = packages.values();
+            Package[] result = new Package[col.size()];
+            col.toArray(result);
+            return result;
+        }
+    }
+
+    /**
+     * Defines and returns a new {@code Package} using the specified
+     * information. If {@code sealBase} is {@code null}, the package is left
+     * unsealed. Otherwise, the package is sealed using this URL.
+     *
+     * @param name
+     *            the name of the package.
+     * @param specTitle
+     *            the title of the specification.
+     * @param specVersion
+     *            the version of the specification.
+     * @param specVendor
+     *            the vendor of the specification.
+     * @param implTitle
+     *            the implementation title.
+     * @param implVersion
+     *            the implementation version.
+     * @param implVendor
+     *            the specification vendor.
+     * @param sealBase
+     *            the URL used to seal this package or {@code null} to leave the
+     *            package unsealed.
+     * @return the {@code Package} object that has been created.
+     * @throws IllegalArgumentException
+     *             if a package with the specified name already exists.
+     */
+    protected Package definePackage(String name, String specTitle, String specVersion,
+            String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase)
+            throws IllegalArgumentException {
+
+        synchronized (packages) {
+            if (packages.containsKey(name)) {
+                throw new IllegalArgumentException("Package " + name + " already defined");
+            }
+
+            Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle,
+                    implVersion, implVendor, sealBase);
+
+            packages.put(name, newPackage);
+
+            return newPackage;
+        }
+    }
+
+    /**
+     * Sets the signers of the specified class. This implementation does
+     * nothing.
+     *
+     * @param c
+     *            the {@code Class} object for which to set the signers.
+     * @param signers
+     *            the signers for {@code c}.
+     */
+    protected final void setSigners(Class<?> c, Object[] signers) {
+    }
+
+    /**
+     * Sets the assertion status of the class with the specified name.
+     * <p>
+     * <strong>Note: </strong>This method does nothing in the Android reference
+     * implementation.
+     * </p>
+     *
+     * @param cname
+     *            the name of the class for which to set the assertion status.
+     * @param enable
+     *            the new assertion status.
+     */
+    public void setClassAssertionStatus(String cname, boolean enable) {
+    }
+
+    /**
+     * Sets the assertion status of the package with the specified name.
+     * <p>
+     * <strong>Note: </strong>This method does nothing in the Android reference
+     * implementation.
+     * </p>
+     *
+     * @param pname
+     *            the name of the package for which to set the assertion status.
+     * @param enable
+     *            the new assertion status.
+     */
+    public void setPackageAssertionStatus(String pname, boolean enable) {
+    }
+
+    /**
+     * Sets the default assertion status for this class loader.
+     * <p>
+     * <strong>Note: </strong>This method does nothing in the Android reference
+     * implementation.
+     * </p>
+     *
+     * @param enable
+     *            the new assertion status.
+     */
+    public void setDefaultAssertionStatus(boolean enable) {
+    }
+
+    /**
+     * Sets the default assertion status for this class loader to {@code false}
+     * and removes any package default and class assertion status settings.
+     * <p>
+     * <strong>Note:</strong> This method does nothing in the Android reference
+     * implementation.
+     * </p>
+     */
+    public void clearAssertionStatus() {
+    }
+}
+
+/*
+ * Provides a helper class that combines two existing URL enumerations into one.
+ * It is required for the getResources() methods. Items are fetched from the
+ * first enumeration until it's empty, then from the second one.
+ */
+class TwoEnumerationsInOne implements Enumeration<URL> {
+
+    private final Enumeration<URL> first;
+
+    private final Enumeration<URL> second;
+
+    public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) {
+        this.first = first;
+        this.second = second;
+    }
+
+    @Override
+    public boolean hasMoreElements() {
+        return first.hasMoreElements() || second.hasMoreElements();
+    }
+
+    @Override
+    public URL nextElement() {
+        if (first.hasMoreElements()) {
+            return first.nextElement();
+        } else {
+            return second.nextElement();
+        }
+    }
+
+}
+
+/**
+ * Provides an explicit representation of the boot class loader. It sits at the
+ * head of the class loader chain and delegates requests to the VM's internal
+ * class loading mechanism.
+ */
+class BootClassLoader extends ClassLoader {
+
+    private static BootClassLoader instance;
+
+    @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
+    public static synchronized BootClassLoader getInstance() {
+        if (instance == null) {
+            instance = new BootClassLoader();
+        }
+
+        return instance;
+    }
+
+    public BootClassLoader() {
+        super(null, true);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        return Class.classForName(name, false, null);
+    }
+
+    @Override
+    protected URL findResource(String name) {
+        return VMClassLoader.getResource(name);
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    protected Enumeration<URL> findResources(String resName) throws IOException {
+        return Collections.enumeration(VMClassLoader.getResources(resName));
+    }
+
+    /**
+     * Returns package information for the given package. Unfortunately, the
+     * Android BootClassLoader doesn't really have this information, and as a
+     * non-secure ClassLoader, it isn't even required to, according to the spec.
+     * Yet, we want to provide it, in order to make all those hopeful callers of
+     * {@code myClass.getPackage().getName()} happy. Thus we construct a Package
+     * object the first time it is being requested and fill most of the fields
+     * with dummy values. The Package object is then put into the ClassLoader's
+     * Package cache, so we see the same one next time. We don't create Package
+     * objects for null arguments or for the default package.
+     * <p>
+     * There a limited chance that we end up with multiple Package objects
+     * representing the same package: It can happen when when a package is
+     * scattered across different JAR files being loaded by different
+     * ClassLoaders. Rather unlikely, and given that this whole thing is more or
+     * less a workaround, probably not worth the effort.
+     */
+    @Override
+    protected Package getPackage(String name) {
+        if (name != null && !name.isEmpty()) {
+            synchronized (this) {
+                Package pack = super.getPackage(name);
+
+                if (pack == null) {
+                    pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0",
+                            "Unknown", null);
+                }
+
+                return pack;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public URL getResource(String resName) {
+        return findResource(resName);
+    }
+
+    @Override
+    protected Class<?> loadClass(String className, boolean resolve)
+           throws ClassNotFoundException {
+        Class<?> clazz = findLoadedClass(className);
+
+        if (clazz == null) {
+            clazz = findClass(className);
+        }
+
+        return clazz;
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String resName) throws IOException {
+        return findResources(resName);
+    }
+}
+
+/**
+ * TODO Open issues - Missing / empty methods - Signer stuff - Protection
+ * domains - Assertions
+ */
diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
new file mode 100644
index 0000000..1422c13
--- /dev/null
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -0,0 +1,340 @@
+/*
+ * 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 java.lang;
+
+import dalvik.system.VMRuntime;
+import java.lang.ref.FinalizerReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.TimeoutException;
+import libcore.util.EmptyArray;
+
+/**
+ * Calls Object.finalize() on objects in the finalizer reference queue. The VM
+ * will abort if any finalize() call takes more than the maximum finalize time
+ * to complete.
+ *
+ * @hide
+ */
+public final class Daemons {
+    private static final int NANOS_PER_MILLI = 1000 * 1000;
+    private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
+    private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
+
+    public static void start() {
+        ReferenceQueueDaemon.INSTANCE.start();
+        FinalizerDaemon.INSTANCE.start();
+        FinalizerWatchdogDaemon.INSTANCE.start();
+        HeapTrimmerDaemon.INSTANCE.start();
+        GCDaemon.INSTANCE.start();
+    }
+
+    public static void stop() {
+        ReferenceQueueDaemon.INSTANCE.stop();
+        FinalizerDaemon.INSTANCE.stop();
+        FinalizerWatchdogDaemon.INSTANCE.stop();
+        HeapTrimmerDaemon.INSTANCE.stop();
+        GCDaemon.INSTANCE.stop();
+    }
+
+    /**
+     * A background task that provides runtime support to the application.
+     * Daemons can be stopped and started, but only so that the zygote can be a
+     * single-threaded process when it forks.
+     */
+    private static abstract class Daemon implements Runnable {
+        private Thread thread;
+
+        public synchronized void start() {
+            if (thread != null) {
+                throw new IllegalStateException("already running");
+            }
+            thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName());
+            thread.setDaemon(true);
+            thread.start();
+        }
+
+        public abstract void run();
+
+        /**
+         * Returns true while the current thread should continue to run; false
+         * when it should return.
+         */
+        protected synchronized boolean isRunning() {
+            return thread != null;
+        }
+
+        public synchronized void interrupt() {
+            if (thread == null) {
+                throw new IllegalStateException("not running");
+            }
+            thread.interrupt();
+        }
+
+        /**
+         * Waits for the runtime thread to stop. This interrupts the thread
+         * currently running the runnable and then waits for it to exit.
+         */
+        public void stop() {
+            Thread threadToStop;
+            synchronized (this) {
+                threadToStop = thread;
+                thread = null;
+            }
+            if (threadToStop == null) {
+                throw new IllegalStateException("not running");
+            }
+            threadToStop.interrupt();
+            while (true) {
+                try {
+                    threadToStop.join();
+                    return;
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+
+        /**
+         * Returns the current stack trace of the thread, or an empty stack trace
+         * if the thread is not currently running.
+         */
+        public synchronized StackTraceElement[] getStackTrace() {
+            return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
+        }
+    }
+
+    /**
+     * This heap management thread moves elements from the garbage collector's
+     * pending list to the managed reference queue.
+     */
+    private static class ReferenceQueueDaemon extends Daemon {
+        private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
+
+        @Override public void run() {
+            while (isRunning()) {
+                Reference<?> list;
+                try {
+                    synchronized (ReferenceQueue.class) {
+                        while (ReferenceQueue.unenqueued == null) {
+                            ReferenceQueue.class.wait();
+                        }
+                        list = ReferenceQueue.unenqueued;
+                        ReferenceQueue.unenqueued = null;
+                    }
+                } catch (InterruptedException e) {
+                    continue;
+                }
+                enqueue(list);
+            }
+        }
+
+        private void enqueue(Reference<?> list) {
+            while (list != null) {
+                Reference<?> reference;
+                // pendingNext is owned by the GC so no synchronization is required
+                if (list == list.pendingNext) {
+                    reference = list;
+                    reference.pendingNext = null;
+                    list = null;
+                } else {
+                    reference = list.pendingNext;
+                    list.pendingNext = reference.pendingNext;
+                    reference.pendingNext = null;
+                }
+                reference.enqueueInternal();
+            }
+        }
+    }
+
+    private static class FinalizerDaemon extends Daemon {
+        private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
+        private final ReferenceQueue<Object> queue = FinalizerReference.queue;
+        private volatile Object finalizingObject;
+        private volatile long finalizingStartedNanos;
+
+        @Override public void run() {
+            while (isRunning()) {
+                // Take a reference, blocking until one is ready or the thread should stop
+                try {
+                    doFinalize((FinalizerReference<?>) queue.remove());
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+
+        @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
+        private void doFinalize(FinalizerReference<?> reference) {
+            FinalizerReference.remove(reference);
+            Object object = reference.get();
+            reference.clear();
+            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.
+                System.logE("Uncaught exception thrown by finalizer", ex);
+            } finally {
+                finalizingObject = null;
+            }
+        }
+    }
+
+    /**
+     * The watchdog exits the VM if the finalizer ever gets stuck. We consider
+     * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS
+     * on one instance.
+     */
+    private static class FinalizerWatchdogDaemon extends Daemon {
+        private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
+
+        @Override public void run() {
+            while (isRunning()) {
+                Object object = waitForObject();
+                if (object == null) {
+                    // We have been interrupted, need to see if this daemon has been stopped.
+                    continue;
+                }
+                boolean finalized = waitForFinalization(object);
+                if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) {
+                    finalizerTimedOut(object);
+                    break;
+                }
+            }
+        }
+
+        private Object waitForObject() {
+            while (true) {
+                Object object = FinalizerDaemon.INSTANCE.finalizingObject;
+                if (object != null) {
+                    return object;
+                }
+                synchronized (this) {
+                    // wait until something is ready to be finalized
+                    // http://code.google.com/p/android/issues/detail?id=22778
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                        // Daemon.stop may have interrupted us.
+                        return null;
+                    }
+                }
+            }
+        }
+
+        private void sleepFor(long startNanos, long durationNanos) {
+            while (true) {
+                long elapsedNanos = System.nanoTime() - startNanos;
+                long sleepNanos = durationNanos - elapsedNanos;
+                long sleepMills = sleepNanos / NANOS_PER_MILLI;
+                if (sleepMills <= 0) {
+                    return;
+                }
+                try {
+                    Thread.sleep(sleepMills);
+                } catch (InterruptedException e) {
+                    if (!isRunning()) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        private boolean waitForFinalization(Object object) {
+            sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS);
+            return object != FinalizerDaemon.INSTANCE.finalizingObject;
+        }
+
+        private static void finalizerTimedOut(Object object) {
+            // The current object has exceeded the finalization deadline; abort!
+            String message = object.getClass().getName() + ".finalize() timed out after "
+                    + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
+            Exception syntheticException = new TimeoutException(message);
+            // We use the stack from where finalize() was running to show where it was stuck.
+            syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
+            Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
+            if (h == null) {
+                // If we have no handler, log and exit.
+                System.logE(message, syntheticException);
+                System.exit(2);
+            }
+            // Otherwise call the handler to do crash reporting.
+            // We don't just throw because we're not the thread that
+            // timed out; we're the thread that detected it.
+            h.uncaughtException(Thread.currentThread(), syntheticException);
+        }
+    }
+
+    // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap.
+    public static void requestHeapTrim() {
+        synchronized (HeapTrimmerDaemon.INSTANCE) {
+            HeapTrimmerDaemon.INSTANCE.notify();
+        }
+    }
+
+    private static class HeapTrimmerDaemon extends Daemon {
+        private static final HeapTrimmerDaemon INSTANCE = new HeapTrimmerDaemon();
+
+        @Override public void run() {
+            while (isRunning()) {
+                try {
+                    synchronized (this) {
+                        wait();
+                    }
+                    VMRuntime.getRuntime().trimHeap();
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+    }
+
+    // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap.
+    public static void requestGC() {
+        GCDaemon.INSTANCE.requestGC();
+    }
+
+    private static class GCDaemon extends Daemon {
+        private static final GCDaemon INSTANCE = new GCDaemon();
+        private int count = 0;
+
+        public void requestGC() {
+            synchronized (this) {
+                ++count;
+                notify();
+            }
+        }
+
+        @Override public void run() {
+            while (isRunning()) {
+                try {
+                    synchronized (this) {
+                        // Wait until a request comes in, unless we have a pending request.
+                        while (count == 0) {
+                            wait();
+                        }
+                        --count;
+                    }
+                    VMRuntime.getRuntime().concurrentGC();
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+    }
+}
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
new file mode 100644
index 0000000..3603f9c
--- /dev/null
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+/*
+ * 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.lang;
+
+import com.android.dex.Dex;
+import java.lang.reflect.ArtField;
+import java.lang.reflect.ArtMethod;
+
+/**
+ * A dex cache holds resolved copies of strings, fields, methods, and classes from the dexfile.
+ */
+final class DexCache {
+    /** Lazily initialized dex file wrapper. Volatile to avoid double-check locking issues. */
+    private volatile Dex dex;
+
+    /** Indexed by the type index array of locations of initialized static storage. */
+    Object[] initializedStaticStorage;
+
+    /** The location of the associated dex file. */
+    String location;
+
+    /**
+     * References to fields as they become resolved following interpreter semantics. May refer to
+     * fields defined in other dex files.
+     */
+    ArtField[] resolvedFields;
+
+    /**
+     * References to methods as they become resolved following interpreter semantics. May refer to
+     * methods defined in other dex files.
+     */
+    ArtMethod[] resolvedMethods;
+
+    /**
+     * References to types as they become resolved following interpreter semantics. May refer to
+     * types defined in other dex files.
+     */
+    Class[] resolvedTypes;
+
+    /**
+     * References to strings as they become resolved following interpreter semantics. All strings
+     * are interned.
+     */
+    String[] strings;
+
+    /** Holds C pointer to dexFile. */
+    private int dexFile;
+
+    // Only created by the VM.
+    private DexCache() {}
+
+    Dex getDex() {
+        Dex result = dex;
+        if (result == null) {
+            synchronized (this) {
+                result = dex;
+                if (result == null) {
+                    dex = result = getDexNative();
+                }
+            }
+        }
+        return result;
+    }
+
+    private native Dex getDexNative();
+}
+
diff --git a/libart/src/main/java/java/lang/Enum.java b/libart/src/main/java/java/lang/Enum.java
new file mode 100644
index 0000000..f98554a
--- /dev/null
+++ b/libart/src/main/java/java/lang/Enum.java
@@ -0,0 +1,222 @@
+/* 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 java.lang;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import libcore.util.BasicLruCache;
+import libcore.util.EmptyArray;
+
+/**
+ * The superclass of all enumerated types. Actual enumeration types inherit from
+ * this class, but extending this class does not make a class an enumeration
+ * type, since the compiler needs to generate special information for it.
+ */
+public abstract class Enum<E extends Enum<E>> implements Serializable, Comparable<E> {
+
+    private static final long serialVersionUID = -4300926546619394005L;
+
+    private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache
+            = new BasicLruCache<Class<? extends Enum>, Object[]>(64) {
+        @Override protected Object[] create(Class<? extends Enum> enumType) {
+            if (!enumType.isEnum()) {
+                return null;
+            }
+            try {
+                Method method = enumType.getDeclaredMethod("values", EmptyArray.CLASS);
+                return (Object[]) method.invoke((Object[]) null);
+            } catch (NoSuchMethodException impossible) {
+                throw new AssertionError("impossible", impossible);
+            } catch (IllegalAccessException impossible) {
+                throw new AssertionError("impossible", impossible);
+            } catch (InvocationTargetException impossible) {
+                throw new AssertionError("impossible", impossible);
+            }
+        }
+    };
+
+    private final String name;
+
+    private final int ordinal;
+
+    /**
+     * Constructor for constants of enum subtypes.
+     *
+     * @param name
+     *            the enum constant's declared name.
+     * @param ordinal
+     *            the enum constant's ordinal, which corresponds to its position
+     *            in the enum declaration, starting at zero.
+     */
+    protected Enum(String name, int ordinal) {
+        this.name = name;
+        this.ordinal = ordinal;
+    }
+
+    /**
+     * Returns the name of this enum constant. The name is the field as it
+     * appears in the {@code enum} declaration.
+     *
+     * @return the name of this enum constant.
+     * @see #toString()
+     */
+    public final String name() {
+        return name;
+    }
+
+    /**
+     * Returns the position of the enum constant in the declaration. The first
+     * constant has an ordinal value of zero.
+     *
+     * @return the ordinal value of this enum constant.
+     */
+    public final int ordinal() {
+        return ordinal;
+    }
+
+    /**
+     * Returns a string containing a concise, human-readable description of this
+     * object. In this case, the enum constant's name is returned.
+     *
+     * @return a printable representation of this object.
+     */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Compares this object with the specified object and indicates if they are
+     * equal. In order to be equal, {@code object} must be identical to this
+     * enum constant.
+     *
+     * @param other
+     *            the object to compare this enum constant with.
+     * @return {@code true} if the specified object is equal to this
+     *         {@code Enum}; {@code false} otherwise.
+     */
+    @Override
+    public final boolean equals(Object other) {
+        return this == other;
+    }
+
+    @Override
+    public final int hashCode() {
+        return ordinal + (name == null ? 0 : name.hashCode());
+    }
+
+    /**
+     * {@code Enum} objects are singletons, they may not be cloned. This method
+     * always throws a {@code CloneNotSupportedException}.
+     *
+     * @return does not return.
+     * @throws CloneNotSupportedException
+     *             is always thrown.
+     */
+    @Override
+    protected final Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException("Enums may not be cloned");
+    }
+
+    /**
+     * Compares this object to the specified enum object to determine their
+     * relative order. This method compares the object's ordinal values, that
+     * is, their position in the enum declaration.
+     *
+     * @param o
+     *            the enum object to compare this object to.
+     * @return a negative value if the ordinal value of this enum constant is
+     *         less than the ordinal value of {@code o}; 0 if the ordinal
+     *         values of this enum constant and {@code o} are equal; a positive
+     *         value if the ordinal value of this enum constant is greater than
+     *         the ordinal value of {@code o}.
+     * @see java.lang.Comparable
+     */
+    public final int compareTo(E o) {
+        return ordinal - o.ordinal;
+    }
+
+    /**
+     * Returns the enum constant's declaring class.
+     *
+     * @return the class object representing the constant's enum type.
+     */
+    @SuppressWarnings("unchecked")
+    public final Class<E> getDeclaringClass() {
+        Class<?> myClass = getClass();
+        Class<?> mySuperClass = myClass.getSuperclass();
+        if (Enum.class == mySuperClass) {
+            return (Class<E>)myClass;
+        }
+        return (Class<E>)mySuperClass;
+    }
+
+    /**
+     * Returns the constant with the specified name of the specified enum type.
+     *
+     * @param enumType
+     *            the class of the enumerated type to search for the constant
+     *            value.
+     * @param name
+     *            the name of the constant value to find.
+     * @return the enum constant.
+     * @throws NullPointerException
+     *             if either {@code enumType} or {@code name} are {@code null}.
+     * @throws IllegalArgumentException
+     *             if {@code enumType} is not an enumerated type or does not
+     *             have a constant value called {@code name}.
+     */
+    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
+        if (enumType == null) {
+            throw new NullPointerException("enumType == null");
+        } else if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        T[] values = getSharedConstants(enumType);
+        if (values == null) {
+            throw new IllegalArgumentException(enumType + " is not an enum type");
+        }
+        for (T value : values) {
+            if (name.equals(value.name())) {
+                return value;
+            }
+        }
+        throw new IllegalArgumentException(name + " is not a constant in " + enumType.getName());
+    }
+
+    /**
+     * Returns a shared, mutable array containing the constants of this enum. It
+     * is an error to modify the returned array.
+     *
+     * @hide
+     */
+    @SuppressWarnings("unchecked") // the cache always returns the type matching enumType
+    public static <T extends Enum<T>> T[] getSharedConstants(Class<T> enumType) {
+        return (T[]) sharedConstantsCache.get(enumType);
+    }
+
+    /**
+     * Enum types may not have finalizers.
+     *
+     * @since 1.6
+     */
+    @Override
+    @SuppressWarnings("FinalizeDoesntCallSuperFinalize")
+    protected final void finalize() {
+    }
+}
diff --git a/libart/src/main/java/java/lang/Object.java b/libart/src/main/java/java/lang/Object.java
new file mode 100644
index 0000000..9c59870
--- /dev/null
+++ b/libart/src/main/java/java/lang/Object.java
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang;
+
+/**
+ * The root class of the Java class hierarchy. All non-primitive types
+ * (including arrays) inherit either directly or indirectly from this class.
+ *
+ * <a name="writing_equals"><h4>Writing a correct {@code equals} method</h4></a>
+ * <p>Follow this style to write a canonical {@code equals} method:
+ * <pre>
+ *   // Use @Override to avoid accidental overloading.
+ *   &#x0040;Override public boolean equals(Object o) {
+ *     // Return true if the objects are identical.
+ *     // (This is just an optimization, not required for correctness.)
+ *     if (this == o) {
+ *       return true;
+ *     }
+ *
+ *     // Return false if the other object has the wrong type.
+ *     // This type may be an interface depending on the interface's specification.
+ *     if (!(o instanceof MyType)) {
+ *       return false;
+ *     }
+ *
+ *     // Cast to the appropriate type.
+ *     // This will succeed because of the instanceof, and lets us access private fields.
+ *     MyType lhs = (MyType) o;
+ *
+ *     // Check each field. Primitive fields, reference fields, and nullable reference
+ *     // fields are all treated differently.
+ *     return primitiveField == lhs.primitiveField &amp;&amp;
+ *             referenceField.equals(lhs.referenceField) &amp;&amp;
+ *             (nullableField == null ? lhs.nullableField == null
+ *                                    : nullableField.equals(lhs.nullableField));
+ *   }
+ * </pre>
+ * <p>If you override {@code equals}, you should also override {@code hashCode}: equal
+ * instances must have equal hash codes.
+ *
+ * <p>See <i>Effective Java</i> item 8 for much more detail and clarification.
+ *
+ * <a name="writing_hashCode"><h4>Writing a correct {@code hashCode} method</h4></a>
+ * <p>Follow this style to write a canonical {@code hashCode} method:
+ * <pre>
+ *   &#x0040;Override public int hashCode() {
+ *     // Start with a non-zero constant.
+ *     int result = 17;
+ *
+ *     // Include a hash for each field.
+ *     result = 31 * result + (booleanField ? 1 : 0);
+ *
+ *     result = 31 * result + byteField;
+ *     result = 31 * result + charField;
+ *     result = 31 * result + shortField;
+ *     result = 31 * result + intField;
+ *
+ *     result = 31 * result + (int) (longField ^ (longField >>> 32));
+ *
+ *     result = 31 * result + Float.floatToIntBits(floatField);
+ *
+ *     long doubleFieldBits = Double.doubleToLongBits(doubleField);
+ *     result = 31 * result + (int) (doubleFieldBits ^ (doubleFieldBits >>> 32));
+ *
+ *     result = 31 * result + Arrays.hashCode(arrayField);
+ *
+ *     result = 31 * result + referenceField.hashCode();
+ *     result = 31 * result +
+ *         (nullableReferenceField == null ? 0
+ *                                         : nullableReferenceField.hashCode());
+ *
+ *     return result;
+ *   }
+ * </pre>
+ *
+ * <p>If you don't intend your type to be used as a hash key, don't simply rely on the default
+ * {@code hashCode} implementation, because that silently and non-obviously breaks any future
+ * code that does use your type as a hash key. You should throw instead:
+ * <pre>
+ *   &#x0040;Override public int hashCode() {
+ *     throw new UnsupportedOperationException();
+ *   }
+ * </pre>
+ *
+ * <p>See <i>Effective Java</i> item 9 for much more detail and clarification.
+ *
+ * <a name="writing_toString"><h4>Writing a useful {@code toString} method</h4></a>
+ * <p>For debugging convenience, it's common to override {@code toString} in this style:
+ * <pre>
+ *   &#x0040;Override public String toString() {
+ *     return getClass().getName() + "[" +
+ *         "primitiveField=" + primitiveField + ", " +
+ *         "referenceField=" + referenceField + ", " +
+ *         "arrayField=" + Arrays.toString(arrayField) + "]";
+ *   }
+ * </pre>
+ * <p>The set of fields to include is generally the same as those that would be tested
+ * in your {@code equals} implementation.
+ * <p>See <i>Effective Java</i> item 10 for much more detail and clarification.
+ */
+public class Object {
+
+    private transient Class<?> shadow$_klass_;
+    private transient int shadow$_monitor_;
+
+    /**
+     * Constructs a new instance of {@code Object}.
+     */
+    public Object() {
+      if (shadow$_klass_.isFinalizable()) {
+        java.lang.ref.FinalizerReference.add(this);
+      }
+    }
+
+    /**
+     * Creates and returns a copy of this {@code Object}. The default
+     * implementation returns a so-called "shallow" copy: It creates a new
+     * instance of the same class and then copies the field values (including
+     * object references) from this instance to the new instance. A "deep" copy,
+     * in contrast, would also recursively clone nested objects. A subclass that
+     * needs to implement this kind of cloning should call {@code super.clone()}
+     * to create the new instance and then create deep copies of the nested,
+     * mutable objects.
+     *
+     * @return a copy of this object.
+     * @throws CloneNotSupportedException
+     *             if this object's class does not implement the {@code
+     *             Cloneable} interface.
+     */
+    protected Object clone() throws CloneNotSupportedException {
+        if (!(this instanceof Cloneable)) {
+            throw new CloneNotSupportedException("Class " + getClass().getName() +
+                                                 " doesn't implement Cloneable");
+        }
+
+        return internalClone();
+    }
+
+    /*
+     * Native helper method for cloning.
+     */
+    private native Object internalClone();
+
+    /**
+     * Compares this instance with the specified object and indicates if they
+     * are equal. In order to be equal, {@code o} must represent the same object
+     * as this instance using a class-specific comparison. The general contract
+     * is that this comparison should be reflexive, symmetric, and transitive.
+     * Also, no object reference other than null is equal to null.
+     *
+     * <p>The default implementation returns {@code true} only if {@code this ==
+     * o}. See <a href="{@docRoot}reference/java/lang/Object.html#writing_equals">Writing a correct
+     * {@code equals} method</a>
+     * if you intend implementing your own {@code equals} method.
+     *
+     * <p>The general contract for the {@code equals} and {@link
+     * #hashCode()} methods is that if {@code equals} returns {@code true} for
+     * any two objects, then {@code hashCode()} must return the same value for
+     * these objects. This means that subclasses of {@code Object} usually
+     * override either both methods or neither of them.
+     *
+     * @param o
+     *            the object to compare this instance with.
+     * @return {@code true} if the specified object is equal to this {@code
+     *         Object}; {@code false} otherwise.
+     * @see #hashCode
+     */
+    public boolean equals(Object o) {
+        return this == o;
+    }
+
+    /**
+     * Invoked when the garbage collector has detected that this instance is no longer reachable.
+     * The default implementation does nothing, but this method can be overridden to free resources.
+     *
+     * <p>Note that objects that override {@code finalize} are significantly more expensive than
+     * objects that don't. Finalizers may be run a long time after the object is no longer
+     * reachable, depending on memory pressure, so it's a bad idea to rely on them for cleanup.
+     * Note also that finalizers are run on a single VM-wide finalizer thread,
+     * so doing blocking work in a finalizer is a bad idea. A finalizer is usually only necessary
+     * for a class that has a native peer and needs to call a native method to destroy that peer.
+     * Even then, it's better to provide an explicit {@code close} method (and implement
+     * {@link java.io.Closeable}), and insist that callers manually dispose of instances. This
+     * works well for something like files, but less well for something like a {@code BigInteger}
+     * where typical calling code would have to deal with lots of temporaries. Unfortunately,
+     * code that creates lots of temporaries is the worst kind of code from the point of view of
+     * the single finalizer thread.
+     *
+     * <p>If you <i>must</i> use finalizers, consider at least providing your own
+     * {@link java.lang.ref.ReferenceQueue} and having your own thread process that queue.
+     *
+     * <p>Unlike constructors, finalizers are not automatically chained. You are responsible for
+     * calling {@code super.finalize()} yourself.
+     *
+     * <p>Uncaught exceptions thrown by finalizers are ignored and do not terminate the finalizer
+     * thread.
+     *
+     * See <i>Effective Java</i> Item 7, "Avoid finalizers" for more.
+     */
+    @FindBugsSuppressWarnings("FI_EMPTY")
+    protected void finalize() throws Throwable {
+    }
+
+    /**
+     * Returns the unique instance of {@link Class} that represents this
+     * object's class. Note that {@code getClass()} is a special case in that it
+     * actually returns {@code Class<? extends Foo>} where {@code Foo} is the
+     * erasure of the type of the expression {@code getClass()} was called upon.
+     * <p>
+     * As an example, the following code actually compiles, although one might
+     * think it shouldn't:
+     * <p>
+     * <pre>{@code
+     *   List<Integer> l = new ArrayList<Integer>();
+     *   Class<? extends List> c = l.getClass();}</pre>
+     *
+     * @return this object's {@code Class} instance.
+     */
+    public final Class<?> getClass() {
+      return shadow$_klass_;
+    }
+
+    /**
+     * Returns an integer hash code for this object. By contract, any two
+     * objects for which {@link #equals} returns {@code true} must return
+     * the same hash code value. This means that subclasses of {@code Object}
+     * usually override both methods or neither method.
+     *
+     * <p>Note that hash values must not change over time unless information used in equals
+     * comparisons also changes.
+     *
+     * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_hashCode">Writing a correct
+     * {@code hashCode} method</a>
+     * if you intend implementing your own {@code hashCode} method.
+     *
+     * @return this object's hash code.
+     * @see #equals
+     */
+    public int hashCode() {
+        return System.identityHashCode(this);
+    }
+
+    /**
+     * Causes a thread which is waiting on this object's monitor (by means of
+     * calling one of the {@code wait()} methods) to be woken up. If more than
+     * one thread is waiting, one of them is chosen at the discretion of the
+     * VM. The chosen thread will not run immediately. The thread
+     * that called {@code notify()} has to release the object's monitor first.
+     * Also, the chosen thread still has to compete against other threads that
+     * try to synchronize on the same object.
+     * <p>
+     * This method can only be invoked by a thread which owns this object's
+     * monitor. A thread becomes owner of an object's monitor
+     * </p>
+     * <ul>
+     * <li>by executing a synchronized method of that object;</li>
+     * <li>by executing the body of a {@code synchronized} statement that
+     * synchronizes on the object;</li>
+     * <li>by executing a synchronized static method if the object is of type
+     * {@code Class}.</li>
+     * </ul>
+     *
+     * @see #notifyAll
+     * @see #wait()
+     * @see #wait(long)
+     * @see #wait(long,int)
+     * @see java.lang.Thread
+     */
+    public final native void notify();
+
+    /**
+     * Causes all threads which are waiting on this object's monitor (by means
+     * of calling one of the {@code wait()} methods) to be woken up. The threads
+     * will not run immediately. The thread that called {@code notify()} has to
+     * release the object's monitor first. Also, the threads still have to
+     * compete against other threads that try to synchronize on the same object.
+     * <p>
+     * This method can only be invoked by a thread which owns this object's
+     * monitor. A thread becomes owner of an object's monitor
+     * </p>
+     * <ul>
+     * <li>by executing a synchronized method of that object;</li>
+     * <li>by executing the body of a {@code synchronized} statement that
+     * synchronizes on the object;</li>
+     * <li>by executing a synchronized static method if the object is of type
+     * {@code Class}.</li>
+     * </ul>
+     *
+     * @throws IllegalMonitorStateException
+     *             if the thread calling this method is not the owner of this
+     *             object's monitor.
+     * @see #notify
+     * @see #wait()
+     * @see #wait(long)
+     * @see #wait(long,int)
+     * @see java.lang.Thread
+     */
+    public final native void notifyAll();
+
+    /**
+     * Returns a string containing a concise, human-readable description of this
+     * object. Subclasses are encouraged to override this method and provide an
+     * implementation that takes into account the object's type and data. The
+     * default implementation is equivalent to the following expression:
+     * <pre>
+     *   getClass().getName() + '@' + Integer.toHexString(hashCode())</pre>
+     * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_toString">Writing a useful
+     * {@code toString} method</a>
+     * if you intend implementing your own {@code toString} method.
+     *
+     * @return a printable representation of this object.
+     */
+    public String toString() {
+        return getClass().getName() + '@' + Integer.toHexString(hashCode());
+    }
+
+    /**
+     * Causes the calling thread to wait until another thread calls the {@code
+     * notify()} or {@code notifyAll()} method of this object. This method can
+     * only be invoked by a thread which owns this object's monitor; see
+     * {@link #notify()} on how a thread can become the owner of a monitor.
+     * <p>
+     * A waiting thread can be sent {@code interrupt()} to cause it to
+     * prematurely stop waiting, so {@code wait} should be called in a loop to
+     * check that the condition that has been waited for has been met before
+     * continuing.
+     * </p>
+     * <p>
+     * While the thread waits, it gives up ownership of this object's monitor.
+     * When it is notified (or interrupted), it re-acquires the monitor before
+     * it starts running.
+     * </p>
+     *
+     * @throws IllegalMonitorStateException
+     *             if the thread calling this method is not the owner of this
+     *             object's monitor.
+     * @throws InterruptedException
+     *             if another thread interrupts this thread while it is waiting.
+     * @see #notify
+     * @see #notifyAll
+     * @see #wait(long)
+     * @see #wait(long,int)
+     * @see java.lang.Thread
+     */
+    public final native void wait() throws InterruptedException;
+
+    /**
+     * Causes the calling thread to wait until another thread calls the {@code
+     * notify()} or {@code notifyAll()} method of this object or until the
+     * specified timeout expires. This method can only be invoked by a thread
+     * which owns this object's monitor; see {@link #notify()} on how a thread
+     * can become the owner of a monitor.
+     * <p>
+     * A waiting thread can be sent {@code interrupt()} to cause it to
+     * prematurely stop waiting, so {@code wait} should be called in a loop to
+     * check that the condition that has been waited for has been met before
+     * continuing.
+     * </p>
+     * <p>
+     * While the thread waits, it gives up ownership of this object's monitor.
+     * When it is notified (or interrupted), it re-acquires the monitor before
+     * it starts running.
+     * </p>
+     *
+     * @param millis
+     *            the maximum time to wait in milliseconds.
+     * @throws IllegalArgumentException
+     *             if {@code millis < 0}.
+     * @throws IllegalMonitorStateException
+     *             if the thread calling this method is not the owner of this
+     *             object's monitor.
+     * @throws InterruptedException
+     *             if another thread interrupts this thread while it is waiting.
+     * @see #notify
+     * @see #notifyAll
+     * @see #wait()
+     * @see #wait(long,int)
+     * @see java.lang.Thread
+     */
+    public final void wait(long millis) throws InterruptedException {
+        wait(millis, 0);
+    }
+
+    /**
+     * Causes the calling thread to wait until another thread calls the {@code
+     * notify()} or {@code notifyAll()} method of this object or until the
+     * specified timeout expires. This method can only be invoked by a thread
+     * that owns this object's monitor; see {@link #notify()} on how a thread
+     * can become the owner of a monitor.
+     * <p>
+     * A waiting thread can be sent {@code interrupt()} to cause it to
+     * prematurely stop waiting, so {@code wait} should be called in a loop to
+     * check that the condition that has been waited for has been met before
+     * continuing.
+     * </p>
+     * <p>
+     * While the thread waits, it gives up ownership of this object's monitor.
+     * When it is notified (or interrupted), it re-acquires the monitor before
+     * it starts running.
+     * </p>
+     *
+     * @param millis
+     *            the maximum time to wait in milliseconds.
+     * @param nanos
+     *            the fraction of a millisecond to wait, specified in
+     *            nanoseconds.
+     * @throws IllegalArgumentException
+     *             if {@code millis < 0}, {@code nanos < 0} or {@code nanos >
+     *             999999}.
+     * @throws IllegalMonitorStateException
+     *             if the thread calling this method is not the owner of this
+     *             object's monitor.
+     * @throws InterruptedException
+     *             if another thread interrupts this thread while it is waiting.
+     * @see #notify
+     * @see #notifyAll
+     * @see #wait()
+     * @see #wait(long,int)
+     * @see java.lang.Thread
+     */
+    public final native void wait(long millis, int nanos) throws InterruptedException;
+}
diff --git a/libart/src/main/java/java/lang/String.java b/libart/src/main/java/java/lang/String.java
new file mode 100644
index 0000000..385f549
--- /dev/null
+++ b/libart/src/main/java/java/lang/String.java
@@ -0,0 +1,2011 @@
+/*
+ *  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 java.lang;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.Charsets;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import libcore.util.EmptyArray;
+
+/**
+ * An immutable sequence of characters/code units ({@code char}s). A
+ * {@code String} is represented by array of UTF-16 values, such that
+ * Unicode supplementary characters (code points) are stored/encoded as
+ * surrogate pairs via Unicode code units ({@code char}).
+ *
+ * <a name="backing_array"><h3>Backing Arrays</h3></a>
+ * This class is implemented using a char[]. The length of the array may exceed
+ * the length of the string. For example, the string "Hello" may be backed by
+ * the array {@code ['H', 'e', 'l', 'l', 'o', 'W'. 'o', 'r', 'l', 'd']} with
+ * offset 0 and length 5.
+ *
+ * <p>Multiple strings can share the same char[] because strings are immutable.
+ * The {@link #substring} method <strong>always</strong> returns a string that
+ * shares the backing array of its source string. Generally this is an
+ * optimization: fewer character arrays need to be allocated, and less copying
+ * is necessary. But this can also lead to unwanted heap retention. Taking a
+ * short substring of long string means that the long shared char[] won't be
+ * garbage until both strings are garbage. This typically happens when parsing
+ * small substrings out of a large input. To avoid this where necessary, call
+ * {@code new String(longString.subString(...))}. The string copy constructor
+ * always ensures that the backing array is no larger than necessary.
+ *
+ * @see StringBuffer
+ * @see StringBuilder
+ * @see Charset
+ * @since 1.0
+ */
+public final class String implements Serializable, Comparable<String>, CharSequence {
+
+    private static final long serialVersionUID = -6849794470754667710L;
+
+    private static final char REPLACEMENT_CHAR = (char) 0xfffd;
+
+    /**
+     * CaseInsensitiveComparator compares Strings ignoring the case of the
+     * characters.
+     */
+    private static final class CaseInsensitiveComparator implements
+            Comparator<String>, Serializable {
+        private static final long serialVersionUID = 8575799808933029326L;
+
+        /**
+         * Compare the two objects to determine the relative ordering.
+         *
+         * @param o1
+         *            an Object to compare
+         * @param o2
+         *            an Object to compare
+         * @return an int < 0 if object1 is less than object2, 0 if they are
+         *         equal, and > 0 if object1 is greater
+         *
+         * @exception ClassCastException
+         *                if objects are not the correct type
+         */
+        public int compare(String o1, String o2) {
+            return o1.compareToIgnoreCase(o2);
+        }
+    }
+
+    /**
+     * A comparator ignoring the case of the characters.
+     */
+    public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
+
+    private static final char[] ASCII;
+    static {
+        ASCII = new char[128];
+        for (int i = 0; i < ASCII.length; ++i) {
+            ASCII[i] = (char) i;
+        }
+    }
+
+    private final char[] value;
+
+    private final int offset;
+
+    private final int count;
+
+    private int hashCode;
+
+    /**
+     * Creates an empty string.
+     */
+    public String() {
+        value = EmptyArray.CHAR;
+        offset = 0;
+        count = 0;
+    }
+
+    /**
+     * Converts the byte array to a string using the system's
+     * {@link java.nio.charset.Charset#defaultCharset default charset}.
+     */
+    @FindBugsSuppressWarnings("DM_DEFAULT_ENCODING")
+    public String(byte[] data) {
+        this(data, 0, data.length);
+    }
+
+    /**
+     * Converts the byte array to a string, setting the high byte of every
+     * character to the specified value.
+     *
+     * @param data
+     *            the byte array to convert to a string.
+     * @param high
+     *            the high byte to use.
+     * @throws NullPointerException
+     *             if {@code data == null}.
+     * @deprecated Use {@link #String(byte[])} or {@link #String(byte[], String)} instead.
+     */
+    @Deprecated
+    public String(byte[] data, int high) {
+        this(data, high, 0, data.length);
+    }
+
+    /**
+     * Converts a subsequence of the byte array to a string using the system's
+     * {@link java.nio.charset.Charset#defaultCharset default charset}.
+     *
+     * @throws NullPointerException
+     *             if {@code data == null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}.
+     */
+    public String(byte[] data, int offset, int byteCount) {
+        this(data, offset, byteCount, Charset.defaultCharset());
+    }
+
+    /**
+     * Converts the byte array to a string, setting the high byte of every
+     * character to {@code high}.
+     *
+     * @throws NullPointerException
+     *             if {@code data == null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}
+     *
+     * @deprecated Use {@link #String(byte[], int, int)} instead.
+     */
+    @Deprecated
+    public String(byte[] data, int high, int offset, int byteCount) {
+        if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
+            throw failedBoundsCheck(data.length, offset, byteCount);
+        }
+        this.offset = 0;
+        this.value = new char[byteCount];
+        this.count = byteCount;
+        high <<= 8;
+        for (int i = 0; i < count; i++) {
+            value[i] = (char) (high + (data[offset++] & 0xff));
+        }
+    }
+
+    /**
+     * Converts the byte array to a string using the named charset.
+     *
+     * <p>The behavior when the bytes cannot be decoded by the named charset
+     * is unspecified. Use {@link java.nio.charset.CharsetDecoder} for more control.
+     *
+     * @throws NullPointerException
+     *             if {@code data == null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}.
+     * @throws UnsupportedEncodingException
+     *             if the named charset is not supported.
+     */
+    public String(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException {
+        this(data, offset, byteCount, Charset.forNameUEE(charsetName));
+    }
+
+    /**
+     * Converts the byte array to a string using the named charset.
+     *
+     * <p>The behavior when the bytes cannot be decoded by the named charset
+     * is unspecified. Use {@link java.nio.charset.CharsetDecoder} for more control.
+     *
+     * @throws NullPointerException
+     *             if {@code data == null}.
+     * @throws UnsupportedEncodingException
+     *             if {@code charsetName} is not supported.
+     */
+    public String(byte[] data, String charsetName) throws UnsupportedEncodingException {
+        this(data, 0, data.length, Charset.forNameUEE(charsetName));
+    }
+
+    /**
+     * Converts the byte array to a string using the given charset.
+     *
+     * <p>The behavior when the bytes cannot be decoded by the given charset
+     * is to replace malformed input and unmappable characters with the charset's default
+     * replacement string. Use {@link java.nio.charset.CharsetDecoder} for more control.
+     *
+     * @throws IndexOutOfBoundsException
+     *             if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}
+     * @throws NullPointerException
+     *             if {@code data == null}
+     *
+     * @since 1.6
+     */
+    public String(byte[] data, int offset, int byteCount, Charset charset) {
+        if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
+            throw failedBoundsCheck(data.length, offset, byteCount);
+        }
+
+        // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because 'count' and
+        // 'value' are final.
+        String canonicalCharsetName = charset.name();
+        if (canonicalCharsetName.equals("UTF-8")) {
+            byte[] d = data;
+            char[] v = new char[byteCount];
+
+            int idx = offset;
+            int last = offset + byteCount;
+            int s = 0;
+outer:
+            while (idx < last) {
+                byte b0 = d[idx++];
+                if ((b0 & 0x80) == 0) {
+                    // 0xxxxxxx
+                    // Range:  U-00000000 - U-0000007F
+                    int val = b0 & 0xff;
+                    v[s++] = (char) val;
+                } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||
+                        ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) {
+                    int utfCount = 1;
+                    if ((b0 & 0xf0) == 0xe0) utfCount = 2;
+                    else if ((b0 & 0xf8) == 0xf0) utfCount = 3;
+                    else if ((b0 & 0xfc) == 0xf8) utfCount = 4;
+                    else if ((b0 & 0xfe) == 0xfc) utfCount = 5;
+
+                    // 110xxxxx (10xxxxxx)+
+                    // Range:  U-00000080 - U-000007FF (count == 1)
+                    // Range:  U-00000800 - U-0000FFFF (count == 2)
+                    // Range:  U-00010000 - U-001FFFFF (count == 3)
+                    // Range:  U-00200000 - U-03FFFFFF (count == 4)
+                    // Range:  U-04000000 - U-7FFFFFFF (count == 5)
+
+                    if (idx + utfCount > last) {
+                        v[s++] = REPLACEMENT_CHAR;
+                        continue;
+                    }
+
+                    // Extract usable bits from b0
+                    int val = b0 & (0x1f >> (utfCount - 1));
+                    for (int i = 0; i < utfCount; ++i) {
+                        byte b = d[idx++];
+                        if ((b & 0xc0) != 0x80) {
+                            v[s++] = REPLACEMENT_CHAR;
+                            idx--; // Put the input char back
+                            continue outer;
+                        }
+                        // Push new bits in from the right side
+                        val <<= 6;
+                        val |= b & 0x3f;
+                    }
+
+                    // Note: Java allows overlong char
+                    // specifications To disallow, check that val
+                    // is greater than or equal to the minimum
+                    // value for each count:
+                    //
+                    // count    min value
+                    // -----   ----------
+                    //   1           0x80
+                    //   2          0x800
+                    //   3        0x10000
+                    //   4       0x200000
+                    //   5      0x4000000
+
+                    // Allow surrogate values (0xD800 - 0xDFFF) to
+                    // be specified using 3-byte UTF values only
+                    if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {
+                        v[s++] = REPLACEMENT_CHAR;
+                        continue;
+                    }
+
+                    // Reject chars greater than the Unicode maximum of U+10FFFF.
+                    if (val > 0x10FFFF) {
+                        v[s++] = REPLACEMENT_CHAR;
+                        continue;
+                    }
+
+                    // Encode chars from U+10000 up as surrogate pairs
+                    if (val < 0x10000) {
+                        v[s++] = (char) val;
+                    } else {
+                        int x = val & 0xffff;
+                        int u = (val >> 16) & 0x1f;
+                        int w = (u - 1) & 0xffff;
+                        int hi = 0xd800 | (w << 6) | (x >> 10);
+                        int lo = 0xdc00 | (x & 0x3ff);
+                        v[s++] = (char) hi;
+                        v[s++] = (char) lo;
+                    }
+                } else {
+                    // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff
+                    v[s++] = REPLACEMENT_CHAR;
+                }
+            }
+
+            if (s == byteCount) {
+                // We guessed right, so we can use our temporary array as-is.
+                this.offset = 0;
+                this.value = v;
+                this.count = s;
+            } else {
+                // Our temporary array was too big, so reallocate and copy.
+                this.offset = 0;
+                this.value = new char[s];
+                this.count = s;
+                System.arraycopy(v, 0, value, 0, s);
+            }
+        } else if (canonicalCharsetName.equals("ISO-8859-1")) {
+            this.offset = 0;
+            this.value = new char[byteCount];
+            this.count = byteCount;
+            Charsets.isoLatin1BytesToChars(data, offset, byteCount, value);
+        } else if (canonicalCharsetName.equals("US-ASCII")) {
+            this.offset = 0;
+            this.value = new char[byteCount];
+            this.count = byteCount;
+            Charsets.asciiBytesToChars(data, offset, byteCount, value);
+        } else {
+            CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount));
+            this.offset = 0;
+            this.count = cb.length();
+            if (count > 0) {
+                // We could use cb.array() directly, but that would mean we'd have to trust
+                // the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later,
+                // which would break String's immutability guarantee. It would also tend to
+                // mean that we'd be wasting memory because CharsetDecoder doesn't trim the
+                // array. So we copy.
+                this.value = new char[count];
+                System.arraycopy(cb.array(), 0, value, 0, count);
+            } else {
+                this.value = EmptyArray.CHAR;
+            }
+        }
+    }
+
+    /**
+     * Converts the byte array to a String using the given charset.
+     *
+     * @throws NullPointerException if {@code data == null}
+     * @since 1.6
+     */
+    public String(byte[] data, Charset charset) {
+        this(data, 0, data.length, charset);
+    }
+
+    /**
+     * Initializes this string to contain the characters in the specified
+     * character array. Modifying the character array after creating the string
+     * has no effect on the string.
+     *
+     * @throws NullPointerException if {@code data == null}
+     */
+    public String(char[] data) {
+        this(data, 0, data.length);
+    }
+
+    /**
+     * Initializes this string to contain the specified characters in the
+     * character array. Modifying the character array after creating the string
+     * has no effect on the string.
+     *
+     * @throws NullPointerException
+     *             if {@code data == null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code charCount < 0 || offset < 0 || offset + charCount > data.length}
+     */
+    public String(char[] data, int offset, int charCount) {
+        if ((offset | charCount) < 0 || charCount > data.length - offset) {
+            throw failedBoundsCheck(data.length, offset, charCount);
+        }
+        this.offset = 0;
+        this.value = new char[charCount];
+        this.count = charCount;
+        System.arraycopy(data, offset, value, 0, count);
+    }
+
+    /*
+     * Internal version of the String(char[], int, int) constructor.
+     * Does not range check, null check, or copy the character array.
+     */
+    String(int offset, int charCount, char[] chars) {
+        this.value = chars;
+        this.offset = offset;
+        this.count = charCount;
+    }
+
+    /**
+     * Constructs a new string with the same sequence of characters as {@code
+     * toCopy}. The returned string's <a href="#backing_array">backing array</a>
+     * is no larger than necessary.
+     */
+    public String(String toCopy) {
+        value = (toCopy.value.length == toCopy.count)
+                ? toCopy.value
+                : Arrays.copyOfRange(toCopy.value, toCopy.offset, toCopy.offset + toCopy.length());
+        offset = 0;
+        count = value.length;
+    }
+
+    /**
+     * Creates a {@code String} from the contents of the specified
+     * {@code StringBuffer}.
+     */
+    public String(StringBuffer stringBuffer) {
+        offset = 0;
+        synchronized (stringBuffer) {
+            value = stringBuffer.shareValue();
+            count = stringBuffer.length();
+        }
+    }
+
+    /**
+     * Creates a {@code String} from the sub-array of Unicode code points.
+     *
+     * @throws NullPointerException
+     *             if {@code codePoints == null}.
+     * @throws IllegalArgumentException
+     *             if any of the elements of {@code codePoints} are not valid
+     *             Unicode code points.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset} or {@code count} are not within the bounds
+     *             of {@code codePoints}.
+     * @since 1.5
+     */
+    public String(int[] codePoints, int offset, int count) {
+        if (codePoints == null) {
+            throw new NullPointerException("codePoints == null");
+        }
+        if ((offset | count) < 0 || count > codePoints.length - offset) {
+            throw failedBoundsCheck(codePoints.length, offset, count);
+        }
+        this.offset = 0;
+        this.value = new char[count * 2];
+        int end = offset + count;
+        int c = 0;
+        for (int i = offset; i < end; i++) {
+            c += Character.toChars(codePoints[i], this.value, c);
+        }
+        this.count = c;
+    }
+
+    /**
+     * Creates a {@code String} from the contents of the specified {@code
+     * StringBuilder}.
+     *
+     * @throws NullPointerException
+     *             if {@code stringBuilder == null}.
+     * @since 1.5
+     */
+    public String(StringBuilder stringBuilder) {
+        if (stringBuilder == null) {
+            throw new NullPointerException("stringBuilder == null");
+        }
+        this.offset = 0;
+        this.count = stringBuilder.length();
+        this.value = new char[this.count];
+        stringBuilder.getChars(0, this.count, this.value, 0);
+    }
+
+    /**
+     * Returns the character at the specified offset in this string.
+     *
+     * @param index
+     *            the zero-based index in this string.
+     * @return the character at the index.
+     * @throws IndexOutOfBoundsException
+     *             if {@code index < 0} or {@code index >= length()}.
+     */
+    public char charAt(int index) {
+        if (index < 0 || index >= count) {
+            throw indexAndLength(index);
+        }
+        return value[offset + index];
+    }
+
+    private StringIndexOutOfBoundsException indexAndLength(int index) {
+        throw new StringIndexOutOfBoundsException(this, index);
+    }
+
+    private StringIndexOutOfBoundsException startEndAndLength(int start, int end) {
+        throw new StringIndexOutOfBoundsException(this, start, end - start);
+    }
+
+    private StringIndexOutOfBoundsException failedBoundsCheck(int arrayLength, int offset, int count) {
+        throw new StringIndexOutOfBoundsException(arrayLength, offset, count);
+    }
+
+    /**
+     * This isn't equivalent to either of ICU's u_foldCase case folds, and thus any of the Unicode
+     * case folds, but it's what the RI uses.
+     */
+    private char foldCase(char ch) {
+        if (ch < 128) {
+            if ('A' <= ch && ch <= 'Z') {
+                return (char) (ch + ('a' - 'A'));
+            }
+            return ch;
+        }
+        return Character.toLowerCase(Character.toUpperCase(ch));
+    }
+
+    /**
+     * Compares the specified string to this string using the Unicode values of
+     * the characters. Returns 0 if the strings contain the same characters in
+     * the same order. Returns a negative integer if the first non-equal
+     * character in this string has a Unicode value which is less than the
+     * Unicode value of the character at the same position in the specified
+     * string, or if this string is a prefix of the specified string. Returns a
+     * positive integer if the first non-equal character in this string has a
+     * Unicode value which is greater than the Unicode value of the character at
+     * the same position in the specified string, or if the specified string is
+     * a prefix of this string.
+     *
+     * @param string
+     *            the string to compare.
+     * @return 0 if the strings are equal, a negative integer if this string is
+     *         before the specified string, or a positive integer if this string
+     *         is after the specified string.
+     * @throws NullPointerException
+     *             if {@code string} is {@code null}.
+     */
+    public native int compareTo(String string);
+
+    /**
+     * Compares the specified string to this string using the Unicode values of
+     * the characters, ignoring case differences. Returns 0 if the strings
+     * contain the same characters in the same order. Returns a negative integer
+     * if the first non-equal character in this string has a Unicode value which
+     * is less than the Unicode value of the character at the same position in
+     * the specified string, or if this string is a prefix of the specified
+     * string. Returns a positive integer if the first non-equal character in
+     * this string has a Unicode value which is greater than the Unicode value
+     * of the character at the same position in the specified string, or if the
+     * specified string is a prefix of this string.
+     *
+     * @param string
+     *            the string to compare.
+     * @return 0 if the strings are equal, a negative integer if this string is
+     *         before the specified string, or a positive integer if this string
+     *         is after the specified string.
+     * @throws NullPointerException
+     *             if {@code string} is {@code null}.
+     */
+    public int compareToIgnoreCase(String string) {
+        int o1 = offset, o2 = string.offset, result;
+        int end = offset + (count < string.count ? count : string.count);
+        char c1, c2;
+        char[] target = string.value;
+        while (o1 < end) {
+            if ((c1 = value[o1++]) == (c2 = target[o2++])) {
+                continue;
+            }
+            c1 = foldCase(c1);
+            c2 = foldCase(c2);
+            if ((result = c1 - c2) != 0) {
+                return result;
+            }
+        }
+        return count - string.count;
+    }
+
+    /**
+     * Concatenates this string and the specified string.
+     *
+     * @param string
+     *            the string to concatenate
+     * @return a new string which is the concatenation of this string and the
+     *         specified string.
+     */
+    public String concat(String string) {
+        if (string.count > 0 && count > 0) {
+            char[] buffer = new char[count + string.count];
+            System.arraycopy(value, offset, buffer, 0, count);
+            System.arraycopy(string.value, string.offset, buffer, count, string.count);
+            return new String(0, buffer.length, buffer);
+        }
+        return count == 0 ? string : this;
+    }
+
+    /**
+     * Creates a new string containing the characters in the specified character
+     * array. Modifying the character array after creating the string has no
+     * effect on the string.
+     *
+     * @param data
+     *            the array of characters.
+     * @return the new string.
+     * @throws NullPointerException
+     *             if {@code data} is {@code null}.
+     */
+    public static String copyValueOf(char[] data) {
+        return new String(data, 0, data.length);
+    }
+
+    /**
+     * Creates a new string containing the specified characters in the character
+     * array. Modifying the character array after creating the string has no
+     * effect on the string.
+     *
+     * @param data
+     *            the array of characters.
+     * @param start
+     *            the starting offset in the character array.
+     * @param length
+     *            the number of characters to use.
+     * @return the new string.
+     * @throws NullPointerException
+     *             if {@code data} is {@code null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code length < 0, start < 0} or {@code start + length >
+     *             data.length}.
+     */
+    public static String copyValueOf(char[] data, int start, int length) {
+        return new String(data, start, length);
+    }
+
+    /**
+     * Compares the specified string to this string to determine if the
+     * specified string is a suffix.
+     *
+     * @param suffix
+     *            the suffix to look for.
+     * @return {@code true} if the specified string is a suffix of this string,
+     *         {@code false} otherwise.
+     * @throws NullPointerException
+     *             if {@code suffix} is {@code null}.
+     */
+    public boolean endsWith(String suffix) {
+        return regionMatches(count - suffix.count, suffix, 0, suffix.count);
+    }
+
+    /**
+     * Compares the specified object to this string and returns true if they are
+     * equal. The object must be an instance of string with the same characters
+     * in the same order.
+     *
+     * @param other
+     *            the object to compare.
+     * @return {@code true} if the specified object is equal to this string,
+     *         {@code false} otherwise.
+     * @see #hashCode
+     */
+    @Override public boolean equals(Object other) {
+        if (other == this) {
+          return true;
+        }
+        if (other instanceof String) {
+            String s = (String)other;
+            int count = this.count;
+            if (s.count != count) {
+                return false;
+            }
+            // TODO: we want to avoid many boundchecks in the loop below
+            // for long Strings until we have array equality intrinsic.
+            // Bad benchmarks just push .equals without first getting a
+            // hashCode hit (unlike real world use in a Hashtable). Filter
+            // out these long strings here. When we get the array equality
+            // intrinsic then remove this use of hashCode.
+            if (hashCode() != s.hashCode()) {
+                return false;
+            }
+            char[] value1 = value;
+            int offset1 = offset;
+            char[] value2 = s.value;
+            int offset2 = s.offset;
+            for (int end = offset1 + count; offset1 < end; ) {
+                if (value1[offset1] != value2[offset2]) {
+                    return false;
+                }
+                offset1++;
+                offset2++;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Compares the specified string to this string ignoring the case of the
+     * characters and returns true if they are equal.
+     *
+     * @param string
+     *            the string to compare.
+     * @return {@code true} if the specified string is equal to this string,
+     *         {@code false} otherwise.
+     */
+    @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
+    public boolean equalsIgnoreCase(String string) {
+        if (string == this) {
+            return true;
+        }
+        if (string == null || count != string.count) {
+            return false;
+        }
+        int o1 = offset, o2 = string.offset;
+        int end = offset + count;
+        char[] target = string.value;
+        while (o1 < end) {
+            char c1 = value[o1++];
+            char c2 = target[o2++];
+            if (c1 != c2 && foldCase(c1) != foldCase(c2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Mangles this string into a byte array by stripping the high order bits from
+     * each character. Use {@link #getBytes()} or {@link #getBytes(String)} instead.
+     *
+     * @param start
+     *            the starting offset of characters to copy.
+     * @param end
+     *            the ending offset of characters to copy.
+     * @param data
+     *            the destination byte array.
+     * @param index
+     *            the starting offset in the destination byte array.
+     * @throws NullPointerException
+     *             if {@code data} is {@code null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code start < 0}, {@code end > length()}, {@code index <
+     *             0} or {@code end - start > data.length - index}.
+     * @deprecated Use {@link #getBytes()} or {@link #getBytes(String)}
+     */
+    @Deprecated
+    public void getBytes(int start, int end, byte[] data, int index) {
+        // Note: last character not copied!
+        if (start >= 0 && start <= end && end <= count) {
+            end += offset;
+            try {
+                for (int i = offset + start; i < end; i++) {
+                    data[index++] = (byte) value[i];
+                }
+            } catch (ArrayIndexOutOfBoundsException ignored) {
+                throw failedBoundsCheck(data.length, index, end - start);
+            }
+        } else {
+            throw startEndAndLength(start, end);
+        }
+    }
+
+    /**
+     * Returns a new byte array containing the characters of this string encoded using the
+     * system's {@link java.nio.charset.Charset#defaultCharset default charset}.
+     *
+     * <p>The behavior when this string cannot be represented in the system's default charset
+     * is unspecified. In practice, when the default charset is UTF-8 (as it is on Android),
+     * all strings can be encoded.
+     */
+    public byte[] getBytes() {
+        return getBytes(Charset.defaultCharset());
+    }
+
+    /**
+     * Returns a new byte array containing the characters of this string encoded using the
+     * named charset.
+     *
+     * <p>The behavior when this string cannot be represented in the named charset
+     * is unspecified. Use {@link java.nio.charset.CharsetEncoder} for more control.
+     *
+     * @throws UnsupportedEncodingException if the charset is not supported
+     */
+    public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
+        return getBytes(Charset.forNameUEE(charsetName));
+    }
+
+    /**
+     * Returns a new byte array containing the characters of this string encoded using the
+     * given charset.
+     *
+     * <p>The behavior when this string cannot be represented in the given charset
+     * is to replace malformed input and unmappable characters with the charset's default
+     * replacement byte array. Use {@link java.nio.charset.CharsetEncoder} for more control.
+     *
+     * @since 1.6
+     */
+    public byte[] getBytes(Charset charset) {
+        String canonicalCharsetName = charset.name();
+        if (canonicalCharsetName.equals("UTF-8")) {
+            return Charsets.toUtf8Bytes(value, offset, count);
+        } else if (canonicalCharsetName.equals("ISO-8859-1")) {
+            return Charsets.toIsoLatin1Bytes(value, offset, count);
+        } else if (canonicalCharsetName.equals("US-ASCII")) {
+            return Charsets.toAsciiBytes(value, offset, count);
+        } else if (canonicalCharsetName.equals("UTF-16BE")) {
+            return Charsets.toBigEndianUtf16Bytes(value, offset, count);
+        } else {
+            CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);
+            ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());
+            byte[] bytes = new byte[buffer.limit()];
+            buffer.get(bytes);
+            return bytes;
+        }
+    }
+
+    /**
+     * Copies the specified characters in this string to the character array
+     * starting at the specified offset in the character array.
+     *
+     * @param start
+     *            the starting offset of characters to copy.
+     * @param end
+     *            the ending offset of characters to copy.
+     * @param buffer
+     *            the destination character array.
+     * @param index
+     *            the starting offset in the character array.
+     * @throws NullPointerException
+     *             if {@code buffer} is {@code null}.
+     * @throws IndexOutOfBoundsException
+     *             if {@code start < 0}, {@code end > length()}, {@code start >
+     *             end}, {@code index < 0}, {@code end - start > buffer.length -
+     *             index}
+     */
+    public void getChars(int start, int end, char[] buffer, int index) {
+        // Note: last character not copied!
+        if (start >= 0 && start <= end && end <= count) {
+            System.arraycopy(value, start + offset, buffer, index, end - start);
+        } else {
+            // We throw StringIndexOutOfBoundsException rather than System.arraycopy's AIOOBE.
+            throw startEndAndLength(start, end);
+        }
+    }
+
+    /**
+     * Version of getChars without bounds checks, for use by other classes
+     * within the java.lang package only.  The caller is responsible for
+     * ensuring that start >= 0 && start <= end && end <= count.
+     */
+    void _getChars(int start, int end, char[] buffer, int index) {
+        // NOTE last character not copied!
+        System.arraycopy(value, start + offset, buffer, index, end - start);
+    }
+
+    @Override public int hashCode() {
+        int hash = hashCode;
+        if (hash == 0) {
+            if (count == 0) {
+                return 0;
+            }
+            final int end = count + offset;
+            final char[] chars = value;
+            for (int i = offset; i < end; ++i) {
+                hash = 31*hash + chars[i];
+            }
+            hashCode = hash;
+        }
+        return hash;
+    }
+
+    /**
+     * Searches in this string for the first index of the specified character.
+     * The search for the character starts at the beginning and moves towards
+     * the end of this string.
+     *
+     * @param c
+     *            the character to find.
+     * @return the index in this string of the specified character, -1 if the
+     *         character isn't found.
+     */
+    public int indexOf(int c) {
+        // TODO: just "return indexOf(c, 0);" when the JIT can inline that deep.
+        if (c > 0xffff) {
+            return indexOfSupplementary(c, 0);
+        }
+        return fastIndexOf(c, 0);
+    }
+
+    /**
+     * Searches in this string for the index of the specified character. The
+     * search for the character starts at the specified offset and moves towards
+     * the end of this string.
+     *
+     * @param c
+     *            the character to find.
+     * @param start
+     *            the starting offset.
+     * @return the index in this string of the specified character, -1 if the
+     *         character isn't found.
+     */
+    public int indexOf(int c, int start) {
+        if (c > 0xffff) {
+            return indexOfSupplementary(c, start);
+        }
+        return fastIndexOf(c, start);
+    }
+
+    private native int fastIndexOf(int c, int start);
+
+    private int indexOfSupplementary(int c, int start) {
+        if (!Character.isSupplementaryCodePoint(c)) {
+            return -1;
+        }
+        char[] chars = Character.toChars(c);
+        String needle = new String(0, chars.length, chars);
+        return indexOf(needle, start);
+    }
+
+    /**
+     * Searches in this string for the first index of the specified string. The
+     * search for the string starts at the beginning and moves towards the end
+     * of this string.
+     *
+     * @param string
+     *            the string to find.
+     * @return the index of the first character of the specified string in this
+     *         string, -1 if the specified string is not a substring.
+     * @throws NullPointerException
+     *             if {@code string} is {@code null}.
+     */
+    public int indexOf(String string) {
+        int start = 0;
+        int subCount = string.count;
+        int _count = count;
+        if (subCount > 0) {
+            if (subCount > _count) {
+                return -1;
+            }
+            char[] target = string.value;
+            int subOffset = string.offset;
+            char firstChar = target[subOffset];
+            int end = subOffset + subCount;
+            while (true) {
+                int i = indexOf(firstChar, start);
+                if (i == -1 || subCount + i > _count) {
+                    return -1; // handles subCount > count || start >= count
+                }
+                int o1 = offset + i, o2 = subOffset;
+                char[] _value = value;
+                while (++o2 < end && _value[++o1] == target[o2]) {
+                    // Intentionally empty
+                }
+                if (o2 == end) {
+                    return i;
+                }
+                start = i + 1;
+            }
+        }
+        return start < _count ? start : _count;
+    }
+
+    /**
+     * Searches in this string for the index of the specified string. The search
+     * for the string starts at the specified offset and moves towards the end
+     * of this string.
+     *
+     * @param subString
+     *            the string to find.
+     * @param start
+     *            the starting offset.
+     * @return the index of the first character of the specified string in this
+     *         string, -1 if the specified string is not a substring.
+     * @throws NullPointerException
+     *             if {@code subString} is {@code null}.
+     */
+    public int indexOf(String subString, int start) {
+        if (start < 0) {
+            start = 0;
+        }
+        int subCount = subString.count;
+        int _count = count;
+        if (subCount > 0) {
+            if (subCount + start > _count) {
+                return -1;
+            }
+            char[] target = subString.value;
+            int subOffset = subString.offset;
+            char firstChar = target[subOffset];
+            int end = subOffset + subCount;
+            while (true) {
+                int i = indexOf(firstChar, start);
+                if (i == -1 || subCount + i > _count) {
+                    return -1; // handles subCount > count || start >= count
+                }
+                int o1 = offset + i, o2 = subOffset;
+                char[] _value = value;
+                while (++o2 < end && _value[++o1] == target[o2]) {
+                    // Intentionally empty
+                }
+                if (o2 == end) {
+                    return i;
+                }
+                start = i + 1;
+            }
+        }
+        return start < _count ? start : _count;
+    }
+
+    /**
+     * Returns an interned string equal to this string. The VM maintains an internal set of
+     * unique strings. All string literals found in loaded classes'
+     * constant pools are automatically interned. Manually-interned strings are only weakly
+     * referenced, so calling {@code intern} won't lead to unwanted retention.
+     *
+     * <p>Interning is typically used because it guarantees that for interned strings
+     * {@code a} and {@code b}, {@code a.equals(b)} can be simplified to
+     * {@code a == b}. (This is not true of non-interned strings.)
+     *
+     * <p>Many applications find it simpler and more convenient to use an explicit
+     * {@link java.util.HashMap} to implement their own pools.
+     */
+    public native String intern();
+
+    /**
+     * Returns true if the length of this string is 0.
+     *
+     * @since 1.6
+     */
+    public boolean isEmpty() {
+        return count == 0;
+    }
+
+    /**
+     * Returns the last index of the code point {@code c}, or -1.
+     * The search for the character starts at the end and moves towards the
+     * beginning of this string.
+     */
+    public int lastIndexOf(int c) {
+        if (c > 0xffff) {
+            return lastIndexOfSupplementary(c, Integer.MAX_VALUE);
+        }
+        int _count = count;
+        int _offset = offset;
+        char[] _value = value;
+        for (int i = _offset + _count - 1; i >= _offset; --i) {
+            if (_value[i] == c) {
+                return i - _offset;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the last index of the code point {@code c}, or -1.
+     * The search for the character starts at offset {@code start} and moves towards
+     * the beginning of this string.
+     */
+    public int lastIndexOf(int c, int start) {
+        if (c > 0xffff) {
+            return lastIndexOfSupplementary(c, start);
+        }
+        int _count = count;
+        int _offset = offset;
+        char[] _value = value;
+        if (start >= 0) {
+            if (start >= _count) {
+                start = _count - 1;
+            }
+            for (int i = _offset + start; i >= _offset; --i) {
+                if (_value[i] == c) {
+                    return i - _offset;
+                }
+            }
+        }
+        return -1;
+    }
+
+    private int lastIndexOfSupplementary(int c, int start) {
+        if (!Character.isSupplementaryCodePoint(c)) {
+            return -1;
+        }
+        char[] chars = Character.toChars(c);
+        String needle = new String(0, chars.length, chars);
+        return lastIndexOf(needle, start);
+    }
+
+    /**
+     * Searches in this string for the last index of the specified string. The
+     * search for the string starts at the end and moves towards the beginning
+     * of this string.
+     *
+     * @param string
+     *            the string to find.
+     * @return the index of the first character of the specified string in this
+     *         string, -1 if the specified string is not a substring.
+     * @throws NullPointerException
+     *             if {@code string} is {@code null}.
+     */
+    public int lastIndexOf(String string) {
+        // Use count instead of count - 1 so lastIndexOf("") returns count
+        return lastIndexOf(string, count);
+    }
+
+    /**
+     * Searches in this string for the index of the specified string. The search
+     * for the string starts at the specified offset and moves towards the
+     * beginning of this string.
+     *
+     * @param subString
+     *            the string to find.
+     * @param start
+     *            the starting offset.
+     * @return the index of the first character of the specified string in this
+     *         string , -1 if the specified string is not a substring.
+     * @throws NullPointerException
+     *             if {@code subString} is {@code null}.
+     */
+    public int lastIndexOf(String subString, int start) {
+        int subCount = subString.count;
+        if (subCount <= count && start >= 0) {
+            if (subCount > 0) {
+                if (start > count - subCount) {
+                    start = count - subCount;
+                }
+                // count and subCount are both >= 1
+                char[] target = subString.value;
+                int subOffset = subString.offset;
+                char firstChar = target[subOffset];
+                int end = subOffset + subCount;
+                while (true) {
+                    int i = lastIndexOf(firstChar, start);
+                    if (i == -1) {
+                        return -1;
+                    }
+                    int o1 = offset + i, o2 = subOffset;
+                    while (++o2 < end && value[++o1] == target[o2]) {
+                        // Intentionally empty
+                    }
+                    if (o2 == end) {
+                        return i;
+                    }
+                    start = i - 1;
+                }
+            }
+            return start < count ? start : count;
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the number of characters in this string.
+     */
+    public int length() {
+        return count;
+    }
+
+    /**
+     * Compares the specified string to this string and compares the specified
+     * range of characters to determine if they are the same.
+     *
+     * @param thisStart
+     *            the starting offset in this string.
+     * @param string
+     *            the string to compare.
+     * @param start
+     *            the starting offset in the specified string.
+     * @param length
+     *            the number of characters to compare.
+     * @return {@code true} if the ranges of characters are equal, {@code false}
+     *         otherwise
+     * @throws NullPointerException
+     *             if {@code string} is {@code null}.
+     */
+    public boolean regionMatches(int thisStart, String string, int start, int length) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+        if (start < 0 || string.count - start < length) {
+            return false;
+        }
+        if (thisStart < 0 || count - thisStart < length) {
+            return false;
+        }
+        if (length <= 0) {
+            return true;
+        }
+        int o1 = offset + thisStart, o2 = string.offset + start;
+        char[] value1 = value;
+        char[] value2 = string.value;
+        for (int i = 0; i < length; ++i) {
+            if (value1[o1 + i] != value2[o2 + i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Compares the specified string to this string and compares the specified
+     * range of characters to determine if they are the same. When ignoreCase is
+     * true, the case of the characters is ignored during the comparison.
+     *
+     * @param ignoreCase
+     *            specifies if case should be ignored.
+     * @param thisStart
+     *            the starting offset in this string.
+     * @param string
+     *            the string to compare.
+     * @param start
+     *            the starting offset in the specified string.
+     * @param length
+     *            the number of characters to compare.
+     * @return {@code true} if the ranges of characters are equal, {@code false}
+     *         otherwise.
+     * @throws NullPointerException
+     *             if {@code string} is {@code null}.
+     */
+    public boolean regionMatches(boolean ignoreCase, int thisStart, String string, int start, int length) {
+        if (!ignoreCase) {
+            return regionMatches(thisStart, string, start, length);
+        }
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+        if (thisStart < 0 || length > count - thisStart) {
+            return false;
+        }
+        if (start < 0 || length > string.count - start) {
+            return false;
+        }
+        thisStart += offset;
+        start += string.offset;
+        int end = thisStart + length;
+        char[] target = string.value;
+        while (thisStart < end) {
+            char c1 = value[thisStart++];
+            char c2 = target[start++];
+            if (c1 != c2 && foldCase(c1) != foldCase(c2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Copies this string replacing occurrences of the specified character with
+     * another character.
+     *
+     * @param oldChar
+     *            the character to replace.
+     * @param newChar
+     *            the replacement character.
+     * @return a new string with occurrences of oldChar replaced by newChar.
+     */
+    public String replace(char oldChar, char newChar) {
+        char[] buffer = value;
+        int _offset = offset;
+        int _count = count;
+
+        int idx = _offset;
+        int last = _offset + _count;
+        boolean copied = false;
+        while (idx < last) {
+            if (buffer[idx] == oldChar) {
+                if (!copied) {
+                    char[] newBuffer = new char[_count];
+                    System.arraycopy(buffer, _offset, newBuffer, 0, _count);
+                    buffer = newBuffer;
+                    idx -= _offset;
+                    last -= _offset;
+                    copied = true;
+                }
+                buffer[idx] = newChar;
+            }
+            idx++;
+        }
+
+        return copied ? new String(0, count, buffer) : this;
+    }
+
+    /**
+     * Copies this string replacing occurrences of the specified target sequence
+     * with another sequence. The string is processed from the beginning to the
+     * end.
+     *
+     * @param target
+     *            the sequence to replace.
+     * @param replacement
+     *            the replacement sequence.
+     * @return the resulting string.
+     * @throws NullPointerException
+     *             if {@code target} or {@code replacement} is {@code null}.
+     */
+    public String replace(CharSequence target, CharSequence replacement) {
+        if (target == null) {
+            throw new NullPointerException("target == null");
+        }
+        if (replacement == null) {
+            throw new NullPointerException("replacement == null");
+        }
+
+        String targetString = target.toString();
+        int matchStart = indexOf(targetString, 0);
+        if (matchStart == -1) {
+            // If there's nothing to replace, return the original string untouched.
+            return this;
+        }
+
+        String replacementString = replacement.toString();
+
+        // The empty target matches at the start and end and between each character.
+        int targetLength = targetString.length();
+        if (targetLength == 0) {
+            // The result contains the original 'count' characters, a copy of the
+            // replacement string before every one of those characters, and a final
+            // copy of the replacement string at the end.
+            int resultLength = count + (count + 1) * replacementString.length();
+            StringBuilder result = new StringBuilder(resultLength);
+            result.append(replacementString);
+            int end = offset + count;
+            for (int i = offset; i != end; ++i) {
+                result.append(value[i]);
+                result.append(replacementString);
+            }
+            return result.toString();
+        }
+
+        StringBuilder result = new StringBuilder(count);
+        int searchStart = 0;
+        do {
+            // Copy characters before the match...
+            result.append(value, offset + searchStart, matchStart - searchStart);
+            // Insert the replacement...
+            result.append(replacementString);
+            // And skip over the match...
+            searchStart = matchStart + targetLength;
+        } while ((matchStart = indexOf(targetString, searchStart)) != -1);
+        // Copy any trailing chars...
+        result.append(value, offset + searchStart, count - searchStart);
+        return result.toString();
+    }
+
+    /**
+     * Compares the specified string to this string to determine if the
+     * specified string is a prefix.
+     *
+     * @param prefix
+     *            the string to look for.
+     * @return {@code true} if the specified string is a prefix of this string,
+     *         {@code false} otherwise
+     * @throws NullPointerException
+     *             if {@code prefix} is {@code null}.
+     */
+    public boolean startsWith(String prefix) {
+        return startsWith(prefix, 0);
+    }
+
+    /**
+     * Compares the specified string to this string, starting at the specified
+     * offset, to determine if the specified string is a prefix.
+     *
+     * @param prefix
+     *            the string to look for.
+     * @param start
+     *            the starting offset.
+     * @return {@code true} if the specified string occurs in this string at the
+     *         specified offset, {@code false} otherwise.
+     * @throws NullPointerException
+     *             if {@code prefix} is {@code null}.
+     */
+    public boolean startsWith(String prefix, int start) {
+        return regionMatches(start, prefix, 0, prefix.count);
+    }
+
+    /**
+     * Returns a string containing a suffix of this string. The returned string
+     * shares this string's <a href="#backing_array">backing array</a>.
+     *
+     * @param start
+     *            the offset of the first character.
+     * @return a new string containing the characters from start to the end of
+     *         the string.
+     * @throws IndexOutOfBoundsException
+     *             if {@code start < 0} or {@code start > length()}.
+     */
+    public String substring(int start) {
+        if (start == 0) {
+            return this;
+        }
+        if (start >= 0 && start <= count) {
+            return new String(offset + start, count - start, value);
+        }
+        throw indexAndLength(start);
+    }
+
+    /**
+     * Returns a string containing a subsequence of characters from this string.
+     * The returned string shares this string's <a href="#backing_array">backing
+     * array</a>.
+     *
+     * @param start
+     *            the offset of the first character.
+     * @param end
+     *            the offset one past the last character.
+     * @return a new string containing the characters from start to end - 1
+     * @throws IndexOutOfBoundsException
+     *             if {@code start < 0}, {@code start > end} or {@code end >
+     *             length()}.
+     */
+    public String substring(int start, int end) {
+        if (start == 0 && end == count) {
+            return this;
+        }
+        // NOTE last character not copied!
+        // Fast range check.
+        if (start >= 0 && start <= end && end <= count) {
+            return new String(offset + start, end - start, value);
+        }
+        throw startEndAndLength(start, end);
+    }
+
+    /**
+     * Returns a new {@code char} array containing a copy of the characters in this string.
+     * This is expensive and rarely useful. If you just want to iterate over the characters in
+     * the string, use {@link #charAt} instead.
+     */
+    public char[] toCharArray() {
+        char[] buffer = new char[count];
+        System.arraycopy(value, offset, buffer, 0, count);
+        return buffer;
+    }
+
+    /**
+     * Converts this string to lower case, using the rules of the user's default locale.
+     * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
+     *
+     * @return a new lower case string, or {@code this} if it's already all lower case.
+     */
+    public String toLowerCase() {
+        return CaseMapper.toLowerCase(Locale.getDefault(), this, value, offset, count);
+    }
+
+    /**
+     * Converts this string to lower case, using the rules of {@code locale}.
+     *
+     * <p>Most case mappings are unaffected by the language of a {@code Locale}. Exceptions include
+     * dotted and dotless I in Azeri and Turkish locales, and dotted and dotless I and J in
+     * Lithuanian locales. On the other hand, it isn't necessary to provide a Greek locale to get
+     * correct case mapping of Greek characters: any locale will do.
+     *
+     * <p>See <a href="http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt">http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt</a>
+     * for full details of context- and language-specific special cases.
+     *
+     * @return a new lower case string, or {@code this} if it's already all lower case.
+     */
+    public String toLowerCase(Locale locale) {
+        return CaseMapper.toLowerCase(locale, this, value, offset, count);
+    }
+
+    /**
+     * Returns this string.
+     */
+    @Override
+    public String toString() {
+        return this;
+    }
+
+    /**
+     * Converts this this string to upper case, using the rules of the user's default locale.
+     * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
+     *
+     * @return a new upper case string, or {@code this} if it's already all upper case.
+     */
+    public String toUpperCase() {
+        return CaseMapper.toUpperCase(Locale.getDefault(), this, value, offset, count);
+    }
+
+    /**
+     * Converts this this string to upper case, using the rules of {@code locale}.
+     *
+     * <p>Most case mappings are unaffected by the language of a {@code Locale}. Exceptions include
+     * dotted and dotless I in Azeri and Turkish locales, and dotted and dotless I and J in
+     * Lithuanian locales. On the other hand, it isn't necessary to provide a Greek locale to get
+     * correct case mapping of Greek characters: any locale will do.
+     *
+     * <p>See <a href="http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt">http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt</a>
+     * for full details of context- and language-specific special cases.
+     *
+     * @return a new upper case string, or {@code this} if it's already all upper case.
+     */
+    public String toUpperCase(Locale locale) {
+        return CaseMapper.toUpperCase(locale, this, value, offset, count);
+    }
+
+    /**
+     * Copies this string removing white space characters from the beginning and
+     * end of the string.
+     *
+     * @return a new string with characters <code><= \\u0020</code> removed from
+     *         the beginning and the end.
+     */
+    public String trim() {
+        int start = offset, last = offset + count - 1;
+        int end = last;
+        while ((start <= end) && (value[start] <= ' ')) {
+            start++;
+        }
+        while ((end >= start) && (value[end] <= ' ')) {
+            end--;
+        }
+        if (start == offset && end == last) {
+            return this;
+        }
+        return new String(start, end - start + 1, value);
+    }
+
+    /**
+     * Creates a new string containing the characters in the specified character
+     * array. Modifying the character array after creating the string has no
+     * effect on the string.
+     *
+     * @param data
+     *            the array of characters.
+     * @return the new string.
+     * @throws NullPointerException
+     *             if {@code data} is {@code null}.
+     */
+    public static String valueOf(char[] data) {
+        return new String(data, 0, data.length);
+    }
+
+    /**
+     * Creates a new string containing the specified characters in the character
+     * array. Modifying the character array after creating the string has no
+     * effect on the string.
+     *
+     * @param data
+     *            the array of characters.
+     * @param start
+     *            the starting offset in the character array.
+     * @param length
+     *            the number of characters to use.
+     * @return the new string.
+     * @throws IndexOutOfBoundsException
+     *             if {@code length < 0}, {@code start < 0} or {@code start +
+     *             length > data.length}
+     * @throws NullPointerException
+     *             if {@code data} is {@code null}.
+     */
+    public static String valueOf(char[] data, int start, int length) {
+        return new String(data, start, length);
+    }
+
+    /**
+     * Converts the specified character to its string representation.
+     *
+     * @param value
+     *            the character.
+     * @return the character converted to a string.
+     */
+    public static String valueOf(char value) {
+        String s;
+        if (value < 128) {
+            s = new String(value, 1, ASCII);
+        } else {
+            s = new String(0, 1, new char[] { value });
+        }
+        s.hashCode = value;
+        return s;
+    }
+
+    /**
+     * Converts the specified double to its string representation.
+     *
+     * @param value
+     *            the double.
+     * @return the double converted to a string.
+     */
+    public static String valueOf(double value) {
+        return Double.toString(value);
+    }
+
+    /**
+     * Converts the specified float to its string representation.
+     *
+     * @param value
+     *            the float.
+     * @return the float converted to a string.
+     */
+    public static String valueOf(float value) {
+        return Float.toString(value);
+    }
+
+    /**
+     * Converts the specified integer to its string representation.
+     *
+     * @param value
+     *            the integer.
+     * @return the integer converted to a string.
+     */
+    public static String valueOf(int value) {
+        return Integer.toString(value);
+    }
+
+    /**
+     * Converts the specified long to its string representation.
+     *
+     * @param value
+     *            the long.
+     * @return the long converted to a string.
+     */
+    public static String valueOf(long value) {
+        return Long.toString(value);
+    }
+
+    /**
+     * Converts the specified object to its string representation. If the object
+     * is null return the string {@code "null"}, otherwise use {@code
+     * toString()} to get the string representation.
+     *
+     * @param value
+     *            the object.
+     * @return the object converted to a string, or the string {@code "null"}.
+     */
+    public static String valueOf(Object value) {
+        return value != null ? value.toString() : "null";
+    }
+
+    /**
+     * Converts the specified boolean to its string representation. When the
+     * boolean is {@code true} return {@code "true"}, otherwise return {@code
+     * "false"}.
+     *
+     * @param value
+     *            the boolean.
+     * @return the boolean converted to a string.
+     */
+    public static String valueOf(boolean value) {
+        return value ? "true" : "false";
+    }
+
+    /**
+     * Returns whether the characters in the StringBuffer {@code strbuf} are the
+     * same as those in this string.
+     *
+     * @param strbuf
+     *            the StringBuffer to compare this string to.
+     * @return {@code true} if the characters in {@code strbuf} are identical to
+     *         those in this string. If they are not, {@code false} will be
+     *         returned.
+     * @throws NullPointerException
+     *             if {@code strbuf} is {@code null}.
+     * @since 1.4
+     */
+    public boolean contentEquals(StringBuffer strbuf) {
+        synchronized (strbuf) {
+            int size = strbuf.length();
+            if (count != size) {
+                return false;
+            }
+            return regionMatches(0, new String(0, size, strbuf.getValue()), 0,
+                    size);
+        }
+    }
+
+    /**
+     * Compares a {@code CharSequence} to this {@code String} to determine if
+     * their contents are equal.
+     *
+     * @param cs
+     *            the character sequence to compare to.
+     * @return {@code true} if equal, otherwise {@code false}
+     * @since 1.5
+     */
+    public boolean contentEquals(CharSequence cs) {
+        if (cs == null) {
+            throw new NullPointerException("cs == null");
+        }
+
+        int len = cs.length();
+
+        if (len != count) {
+            return false;
+        }
+
+        if (len == 0 && count == 0) {
+            return true; // since both are empty strings
+        }
+
+        return regionMatches(0, cs.toString(), 0, len);
+    }
+
+    /**
+     * Tests whether this string matches the given {@code regularExpression}. This method returns
+     * true only if the regular expression matches the <i>entire</i> input string. A common mistake is
+     * to assume that this method behaves like {@link #contains}; if you want to match anywhere
+     * within the input string, you need to add {@code .*} to the beginning and end of your
+     * regular expression. See {@link Pattern#matches}.
+     *
+     * <p>If the same regular expression is to be used for multiple operations, it may be more
+     * efficient to reuse a compiled {@code Pattern}.
+     *
+     * @throws PatternSyntaxException
+     *             if the syntax of the supplied regular expression is not
+     *             valid.
+     * @throws NullPointerException if {@code regularExpression == null}
+     * @since 1.4
+     */
+    public boolean matches(String regularExpression) {
+        return Pattern.matches(regularExpression, this);
+    }
+
+    /**
+     * Replaces all matches for {@code regularExpression} within this string with the given
+     * {@code replacement}.
+     * See {@link Pattern} for regular expression syntax.
+     *
+     * <p>If the same regular expression is to be used for multiple operations, it may be more
+     * efficient to reuse a compiled {@code Pattern}.
+     *
+     * @throws PatternSyntaxException
+     *             if the syntax of the supplied regular expression is not
+     *             valid.
+     * @throws NullPointerException if {@code regularExpression == null}
+     * @see Pattern
+     * @since 1.4
+     */
+    public String replaceAll(String regularExpression, String replacement) {
+        return Pattern.compile(regularExpression).matcher(this).replaceAll(replacement);
+    }
+
+    /**
+     * Replaces the first match for {@code regularExpression} within this string with the given
+     * {@code replacement}.
+     * See {@link Pattern} for regular expression syntax.
+     *
+     * <p>If the same regular expression is to be used for multiple operations, it may be more
+     * efficient to reuse a compiled {@code Pattern}.
+     *
+     * @throws PatternSyntaxException
+     *             if the syntax of the supplied regular expression is not
+     *             valid.
+     * @throws NullPointerException if {@code regularExpression == null}
+     * @see Pattern
+     * @since 1.4
+     */
+    public String replaceFirst(String regularExpression, String replacement) {
+        return Pattern.compile(regularExpression).matcher(this).replaceFirst(replacement);
+    }
+
+    /**
+     * Splits this string using the supplied {@code regularExpression}.
+     * Equivalent to {@code split(regularExpression, 0)}.
+     * See {@link Pattern#split(CharSequence, int)} for an explanation of {@code limit}.
+     * See {@link Pattern} for regular expression syntax.
+     *
+     * <p>If the same regular expression is to be used for multiple operations, it may be more
+     * efficient to reuse a compiled {@code Pattern}.
+     *
+     * @throws NullPointerException if {@code regularExpression ==  null}
+     * @throws PatternSyntaxException
+     *             if the syntax of the supplied regular expression is not
+     *             valid.
+     * @see Pattern
+     * @since 1.4
+     */
+    public String[] split(String regularExpression) {
+        return split(regularExpression, 0);
+    }
+
+    /**
+     * Splits this string using the supplied {@code regularExpression}.
+     * See {@link Pattern#split(CharSequence, int)} for an explanation of {@code limit}.
+     * See {@link Pattern} for regular expression syntax.
+     *
+     * <p>If the same regular expression is to be used for multiple operations, it may be more
+     * efficient to reuse a compiled {@code Pattern}.
+     *
+     * @throws NullPointerException if {@code regularExpression ==  null}
+     * @throws PatternSyntaxException
+     *             if the syntax of the supplied regular expression is not
+     *             valid.
+     * @since 1.4
+     */
+    public String[] split(String regularExpression, int limit) {
+        String[] result = java.util.regex.Splitter.fastSplit(regularExpression, this, limit);
+        return result != null ? result : Pattern.compile(regularExpression).split(this, limit);
+    }
+
+    /**
+     * Has the same result as the substring function, but is present so that
+     * string may implement the CharSequence interface.
+     *
+     * @param start
+     *            the offset the first character.
+     * @param end
+     *            the offset of one past the last character to include.
+     * @return the subsequence requested.
+     * @throws IndexOutOfBoundsException
+     *             if {@code start < 0}, {@code end < 0}, {@code start > end} or
+     *             {@code end > length()}.
+     * @see java.lang.CharSequence#subSequence(int, int)
+     * @since 1.4
+     */
+    public CharSequence subSequence(int start, int end) {
+        return substring(start, end);
+    }
+
+    /**
+     * Returns the Unicode code point at the given {@code index}.
+     *
+     * @throws IndexOutOfBoundsException if {@code index < 0 || index >= length()}
+     * @see Character#codePointAt(char[], int, int)
+     * @since 1.5
+     */
+    public int codePointAt(int index) {
+        if (index < 0 || index >= count) {
+            throw indexAndLength(index);
+        }
+        return Character.codePointAt(value, offset + index, offset + count);
+    }
+
+    /**
+     * Returns the Unicode code point that precedes the given {@code index}.
+     *
+     * @throws IndexOutOfBoundsException if {@code index < 1 || index > length()}
+     * @see Character#codePointBefore(char[], int, int)
+     * @since 1.5
+     */
+    public int codePointBefore(int index) {
+        if (index < 1 || index > count) {
+            throw indexAndLength(index);
+        }
+        return Character.codePointBefore(value, offset + index, offset);
+    }
+
+    /**
+     * Calculates the number of Unicode code points between {@code start}
+     * and {@code end}.
+     *
+     * @param start
+     *            the inclusive beginning index of the subsequence.
+     * @param end
+     *            the exclusive end index of the subsequence.
+     * @return the number of Unicode code points in the subsequence.
+     * @throws IndexOutOfBoundsException
+     *         if {@code start < 0 || end > length() || start > end}
+     * @see Character#codePointCount(CharSequence, int, int)
+     * @since 1.5
+     */
+    public int codePointCount(int start, int end) {
+        if (start < 0 || end > count || start > end) {
+            throw startEndAndLength(start, end);
+        }
+        return Character.codePointCount(value, offset + start, end - start);
+    }
+
+    /**
+     * Determines if this {@code String} contains the sequence of characters in
+     * the {@code CharSequence} passed.
+     *
+     * @param cs
+     *            the character sequence to search for.
+     * @return {@code true} if the sequence of characters are contained in this
+     *         string, otherwise {@code false}.
+     * @since 1.5
+     */
+    public boolean contains(CharSequence cs) {
+        if (cs == null) {
+            throw new NullPointerException("cs == null");
+        }
+        return indexOf(cs.toString()) >= 0;
+    }
+
+    /**
+     * Returns the index within this object that is offset from {@code index} by
+     * {@code codePointOffset} code points.
+     *
+     * @param index
+     *            the index within this object to calculate the offset from.
+     * @param codePointOffset
+     *            the number of code points to count.
+     * @return the index within this object that is the offset.
+     * @throws IndexOutOfBoundsException
+     *             if {@code index} is negative or greater than {@code length()}
+     *             or if there aren't enough code points before or after {@code
+     *             index} to match {@code codePointOffset}.
+     * @since 1.5
+     */
+    public int offsetByCodePoints(int index, int codePointOffset) {
+        int s = index + offset;
+        int r = Character.offsetByCodePoints(value, offset, count, s, codePointOffset);
+        return r - offset;
+    }
+
+    /**
+     * Returns a localized formatted string, using the supplied format and arguments,
+     * using the user's default locale.
+     *
+     * <p>If you're formatting a string other than for human
+     * consumption, you should use the {@code format(Locale, String, Object...)}
+     * overload and supply {@code Locale.US}. See
+     * "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
+     *
+     * @param format the format string (see {@link java.util.Formatter#format})
+     * @param args
+     *            the list of arguments passed to the formatter. If there are
+     *            more arguments than required by {@code format},
+     *            additional arguments are ignored.
+     * @return the formatted string.
+     * @throws NullPointerException if {@code format == null}
+     * @throws java.util.IllegalFormatException
+     *             if the format is invalid.
+     * @since 1.5
+     */
+    public static String format(String format, Object... args) {
+        return format(Locale.getDefault(), format, args);
+    }
+
+    /**
+     * Returns a formatted string, using the supplied format and arguments,
+     * localized to the given locale.
+     *
+     * @param locale
+     *            the locale to apply; {@code null} value means no localization.
+     * @param format the format string (see {@link java.util.Formatter#format})
+     * @param args
+     *            the list of arguments passed to the formatter. If there are
+     *            more arguments than required by {@code format},
+     *            additional arguments are ignored.
+     * @return the formatted string.
+     * @throws NullPointerException if {@code format == null}
+     * @throws java.util.IllegalFormatException
+     *             if the format is invalid.
+     * @since 1.5
+     */
+    public static String format(Locale locale, String format, Object... args) {
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+        int bufferSize = format.length() + (args == null ? 0 : args.length * 10);
+        Formatter f = new Formatter(new StringBuilder(bufferSize), locale);
+        return f.format(format, args).toString();
+    }
+
+    /*
+     * An implementation of a String.indexOf that is supposed to perform
+     * substantially better than the default algorithm if the "needle" (the
+     * subString being searched for) is a constant string.
+     *
+     * For example, a JIT, upon encountering a call to String.indexOf(String),
+     * where the needle is a constant string, may compute the values cache, md2
+     * and lastChar, and change the call to the following method.
+     */
+    @FindBugsSuppressWarnings("UPM_UNCALLED_PRIVATE_METHOD")
+    @SuppressWarnings("unused")
+    private static int indexOf(String haystackString, String needleString,
+            int cache, int md2, char lastChar) {
+        char[] haystack = haystackString.value;
+        int haystackOffset = haystackString.offset;
+        int haystackLength = haystackString.count;
+        char[] needle = needleString.value;
+        int needleOffset = needleString.offset;
+        int needleLength = needleString.count;
+        int needleLengthMinus1 = needleLength - 1;
+        int haystackEnd = haystackOffset + haystackLength;
+        outer_loop: for (int i = haystackOffset + needleLengthMinus1; i < haystackEnd;) {
+            if (lastChar == haystack[i]) {
+                for (int j = 0; j < needleLengthMinus1; ++j) {
+                    if (needle[j + needleOffset] != haystack[i + j
+                            - needleLengthMinus1]) {
+                        int skip = 1;
+                        if ((cache & (1 << haystack[i])) == 0) {
+                            skip += j;
+                        }
+                        i += Math.max(md2, skip);
+                        continue outer_loop;
+                    }
+                }
+                return i - needleLengthMinus1 - haystackOffset;
+            }
+
+            if ((cache & (1 << haystack[i])) == 0) {
+                i += needleLengthMinus1;
+            }
+            i++;
+        }
+        return -1;
+    }
+}
diff --git a/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java
new file mode 100644
index 0000000..5c81e36
--- /dev/null
+++ b/libart/src/main/java/java/lang/Thread.java
@@ -0,0 +1,1267 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang;
+
+import dalvik.system.VMStack;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import libcore.util.EmptyArray;
+
+/**
+ * A {@code Thread} is a concurrent unit of execution. It has its own call stack
+ * for methods being invoked, their arguments and local variables. Each application
+ * has at least one thread running when it is started, the main thread, in the main
+ * {@link ThreadGroup}. The runtime keeps its own threads in the system thread
+ * group.
+ *
+ * <p>There are two ways to execute code in a new thread.
+ * You can either subclass {@code Thread} and overriding its {@link #run()} method,
+ * or construct a new {@code Thread} and pass a {@link Runnable} to the constructor.
+ * In either case, the {@link #start()} method must be called to actually execute
+ * the new {@code Thread}.
+ *
+ * <p>Each {@code Thread} has an integer priority that affect how the thread is
+ * scheduled by the OS. A new thread inherits the priority of its parent.
+ * A thread's priority can be set using the {@link #setPriority(int)} method.
+ */
+public class Thread implements Runnable {
+    private static final int NANOS_PER_MILLI = 1000000;
+
+    /** Park states */
+    private static class ParkState {
+        /** park state indicating unparked */
+        private static final int UNPARKED = 1;
+
+        /** park state indicating preemptively unparked */
+        private static final int PREEMPTIVELY_UNPARKED = 2;
+
+        /** park state indicating parked */
+        private static final int PARKED = 3;
+    }
+
+    /**
+     * A representation of a thread's state. A given thread may only be in one
+     * state at a time.
+     */
+    public enum State {
+        /**
+         * The thread has been created, but has never been started.
+         */
+        NEW,
+        /**
+         * The thread may be run.
+         */
+        RUNNABLE,
+        /**
+         * The thread is blocked and waiting for a lock.
+         */
+        BLOCKED,
+        /**
+         * The thread is waiting.
+         */
+        WAITING,
+        /**
+         * The thread is waiting for a specified amount of time.
+         */
+        TIMED_WAITING,
+        /**
+         * The thread has been terminated.
+         */
+        TERMINATED
+    }
+
+    /**
+     * The maximum priority value allowed for a thread.
+     * This corresponds to (but does not have the same value as)
+     * {@code android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY}.
+     */
+    public static final int MAX_PRIORITY = 10;
+
+    /**
+     * The minimum priority value allowed for a thread.
+     * This corresponds to (but does not have the same value as)
+     * {@code android.os.Process.THREAD_PRIORITY_LOWEST}.
+     */
+    public static final int MIN_PRIORITY = 1;
+
+    /**
+     * The normal (default) priority value assigned to the main thread.
+     * This corresponds to (but does not have the same value as)
+     * {@code android.os.Process.THREAD_PRIORITY_DEFAULT}.
+
+     */
+    public static final int NORM_PRIORITY = 5;
+
+    /* some of these are accessed directly by the VM; do not rename them */
+    private volatile int nativePeer;
+    volatile ThreadGroup group;
+    volatile boolean daemon;
+    volatile String name;
+    volatile int priority;
+    volatile long stackSize;
+    Runnable target;
+    private static int count = 0;
+
+    /**
+     * Holds the thread's ID. We simply count upwards, so
+     * each Thread has a unique ID.
+     */
+    private long id;
+
+    /**
+     * Normal thread local values.
+     */
+    ThreadLocal.Values localValues;
+
+    /**
+     * Inheritable thread local values.
+     */
+    ThreadLocal.Values inheritableValues;
+
+    /** Callbacks to run on interruption. */
+    private final List<Runnable> interruptActions = new ArrayList<Runnable>();
+
+    /**
+     * Holds the class loader for this Thread, in case there is one.
+     */
+    private ClassLoader contextClassLoader;
+
+    /**
+     * Holds the handler for uncaught exceptions in this Thread,
+     * in case there is one.
+     */
+    private UncaughtExceptionHandler uncaughtHandler;
+
+    /**
+     * Holds the default handler for uncaught exceptions, in case there is one.
+     */
+    private static UncaughtExceptionHandler defaultUncaughtHandler;
+
+    /**
+     * Reflects whether this Thread has already been started. A Thread
+     * can only be started once (no recycling). Also, we need it to deduce
+     * the proper Thread status.
+     */
+    boolean hasBeenStarted = false;
+
+    /** the park state of the thread */
+    private int parkState = ParkState.UNPARKED;
+
+    /**
+     * The synchronization object responsible for this thread's join/sleep/park operations.
+     */
+    private final Object lock = new Object();
+
+    /** Looked up reflectively and used by java.util.concurrent.locks.LockSupport. */
+    private Object parkBlocker;
+
+    /**
+     * Constructs a new {@code Thread} with no {@code Runnable} object and a
+     * newly generated name. The new {@code Thread} will belong to the same
+     * {@code ThreadGroup} as the {@code Thread} calling this constructor.
+     *
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread() {
+        create(null, null, null, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with a {@code Runnable} object and a
+     * newly generated name. The new {@code Thread} will belong to the same
+     * {@code ThreadGroup} as the {@code Thread} calling this constructor.
+     *
+     * @param runnable
+     *            a {@code Runnable} whose method <code>run</code> will be
+     *            executed by the new {@code Thread}
+     *
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread(Runnable runnable) {
+        create(null, runnable, null, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with a {@code Runnable} object and name
+     * provided. The new {@code Thread} will belong to the same {@code
+     * ThreadGroup} as the {@code Thread} calling this constructor.
+     *
+     * @param runnable
+     *            a {@code Runnable} whose method <code>run</code> will be
+     *            executed by the new {@code Thread}
+     * @param threadName
+     *            the name for the {@code Thread} being created
+     *
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread(Runnable runnable, String threadName) {
+        if (threadName == null) {
+            throw new NullPointerException("threadName == null");
+        }
+
+        create(null, runnable, threadName, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with no {@code Runnable} object and the
+     * name provided. The new {@code Thread} will belong to the same {@code
+     * ThreadGroup} as the {@code Thread} calling this constructor.
+     *
+     * @param threadName
+     *            the name for the {@code Thread} being created
+     *
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     *
+     */
+    public Thread(String threadName) {
+        if (threadName == null) {
+            throw new NullPointerException("threadName == null");
+        }
+
+        create(null, null, threadName, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with a {@code Runnable} object and a
+     * newly generated name. The new {@code Thread} will belong to the {@code
+     * ThreadGroup} passed as parameter.
+     *
+     * @param group
+     *            {@code ThreadGroup} to which the new {@code Thread} will
+     *            belong
+     * @param runnable
+     *            a {@code Runnable} whose method <code>run</code> will be
+     *            executed by the new {@code Thread}
+     * @throws IllegalThreadStateException
+     *             if <code>group.destroy()</code> has already been done
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread(ThreadGroup group, Runnable runnable) {
+        create(group, runnable, null, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with a {@code Runnable} object, the given
+     * name and belonging to the {@code ThreadGroup} passed as parameter.
+     *
+     * @param group
+     *            ThreadGroup to which the new {@code Thread} will belong
+     * @param runnable
+     *            a {@code Runnable} whose method <code>run</code> will be
+     *            executed by the new {@code Thread}
+     * @param threadName
+     *            the name for the {@code Thread} being created
+     * @throws IllegalThreadStateException
+     *             if <code>group.destroy()</code> has already been done
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread(ThreadGroup group, Runnable runnable, String threadName) {
+        if (threadName == null) {
+            throw new NullPointerException("threadName == null");
+        }
+
+        create(group, runnable, threadName, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with no {@code Runnable} object, the
+     * given name and belonging to the {@code ThreadGroup} passed as parameter.
+     *
+     * @param group
+     *            {@code ThreadGroup} to which the new {@code Thread} will belong
+     * @param threadName
+     *            the name for the {@code Thread} being created
+     * @throws IllegalThreadStateException
+     *             if <code>group.destroy()</code> has already been done
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread(ThreadGroup group, String threadName) {
+        if (threadName == null) {
+            throw new NullPointerException("threadName == null");
+        }
+
+        create(group, null, threadName, 0);
+    }
+
+    /**
+     * Constructs a new {@code Thread} with a {@code Runnable} object, the given
+     * name and belonging to the {@code ThreadGroup} passed as parameter.
+     *
+     * @param group
+     *            {@code ThreadGroup} to which the new {@code Thread} will
+     *            belong
+     * @param runnable
+     *            a {@code Runnable} whose method <code>run</code> will be
+     *            executed by the new {@code Thread}
+     * @param threadName
+     *            the name for the {@code Thread} being created
+     * @param stackSize
+     *            a stack size for the new {@code Thread}. This has a highly
+     *            platform-dependent interpretation. It may even be ignored
+     *            completely.
+     * @throws IllegalThreadStateException
+     *             if <code>group.destroy()</code> has already been done
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
+        if (threadName == null) {
+            throw new NullPointerException("threadName == null");
+        }
+        create(group, runnable, threadName, stackSize);
+    }
+
+    /**
+     * Package-scope method invoked by Dalvik VM to create "internal"
+     * threads or attach threads created externally.
+     *
+     * Don't call Thread.currentThread(), since there may not be such
+     * a thing (e.g. for Main).
+     */
+    Thread(ThreadGroup group, String name, int priority, boolean daemon) {
+        synchronized (Thread.class) {
+            id = ++Thread.count;
+        }
+
+        if (name == null) {
+            this.name = "Thread-" + id;
+        } else {
+            this.name = name;
+        }
+
+        if (group == null) {
+            throw new InternalError("group == null");
+        }
+
+        this.group = group;
+
+        this.target = null;
+        this.stackSize = 0;
+        this.priority = priority;
+        this.daemon = daemon;
+
+        /* add ourselves to our ThreadGroup of choice */
+        this.group.addThread(this);
+    }
+
+    /**
+     * Initializes a new, existing Thread object with a runnable object,
+     * the given name and belonging to the ThreadGroup passed as parameter.
+     * This is the method that the several public constructors delegate their
+     * work to.
+     *
+     * @param group ThreadGroup to which the new Thread will belong
+     * @param runnable a java.lang.Runnable whose method <code>run</code> will
+     *        be executed by the new Thread
+     * @param threadName Name for the Thread being created
+     * @param stackSize Platform dependent stack size
+     * @throws IllegalThreadStateException if <code>group.destroy()</code> has
+     *         already been done
+     * @see java.lang.ThreadGroup
+     * @see java.lang.Runnable
+     */
+    private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
+        Thread currentThread = Thread.currentThread();
+        if (group == null) {
+            group = currentThread.getThreadGroup();
+        }
+
+        if (group.isDestroyed()) {
+            throw new IllegalThreadStateException("Group already destroyed");
+        }
+
+        this.group = group;
+
+        synchronized (Thread.class) {
+            id = ++Thread.count;
+        }
+
+        if (threadName == null) {
+            this.name = "Thread-" + id;
+        } else {
+            this.name = threadName;
+        }
+
+        this.target = runnable;
+        this.stackSize = stackSize;
+
+        this.priority = currentThread.getPriority();
+
+        this.contextClassLoader = currentThread.contextClassLoader;
+
+        // Transfer over InheritableThreadLocals.
+        if (currentThread.inheritableValues != null) {
+            inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
+        }
+
+        // add ourselves to our ThreadGroup of choice
+        this.group.addThread(this);
+    }
+
+    /**
+     * Returns the number of active {@code Thread}s in the running {@code
+     * Thread}'s group and its subgroups.
+     *
+     * @return the number of {@code Thread}s
+     */
+    public static int activeCount() {
+        return currentThread().getThreadGroup().activeCount();
+    }
+
+    /**
+     * Does nothing.
+     */
+    public final void checkAccess() {
+    }
+
+    /**
+     * Returns the number of stack frames in this thread.
+     *
+     * @return Number of stack frames
+     * @deprecated The results of this call were never well defined. To make
+     *             things worse, it would depend on whether the Thread was
+     *             suspended or not, and suspend was deprecated too.
+     */
+    @Deprecated
+    public int countStackFrames() {
+        return getStackTrace().length;
+    }
+
+    /**
+     * Returns the Thread of the caller, that is, the current Thread.
+     */
+    public static native Thread currentThread();
+
+    /**
+     * Throws {@code UnsupportedOperationException}.
+     * @deprecated Not implemented.
+     */
+    @Deprecated
+    public void destroy() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Prints to the standard error stream a text representation of the current
+     * stack for this Thread.
+     *
+     * @see Throwable#printStackTrace()
+     */
+    public static void dumpStack() {
+        new Throwable("stack dump").printStackTrace();
+    }
+
+    /**
+     * Copies an array with all Threads which are in the same ThreadGroup as the
+     * receiver - and subgroups - into the array <code>threads</code> passed as
+     * parameter. If the array passed as parameter is too small no exception is
+     * thrown - the extra elements are simply not copied.
+     *
+     * @param threads
+     *            array into which the Threads will be copied
+     * @return How many Threads were copied over
+     */
+    public static int enumerate(Thread[] threads) {
+        Thread thread = Thread.currentThread();
+        return thread.getThreadGroup().enumerate(threads);
+    }
+
+    /**
+     * Returns a map of all the currently live threads to their stack traces.
+     */
+    public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
+        Map<Thread, StackTraceElement[]> map = new HashMap<Thread, StackTraceElement[]>();
+
+        // Find out how many live threads we have. Allocate a bit more
+        // space than needed, in case new ones are just being created.
+        int count = ThreadGroup.systemThreadGroup.activeCount();
+        Thread[] threads = new Thread[count + count / 2];
+
+        // Enumerate the threads and collect the stacktraces.
+        count = ThreadGroup.systemThreadGroup.enumerate(threads);
+        for (int i = 0; i < count; i++) {
+            map.put(threads[i], threads[i].getStackTrace());
+        }
+
+        return map;
+    }
+
+    /**
+     * Returns the context ClassLoader for this Thread.
+     *
+     * @return ClassLoader The context ClassLoader
+     * @see java.lang.ClassLoader
+     * @see #getContextClassLoader()
+     */
+    public ClassLoader getContextClassLoader() {
+        return contextClassLoader;
+    }
+
+    /**
+     * Returns the default exception handler that's executed when uncaught
+     * exception terminates a thread.
+     *
+     * @return an {@link UncaughtExceptionHandler} or <code>null</code> if
+     *         none exists.
+     */
+    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
+        return defaultUncaughtHandler;
+    }
+
+    /**
+     * Returns the thread's identifier. The ID is a positive <code>long</code>
+     * generated on thread creation, is unique to the thread, and doesn't change
+     * during the lifetime of the thread; the ID may be reused after the thread
+     * has been terminated.
+     *
+     * @return the thread's ID.
+     */
+    public long getId() {
+        return id;
+    }
+
+    /**
+     * Returns the name of the Thread.
+     */
+    public final String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the priority of the Thread.
+     */
+    public final int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Returns an array of {@link StackTraceElement} representing the current thread's stack.
+     */
+    public StackTraceElement[] getStackTrace() {
+        StackTraceElement ste[] = VMStack.getThreadStackTrace(this);
+        return ste != null ? ste : EmptyArray.STACK_TRACE_ELEMENT;
+    }
+
+    /**
+     * Returns the current state of the Thread. This method is useful for
+     * monitoring purposes.
+     *
+     * @return a {@link State} value.
+     */
+    public State getState() {
+        return State.values()[nativeGetStatus(hasBeenStarted)];
+    }
+
+    private native int nativeGetStatus(boolean hasBeenStarted);
+
+    /**
+     * Returns the ThreadGroup to which this Thread belongs.
+     *
+     * @return the Thread's ThreadGroup
+     */
+    public final ThreadGroup getThreadGroup() {
+        // TODO This should actually be done at native termination.
+        if (getState() == Thread.State.TERMINATED) {
+            return null;
+        } else {
+            return group;
+        }
+    }
+
+    /**
+     * Returns the thread's uncaught exception handler. If not explicitly set,
+     * then the ThreadGroup's handler is returned. If the thread is terminated,
+     * then <code>null</code> is returned.
+     *
+     * @return an {@link UncaughtExceptionHandler} instance or {@code null}.
+     */
+    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
+        if (uncaughtHandler != null) {
+            return uncaughtHandler;
+        } else {
+            return group;           // ThreadGroup is instance of UEH
+        }
+    }
+
+    /**
+     * Posts an interrupt request to this {@code Thread}. The behavior depends on
+     * the state of this {@code Thread}:
+     * <ul>
+     * <li>
+     * {@code Thread}s blocked in one of {@code Object}'s {@code wait()} methods
+     * or one of {@code Thread}'s {@code join()} or {@code sleep()} methods will
+     * be woken up, their interrupt status will be cleared, and they receive an
+     * {@link InterruptedException}.
+     * <li>
+     * {@code Thread}s blocked in an I/O operation of an
+     * {@link java.nio.channels.InterruptibleChannel} will have their interrupt
+     * status set and receive an
+     * {@link java.nio.channels.ClosedByInterruptException}. Also, the channel
+     * will be closed.
+     * <li>
+     * {@code Thread}s blocked in a {@link java.nio.channels.Selector} will have
+     * their interrupt status set and return immediately. They don't receive an
+     * exception in this case.
+     * <ul>
+     *
+     * @see Thread#interrupted
+     * @see Thread#isInterrupted
+     */
+    public void interrupt() {
+        // Interrupt this thread before running actions so that other
+        // threads that observe the interrupt as a result of an action
+        // will see that this thread is in the interrupted state.
+        nativeInterrupt();
+
+        synchronized (interruptActions) {
+            for (int i = interruptActions.size() - 1; i >= 0; i--) {
+                interruptActions.get(i).run();
+            }
+        }
+    }
+
+    private native void nativeInterrupt();
+
+    /**
+     * Returns a <code>boolean</code> indicating whether the current Thread (
+     * <code>currentThread()</code>) has a pending interrupt request (<code>
+     * true</code>) or not (<code>false</code>). It also has the side-effect of
+     * clearing the flag.
+     *
+     * @return a <code>boolean</code> indicating the interrupt status
+     * @see Thread#currentThread
+     * @see Thread#interrupt
+     * @see Thread#isInterrupted
+     */
+    public static native boolean interrupted();
+
+    /**
+     * Returns <code>true</code> if the receiver has already been started and
+     * still runs code (hasn't died yet). Returns <code>false</code> either if
+     * the receiver hasn't been started yet or if it has already started and run
+     * to completion and died.
+     *
+     * @return a <code>boolean</code> indicating the liveness of the Thread
+     * @see Thread#start
+     */
+    public final boolean isAlive() {
+        return (nativePeer != 0);
+    }
+
+    /**
+     * Tests whether this is a daemon thread.
+     * A daemon thread only runs as long as there are non-daemon threads running.
+     * When the last non-daemon thread ends, the runtime will exit. This is not
+     * normally relevant to applications with a UI.
+     */
+    public final boolean isDaemon() {
+        return daemon;
+    }
+
+    /**
+     * Returns a <code>boolean</code> indicating whether the receiver has a
+     * pending interrupt request (<code>true</code>) or not (
+     * <code>false</code>)
+     *
+     * @return a <code>boolean</code> indicating the interrupt status
+     * @see Thread#interrupt
+     * @see Thread#interrupted
+     */
+    public native boolean isInterrupted();
+
+    /**
+     * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+     * the receiver finishes its execution and dies.
+     *
+     * @throws InterruptedException if <code>interrupt()</code> was called for
+     *         the receiver while it was in the <code>join()</code> call
+     * @see Object#notifyAll
+     * @see java.lang.ThreadDeath
+     */
+    public final void join() throws InterruptedException {
+        synchronized (lock) {
+            while (isAlive()) {
+                lock.wait();
+            }
+        }
+    }
+
+    /**
+     * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+     * the receiver finishes its execution and dies or the specified timeout
+     * expires, whatever happens first.
+     *
+     * @param millis The maximum time to wait (in milliseconds).
+     * @throws InterruptedException if <code>interrupt()</code> was called for
+     *         the receiver while it was in the <code>join()</code> call
+     * @see Object#notifyAll
+     * @see java.lang.ThreadDeath
+     */
+    public final void join(long millis) throws InterruptedException {
+        join(millis, 0);
+    }
+
+    /**
+     * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+     * the receiver finishes its execution and dies or the specified timeout
+     * expires, whatever happens first.
+     *
+     * @param millis The maximum time to wait (in milliseconds).
+     * @param nanos Extra nanosecond precision
+     * @throws InterruptedException if <code>interrupt()</code> was called for
+     *         the receiver while it was in the <code>join()</code> call
+     * @see Object#notifyAll
+     * @see java.lang.ThreadDeath
+     */
+    public final void join(long millis, int nanos) throws InterruptedException {
+        if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) {
+            throw new IllegalArgumentException("bad timeout: millis=" + millis + ",nanos=" + nanos);
+        }
+
+        // avoid overflow: if total > 292,277 years, just wait forever
+        boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI;
+        boolean forever = (millis | nanos) == 0;
+        if (forever | overflow) {
+            join();
+            return;
+        }
+
+        synchronized (lock) {
+            if (!isAlive()) {
+                return;
+            }
+
+            // guaranteed not to overflow
+            long nanosToWait = millis * NANOS_PER_MILLI + nanos;
+
+            // wait until this thread completes or the timeout has elapsed
+            long start = System.nanoTime();
+            while (true) {
+                lock.wait(millis, nanos);
+                if (!isAlive()) {
+                    break;
+                }
+                long nanosElapsed = System.nanoTime() - start;
+                long nanosRemaining = nanosToWait - nanosElapsed;
+                if (nanosRemaining <= 0) {
+                    break;
+                }
+                millis = nanosRemaining / NANOS_PER_MILLI;
+                nanos = (int) (nanosRemaining - millis * NANOS_PER_MILLI);
+            }
+        }
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}.
+     * @deprecated Only useful in conjunction with deprecated method {@link Thread#suspend}.
+     */
+    @Deprecated
+    public final void resume() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Calls the <code>run()</code> method of the Runnable object the receiver
+     * holds. If no Runnable is set, does nothing.
+     *
+     * @see Thread#start
+     */
+    public void run() {
+        if (target != null) {
+            target.run();
+        }
+    }
+
+    /**
+     * Set the context ClassLoader for the receiver.
+     *
+     * @param cl The context ClassLoader
+     * @see #getContextClassLoader()
+     */
+    public void setContextClassLoader(ClassLoader cl) {
+        contextClassLoader = cl;
+    }
+
+    /**
+     * Marks this thread as a daemon thread.
+     * A daemon thread only runs as long as there are non-daemon threads running.
+     * When the last non-daemon thread ends, the runtime will exit. This is not
+     * normally relevant to applications with a UI.
+     * @throws IllegalThreadStateException - if this thread has already started.
+     */
+    public final void setDaemon(boolean isDaemon) {
+        checkNotStarted();
+
+        if (nativePeer == 0) {
+            daemon = isDaemon;
+        }
+    }
+
+    private void checkNotStarted() {
+        if (hasBeenStarted) {
+            throw new IllegalThreadStateException("Thread already started");
+        }
+    }
+
+    /**
+     * Sets the default uncaught exception handler. This handler is invoked in
+     * case any Thread dies due to an unhandled exception.
+     *
+     * @param handler
+     *            The handler to set or null.
+     */
+    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+        Thread.defaultUncaughtHandler = handler;
+    }
+
+    /**
+     * Adds a runnable to be invoked upon interruption. If this thread has
+     * already been interrupted, the runnable will be invoked immediately. The
+     * action should be idempotent as it may be invoked multiple times for a
+     * single interruption.
+     *
+     * <p>Each call to this method must be matched with a corresponding call to
+     * {@link #popInterruptAction$}.
+     *
+     * @hide used by NIO
+     */
+    public final void pushInterruptAction$(Runnable interruptAction) {
+        synchronized (interruptActions) {
+            interruptActions.add(interruptAction);
+        }
+
+        if (interruptAction != null && isInterrupted()) {
+            interruptAction.run();
+        }
+    }
+
+    /**
+     * Removes {@code interruptAction} so it is not invoked upon interruption.
+     *
+     * @param interruptAction the pushed action, used to check that the call
+     *     stack is correctly nested.
+     *
+     * @hide used by NIO
+     */
+    public final void popInterruptAction$(Runnable interruptAction) {
+        synchronized (interruptActions) {
+            Runnable removed = interruptActions.remove(interruptActions.size() - 1);
+            if (interruptAction != removed) {
+                throw new IllegalArgumentException("Expected " + interruptAction + " but was " + removed);
+            }
+        }
+    }
+
+    /**
+     * Sets the name of the Thread.
+     *
+     * @param threadName the new name for the Thread
+     * @see Thread#getName
+     */
+    public final void setName(String threadName) {
+        if (threadName == null) {
+            throw new NullPointerException("threadName == null");
+        }
+        // The lock is taken to ensure no race occurs between starting the
+        // the thread and setting its name (and the name of its native peer).
+        synchronized (this) {
+            this.name = threadName;
+
+            if (isAlive()) {
+                nativeSetName(threadName);
+            }
+        }
+    }
+
+    /**
+     * Tell the VM that the thread's name has changed.  This is useful for
+     * DDMS, which would otherwise be oblivious to Thread.setName calls.
+     */
+    private native void nativeSetName(String newName);
+
+    /**
+     * Sets the priority of this thread. If the requested priority is greater than the
+     * parent thread group's {@link java.lang.ThreadGroup#getMaxPriority}, the group's maximum
+     * priority will be used instead.
+     *
+     * @throws IllegalArgumentException - if the new priority is greater than {@link #MAX_PRIORITY}
+     *     or less than {@link #MIN_PRIORITY}
+     */
+    public final void setPriority(int priority) {
+        if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
+            throw new IllegalArgumentException("Priority out of range: " + priority);
+        }
+
+        if (priority > group.getMaxPriority()) {
+            priority = group.getMaxPriority();
+        }
+
+        // The lock is taken to ensure no race occurs between starting the
+        // the thread and setting its priority (and the priority of its native peer).
+        synchronized (this) {
+            this.priority = priority;
+
+            if (isAlive()) {
+                nativeSetPriority(priority);
+            }
+        }
+    }
+
+    private native void nativeSetPriority(int newPriority);
+
+    /**
+     * <p>
+     * Sets the uncaught exception handler. This handler is invoked in case this
+     * Thread dies due to an unhandled exception.
+     * </p>
+     *
+     * @param handler
+     *            The handler to set or <code>null</code>.
+     */
+    public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+        uncaughtHandler = handler;
+    }
+
+    /**
+     * Causes the thread which sent this message to sleep for the given interval
+     * of time (given in milliseconds). The precision is not guaranteed - the
+     * Thread may sleep more or less than requested.
+     *
+     * @param time
+     *            The time to sleep in milliseconds.
+     * @throws InterruptedException
+     *             if <code>interrupt()</code> was called for this Thread while
+     *             it was sleeping
+     * @see Thread#interrupt()
+     */
+    public static void sleep(long time) throws InterruptedException {
+        Thread.sleep(time, 0);
+    }
+
+    /**
+     * Causes the thread which sent this message to sleep for the given interval
+     * of time (given in milliseconds and nanoseconds). The precision is not
+     * guaranteed - the Thread may sleep more or less than requested.
+     *
+     * @param millis
+     *            The time to sleep in milliseconds.
+     * @param nanos
+     *            Extra nanosecond precision
+     * @throws InterruptedException
+     *             if <code>interrupt()</code> was called for this Thread while
+     *             it was sleeping
+     * @see Thread#interrupt()
+     */
+    public static void sleep(long millis, int nanos) throws InterruptedException {
+        // The JLS 3rd edition, section 17.9 says: "...sleep for zero
+        // time...need not have observable effects."
+        if (millis == 0 && nanos == 0) {
+            return;
+        }
+
+        long start = System.nanoTime();
+        long duration = (millis * NANOS_PER_MILLI) + nanos;
+
+        Object lock = currentThread().lock;
+
+        // Wait may return early, so loop until sleep duration passes.
+        synchronized (lock) {
+            while (true) {
+                sleep(lock, millis, nanos);
+
+                long now = System.nanoTime();
+                long elapsed = now - start;
+
+                if (elapsed >= duration) {
+                    break;
+                }
+
+                duration -= elapsed;
+                start = now;
+                millis = duration / NANOS_PER_MILLI;
+                nanos = (int) (duration % NANOS_PER_MILLI);
+            }
+        }
+    }
+
+    private static native void sleep(Object lock, long millis, int nanos);
+
+    /**
+     * Starts the new Thread of execution. The <code>run()</code> method of
+     * the receiver will be called by the receiver Thread itself (and not the
+     * Thread calling <code>start()</code>).
+     *
+     * @throws IllegalThreadStateException - if this thread has already started.
+     * @see Thread#run
+     */
+    public synchronized void start() {
+        checkNotStarted();
+
+        hasBeenStarted = true;
+
+        nativeCreate(this, stackSize, daemon);
+    }
+
+    private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
+
+    /**
+     * Requests the receiver Thread to stop and throw ThreadDeath. The Thread is
+     * resumed if it was suspended and awakened if it was sleeping, so that it
+     * can proceed to throw ThreadDeath.
+     *
+     * @deprecated because stopping a thread in this manner is unsafe and can
+     * leave your application and the VM in an unpredictable state.
+     */
+    @Deprecated
+    public final void stop() {
+        stop(new ThreadDeath());
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}.
+     * @deprecated because stopping a thread in this manner is unsafe and can
+     * leave your application and the VM in an unpredictable state.
+     */
+    @Deprecated
+    public final synchronized void stop(Throwable throwable) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}.
+     * @deprecated May cause deadlocks.
+     */
+    @Deprecated
+    public final void suspend() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a string containing a concise, human-readable description of the
+     * Thread. It includes the Thread's name, priority, and group name.
+     *
+     * @return a printable representation for the receiver.
+     */
+    @Override
+    public String toString() {
+        return "Thread[" + name + "," + priority + "," + group.getName() + "]";
+    }
+
+    /**
+     * Causes the calling Thread to yield execution time to another Thread that
+     * is ready to run. The actual scheduling is implementation-dependent.
+     */
+    public static native void yield();
+
+    /**
+     * Indicates whether the current Thread has a monitor lock on the specified
+     * object.
+     *
+     * @param object the object to test for the monitor lock
+     * @return true if the current thread has a monitor lock on the specified
+     *         object; false otherwise
+     */
+    public static boolean holdsLock(Object object) {
+        return currentThread().nativeHoldsLock(object);
+    }
+
+    private native boolean nativeHoldsLock(Object object);
+
+    /**
+     * Implemented by objects that want to handle cases where a thread is being
+     * terminated by an uncaught exception. Upon such termination, the handler
+     * is notified of the terminating thread and causal exception. If there is
+     * no explicit handler set then the thread's group is the default handler.
+     */
+    public static interface UncaughtExceptionHandler {
+        /**
+         * The thread is being terminated by an uncaught exception. Further
+         * exceptions thrown in this method are prevent the remainder of the
+         * method from executing, but are otherwise ignored.
+         *
+         * @param thread the thread that has an uncaught exception
+         * @param ex the exception that was thrown
+         */
+        void uncaughtException(Thread thread, Throwable ex);
+    }
+
+    /**
+     * Unparks this thread. This unblocks the thread it if it was
+     * previously parked, or indicates that the thread is "preemptively
+     * unparked" if it wasn't already parked. The latter means that the
+     * next time the thread is told to park, it will merely clear its
+     * latent park bit and carry on without blocking.
+     *
+     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+     * in-depth information of the behavior of this method.</p>
+     *
+     * @hide for Unsafe
+     */
+    public void unpark() {
+        synchronized (lock) {
+            switch (parkState) {
+                case ParkState.PREEMPTIVELY_UNPARKED: {
+                    /*
+                     * Nothing to do in this case: By definition, a
+                     * preemptively unparked thread is to remain in
+                     * the preemptively unparked state if it is told
+                     * to unpark.
+                     */
+                    break;
+                }
+                case ParkState.UNPARKED: {
+                    parkState = ParkState.PREEMPTIVELY_UNPARKED;
+                    break;
+                }
+                default /*parked*/: {
+                    parkState = ParkState.UNPARKED;
+                    lock.notifyAll();
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Parks the current thread for a particular number of nanoseconds, or
+     * indefinitely. If not indefinitely, this method unparks the thread
+     * after the given number of nanoseconds if no other thread unparks it
+     * first. If the thread has been "preemptively unparked," this method
+     * cancels that unparking and returns immediately. This method may
+     * also return spuriously (that is, without the thread being told to
+     * unpark and without the indicated amount of time elapsing).
+     *
+     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+     * in-depth information of the behavior of this method.</p>
+     *
+     * <p>This method must only be called when <code>this</code> is the current
+     * thread.
+     *
+     * @param nanos number of nanoseconds to park for or <code>0</code>
+     * to park indefinitely
+     * @throws IllegalArgumentException thrown if <code>nanos &lt; 0</code>
+     *
+     * @hide for Unsafe
+     */
+    public void parkFor(long nanos) {
+        synchronized (lock) {
+            switch (parkState) {
+                case ParkState.PREEMPTIVELY_UNPARKED: {
+                    parkState = ParkState.UNPARKED;
+                    break;
+                }
+                case ParkState.UNPARKED: {
+                    long millis = nanos / NANOS_PER_MILLI;
+                    nanos %= NANOS_PER_MILLI;
+
+                    parkState = ParkState.PARKED;
+                    try {
+                        lock.wait(millis, (int) nanos);
+                    } catch (InterruptedException ex) {
+                        interrupt();
+                    } finally {
+                        /*
+                         * Note: If parkState manages to become
+                         * PREEMPTIVELY_UNPARKED before hitting this
+                         * code, it should left in that state.
+                         */
+                        if (parkState == ParkState.PARKED) {
+                            parkState = ParkState.UNPARKED;
+                        }
+                    }
+                    break;
+                }
+                default /*parked*/: {
+                    throw new AssertionError("Attempt to repark");
+                }
+            }
+        }
+    }
+
+    /**
+     * Parks the current thread until the specified system time. This
+     * method attempts to unpark the current thread immediately after
+     * <code>System.currentTimeMillis()</code> reaches the specified
+     * value, if no other thread unparks it first. If the thread has
+     * been "preemptively unparked," this method cancels that
+     * unparking and returns immediately. This method may also return
+     * spuriously (that is, without the thread being told to unpark
+     * and without the indicated amount of time elapsing).
+     *
+     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+     * in-depth information of the behavior of this method.</p>
+     *
+     * <p>This method must only be called when <code>this</code> is the
+     * current thread.
+     *
+     * @param time the time after which the thread should be unparked,
+     * in absolute milliseconds-since-the-epoch
+     *
+     * @hide for Unsafe
+     */
+    public void parkUntil(long time) {
+        synchronized (lock) {
+            /*
+             * Note: This conflates the two time bases of "wall clock"
+             * time and "monotonic uptime" time. However, given that
+             * the underlying system can only wait on monotonic time,
+             * it is unclear if there is any way to avoid the
+             * conflation. The downside here is that if, having
+             * calculated the delay, the wall clock gets moved ahead,
+             * this method may not return until well after the wall
+             * clock has reached the originally designated time. The
+             * reverse problem (the wall clock being turned back)
+             * isn't a big deal, since this method is allowed to
+             * spuriously return for any reason, and this situation
+             * can safely be construed as just such a spurious return.
+             */
+            long delayMillis = time - System.currentTimeMillis();
+
+            if (delayMillis <= 0) {
+                parkState = ParkState.UNPARKED;
+            } else {
+                parkFor(delayMillis * NANOS_PER_MILLI);
+            }
+        }
+    }
+}
diff --git a/libart/src/main/java/java/lang/ThreadGroup.java b/libart/src/main/java/java/lang/ThreadGroup.java
new file mode 100644
index 0000000..51b2137
--- /dev/null
+++ b/libart/src/main/java/java/lang/ThreadGroup.java
@@ -0,0 +1,726 @@
+/*
+ * 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 java.lang;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import libcore.util.CollectionUtils;
+
+/**
+ * {@code ThreadGroup} is a means of organizing threads into a hierarchical structure.
+ * This class is obsolete. See <i>Effective Java</i> Item 73, "Avoid thread groups" for details.
+ * @see Thread
+ */
+public class ThreadGroup implements Thread.UncaughtExceptionHandler {
+
+    // Name of this ThreadGroup
+    // VM needs this field name for debugging.
+    private String name;
+
+    // Maximum priority for Threads inside this ThreadGroup
+    private int maxPriority = Thread.MAX_PRIORITY;
+
+    // The ThreadGroup to which this ThreadGroup belongs
+    // VM needs this field name for debugging.
+    final ThreadGroup parent;
+
+    /**
+     * Weak references to the threads in this group.
+     * Access is guarded by synchronizing on this field.
+     */
+    private final List<WeakReference<Thread>> threadRefs = new ArrayList<WeakReference<Thread>>(5);
+
+    /**
+     * View of the threads.
+     * Access is guarded by synchronizing on threadRefs.
+     */
+    private final Iterable<Thread> threads = CollectionUtils.dereferenceIterable(threadRefs, true);
+
+    /**
+     * Thread groups. Access is guarded by synchronizing on this field.
+     */
+    private final List<ThreadGroup> groups = new ArrayList<ThreadGroup>(3);
+
+    // Whether this ThreadGroup is a daemon ThreadGroup or not
+    private boolean isDaemon;
+
+    // Whether this ThreadGroup has already been destroyed or not
+    private boolean isDestroyed;
+
+    /* the VM uses these directly; do not rename */
+    static final ThreadGroup systemThreadGroup = new ThreadGroup();
+    static final ThreadGroup mainThreadGroup = new ThreadGroup(systemThreadGroup, "main");
+
+    /**
+     * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup}
+     * will be child of the {@code ThreadGroup} to which the calling thread belongs.
+     *
+     * @param name the name
+     * @see Thread#currentThread
+     */
+    public ThreadGroup(String name) {
+        this(Thread.currentThread().getThreadGroup(), name);
+    }
+
+    /**
+     * Constructs a new {@code ThreadGroup} with the given name, as a child of the
+     * given {@code ThreadGroup}.
+     *
+     * @param parent the parent
+     * @param name the name
+     * @throws NullPointerException if {@code parent == null}
+     * @throws IllegalThreadStateException if {@code parent} has been
+     *         destroyed already
+     */
+    public ThreadGroup(ThreadGroup parent, String name) {
+        if (parent == null) {
+            throw new NullPointerException("parent == null");
+        }
+        this.name = name;
+        this.parent = parent;
+        if (parent != null) {
+            parent.add(this);
+            this.setMaxPriority(parent.getMaxPriority());
+            if (parent.isDaemon()) {
+                this.setDaemon(true);
+            }
+        }
+    }
+
+    /**
+     * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
+     * but we have an additional group above that in Android.
+     */
+    private ThreadGroup() {
+        this.name = "system";
+        this.parent = null;
+    }
+
+    /**
+     * Returns the number of running {@code Thread}s which are children of this thread group,
+     * directly or indirectly.
+     *
+     * @return the number of children
+     */
+    public int activeCount() {
+        int count = 0;
+        synchronized (threadRefs) {
+            for (Thread thread : threads) {
+                if (thread.isAlive()) {
+                    count++;
+                }
+            }
+        }
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                count += group.activeCount();
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Returns the number of {@code ThreadGroup}s which are children of this group,
+     * directly or indirectly.
+     *
+     * @return the number of children
+     */
+    public int activeGroupCount() {
+        int count = 0;
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                // One for this group & the subgroups
+                count += 1 + group.activeGroupCount();
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Adds a {@code ThreadGroup} to this thread group.
+     *
+     * @param g ThreadGroup to add
+     * @throws IllegalThreadStateException if this group has been destroyed already
+     */
+    private void add(ThreadGroup g) throws IllegalThreadStateException {
+        synchronized (groups) {
+            if (isDestroyed) {
+                throw new IllegalThreadStateException();
+            }
+            groups.add(g);
+        }
+    }
+
+    /**
+     * Does nothing. The definition of this method depends on the deprecated
+     * method {@link #suspend()}. The exact behavior of this call was never
+     * specified.
+     *
+     * @param b Used to control low memory implicit suspension
+     * @return {@code true} (always)
+     *
+     * @deprecated Required deprecated method suspend().
+     */
+    @Deprecated
+    public boolean allowThreadSuspension(boolean b) {
+        // Does not apply to this VM, no-op
+        return true;
+    }
+
+    /**
+     * Does nothing.
+     */
+    public final void checkAccess() {
+    }
+
+    /**
+     * Destroys this thread group and recursively all its subgroups. It is only legal
+     * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon
+     * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads
+     * or thread groups in it).
+     *
+     * @throws IllegalThreadStateException if this thread group or any of its
+     *         subgroups has been destroyed already or if it still contains
+     *         threads.
+     */
+    public final void destroy() {
+        synchronized (threadRefs) {
+            synchronized (groups) {
+                if (isDestroyed) {
+                    throw new IllegalThreadStateException(
+                            "Thread group was already destroyed: "
+                            + (this.name != null ? this.name : "n/a"));
+                }
+                if (threads.iterator().hasNext()) {
+                    throw new IllegalThreadStateException(
+                            "Thread group still contains threads: "
+                            + (this.name != null ? this.name : "n/a"));
+                }
+                // Call recursively for subgroups
+                while (!groups.isEmpty()) {
+                    // We always get the first element - remember, when the
+                    // child dies it removes itself from our collection. See
+                    // below.
+                    groups.get(0).destroy();
+                }
+
+                if (parent != null) {
+                    parent.remove(this);
+                }
+
+                // Now that the ThreadGroup is really destroyed it can be tagged as so
+                this.isDestroyed = true;
+            }
+        }
+    }
+
+    /*
+     * Auxiliary method that destroys this thread group and recursively all its
+     * subgroups if this is a daemon ThreadGroup.
+     *
+     * @see #destroy
+     * @see #setDaemon
+     * @see #isDaemon
+     */
+    private void destroyIfEmptyDaemon() {
+        // Has to be non-destroyed daemon to make sense
+        synchronized (threadRefs) {
+            if (isDaemon && !isDestroyed && !threads.iterator().hasNext()) {
+                synchronized (groups) {
+                    if (groups.isEmpty()) {
+                        destroy();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Iterates over all active threads in this group (and its sub-groups) and
+     * stores the threads in the given array. Returns when the array is full or
+     * no more threads remain, whichever happens first.
+     *
+     * <p>Note that this method will silently ignore any threads that don't fit in the
+     * supplied array.
+     *
+     * @param threads the array into which the {@code Thread}s will be copied
+     * @return the number of {@code Thread}s that were copied
+     */
+    public int enumerate(Thread[] threads) {
+        return enumerate(threads, true);
+    }
+
+    /**
+     * Iterates over all active threads in this group (and, optionally, its
+     * sub-groups) and stores the threads in the given array. Returns when the
+     * array is full or no more threads remain, whichever happens first.
+     *
+     * <p>Note that this method will silently ignore any threads that don't fit in the
+     * supplied array.
+     *
+     * @param threads the array into which the {@code Thread}s will be copied
+     * @param recurse indicates whether {@code Thread}s in subgroups should be
+     *        recursively copied as well
+     * @return the number of {@code Thread}s that were copied
+     */
+    public int enumerate(Thread[] threads, boolean recurse) {
+        return enumerateGeneric(threads, recurse, 0, true);
+    }
+
+    /**
+     * Iterates over all thread groups in this group (and its sub-groups) and
+     * and stores the groups in the given array. Returns when the array is full
+     * or no more groups remain, whichever happens first.
+     *
+     * <p>Note that this method will silently ignore any thread groups that don't fit in the
+     * supplied array.
+     *
+     * @param groups the array into which the {@code ThreadGroup}s will be copied
+     * @return the number of {@code ThreadGroup}s that were copied
+     */
+    public int enumerate(ThreadGroup[] groups) {
+        return enumerate(groups, true);
+    }
+
+    /**
+     * Iterates over all thread groups in this group (and, optionally, its
+     * sub-groups) and stores the groups in the given array. Returns when
+     * the array is full or no more groups remain, whichever happens first.
+     *
+     * <p>Note that this method will silently ignore any thread groups that don't fit in the
+     * supplied array.
+     *
+     * @param groups the array into which the {@code ThreadGroup}s will be copied
+     * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be
+     *        recursively copied as well or not
+     * @return the number of {@code ThreadGroup}s that were copied
+     */
+    public int enumerate(ThreadGroup[] groups, boolean recurse) {
+        return enumerateGeneric(groups, recurse, 0, false);
+    }
+
+    /**
+     * Copies into <param>enumeration</param> starting at
+     * <param>enumerationIndex</param> all Threads or ThreadGroups in the
+     * receiver. If <param>recurse</param> is true, recursively enumerate the
+     * elements in subgroups.
+     *
+     * If the array passed as parameter is too small no exception is thrown -
+     * the extra elements are simply not copied.
+     *
+     * @param enumeration array into which the elements will be copied
+     * @param recurse Indicates whether subgroups should be enumerated or not
+     * @param enumerationIndex Indicates in which position of the enumeration
+     *        array we are
+     * @param enumeratingThreads Indicates whether we are enumerating Threads or
+     *        ThreadGroups
+     * @return How many elements were enumerated/copied over
+     */
+    private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
+            boolean enumeratingThreads) {
+        if (enumeratingThreads) {
+            synchronized (threadRefs) {
+                // walk the references directly so we can iterate in reverse order
+                for (int i = threadRefs.size() - 1; i >= 0; --i) {
+                    Thread thread = threadRefs.get(i).get();
+                    if (thread != null && thread.isAlive()) {
+                        if (enumerationIndex >= enumeration.length) {
+                            return enumerationIndex;
+                        }
+                        enumeration[enumerationIndex++] = thread;
+                    }
+                }
+            }
+        } else {
+            synchronized (groups) {
+                for (int i = groups.size() - 1; i >= 0; --i) {
+                    if (enumerationIndex >= enumeration.length) {
+                        return enumerationIndex;
+                    }
+                    enumeration[enumerationIndex++] = groups.get(i);
+                }
+            }
+        }
+
+        if (recurse) {
+            synchronized (groups) {
+                for (ThreadGroup group : groups) {
+                    if (enumerationIndex >= enumeration.length) {
+                        return enumerationIndex;
+                    }
+                    enumerationIndex = group.enumerateGeneric(enumeration, recurse,
+                            enumerationIndex, enumeratingThreads);
+                }
+            }
+        }
+        return enumerationIndex;
+    }
+
+    /**
+     * Returns the maximum allowed priority for a {@code Thread} in this thread group.
+     *
+     * @return the maximum priority
+     *
+     * @see #setMaxPriority
+     */
+    public final int getMaxPriority() {
+        return maxPriority;
+    }
+
+    /**
+     * Returns the name of this thread group.
+     *
+     * @return the group's name
+     */
+    public final String getName() {
+        return name;
+    }
+
+    /**
+     * Returns this thread group's parent {@code ThreadGroup}. It can be null if this
+     * is the the root ThreadGroup.
+     *
+     * @return the parent
+     */
+    public final ThreadGroup getParent() {
+        return parent;
+    }
+
+    /**
+     * Interrupts every {@code Thread} in this group and recursively in all its
+     * subgroups.
+     *
+     * @see Thread#interrupt
+     */
+    public final void interrupt() {
+        synchronized (threadRefs) {
+            for (Thread thread : threads) {
+                thread.interrupt();
+            }
+        }
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                group.interrupt();
+            }
+        }
+    }
+
+    /**
+     * Checks whether this thread group is a daemon {@code ThreadGroup}.
+     *
+     * @return true if this thread group is a daemon {@code ThreadGroup}
+     *
+     * @see #setDaemon
+     * @see #destroy
+     */
+    public final boolean isDaemon() {
+        return isDaemon;
+    }
+
+    /**
+     * Checks whether this thread group has already been destroyed.
+     *
+     * @return true if this thread group has already been destroyed
+     * @see #destroy
+     */
+    public synchronized boolean isDestroyed() {
+        return isDestroyed;
+    }
+
+    /**
+     * Outputs to {@code System.out} a text representation of the
+     * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively).
+     * Proper indentation is used to show the nesting of groups inside groups
+     * and threads inside groups.
+     */
+    public void list() {
+        // We start in a fresh line
+        System.out.println();
+        list(0);
+    }
+
+    /*
+     * Outputs to {@code System.out}a text representation of the
+     * hierarchy of Threads and ThreadGroups in this thread group (and recursively).
+     * The indentation will be four spaces per level of nesting.
+     *
+     * @param levels How many levels of nesting, so that proper indentation can
+     * be output.
+     */
+    private void list(int levels) {
+        indent(levels);
+        System.out.println(this.toString());
+
+        ++levels;
+        synchronized (threadRefs) {
+            for (Thread thread : threads) {
+                indent(levels);
+                System.out.println(thread);
+            }
+        }
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                group.list(levels);
+            }
+        }
+    }
+
+    private void indent(int levels) {
+        for (int i = 0; i < levels; i++) {
+            System.out.print("    "); // 4 spaces for each level
+        }
+    }
+
+    /**
+     * Checks whether this thread group is a direct or indirect parent group of a
+     * given {@code ThreadGroup}.
+     *
+     * @param g the potential child {@code ThreadGroup}
+     * @return true if this thread group is parent of {@code g}
+     */
+    public final boolean parentOf(ThreadGroup g) {
+        while (g != null) {
+            if (this == g) {
+                return true;
+            }
+            g = g.parent;
+        }
+        return false;
+    }
+
+    /**
+     * Removes an immediate subgroup.
+     *
+     * @param g ThreadGroup to remove
+     *
+     * @see #add(Thread)
+     * @see #add(ThreadGroup)
+     */
+    private void remove(ThreadGroup g) {
+        synchronized (groups) {
+            for (Iterator<ThreadGroup> i = groups.iterator(); i.hasNext(); ) {
+                ThreadGroup threadGroup = i.next();
+                if (threadGroup.equals(g)) {
+                    i.remove();
+                    break;
+                }
+            }
+        }
+        destroyIfEmptyDaemon();
+    }
+
+    /**
+     * Resumes every thread in this group and recursively in all its
+     * subgroups.
+     *
+     * @see Thread#resume
+     * @see #suspend
+     *
+     * @deprecated Requires deprecated method Thread.resume().
+     */
+    @SuppressWarnings("deprecation")
+    @Deprecated
+    public final void resume() {
+        synchronized (threadRefs) {
+            for (Thread thread : threads) {
+                thread.resume();
+            }
+        }
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                group.resume();
+            }
+        }
+    }
+
+    /**
+     * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon
+     * thread groups are automatically destroyed when they become empty.
+     *
+     * @param isDaemon the new value
+     * @see #isDaemon
+     * @see #destroy
+     */
+    public final void setDaemon(boolean isDaemon) {
+        this.isDaemon = isDaemon;
+    }
+
+    /**
+     * Configures the maximum allowed priority for a {@code Thread} in this group and
+     * recursively in all its subgroups.
+     *
+     * <p>A caller can never increase the maximum priority of a thread group.
+     * Such an attempt will not result in an exception, it will
+     * simply leave the thread group with its current maximum priority.
+     *
+     * @param newMax the new maximum priority to be set
+     *
+     * @throws IllegalArgumentException if the new priority is greater than
+     *         Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY
+     *
+     * @see #getMaxPriority
+     */
+    public final void setMaxPriority(int newMax) {
+        if (newMax <= this.maxPriority) {
+            if (newMax < Thread.MIN_PRIORITY) {
+                newMax = Thread.MIN_PRIORITY;
+            }
+
+            int parentPriority = parent == null ? newMax : parent.getMaxPriority();
+            this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
+            synchronized (groups) {
+                for (ThreadGroup group : groups) {
+                    group.setMaxPriority(newMax);
+                }
+            }
+        }
+    }
+
+    /**
+     * Stops every thread in this group and recursively in all its subgroups.
+     *
+     * @see Thread#stop()
+     * @see Thread#stop(Throwable)
+     * @see ThreadDeath
+     *
+     * @deprecated Requires deprecated method Thread.stop().
+     */
+    @SuppressWarnings("deprecation")
+    @Deprecated
+    public final void stop() {
+        if (stopHelper()) {
+            Thread.currentThread().stop();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private boolean stopHelper() {
+        boolean stopCurrent = false;
+        synchronized (threadRefs) {
+            Thread current = Thread.currentThread();
+            for (Thread thread : threads) {
+                if (thread == current) {
+                    stopCurrent = true;
+                } else {
+                    thread.stop();
+                }
+            }
+        }
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                stopCurrent |= group.stopHelper();
+            }
+        }
+        return stopCurrent;
+    }
+
+    /**
+     * Suspends every thread in this group and recursively in all its
+     * subgroups.
+     *
+     * @see Thread#suspend
+     * @see #resume
+     *
+     * @deprecated Requires deprecated method Thread.suspend().
+     */
+    @SuppressWarnings("deprecation")
+    @Deprecated
+    public final void suspend() {
+        if (suspendHelper()) {
+            Thread.currentThread().suspend();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private boolean suspendHelper() {
+        boolean suspendCurrent = false;
+        synchronized (threadRefs) {
+            Thread current = Thread.currentThread();
+            for (Thread thread : threads) {
+                if (thread == current) {
+                    suspendCurrent = true;
+                } else {
+                    thread.suspend();
+                }
+            }
+        }
+        synchronized (groups) {
+            for (ThreadGroup group : groups) {
+                suspendCurrent |= group.suspendHelper();
+            }
+        }
+        return suspendCurrent;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName() + "[name=" + getName()
+                + ",maxPriority=" + getMaxPriority() + "]";
+    }
+
+    /**
+     * Handles uncaught exceptions. Any uncaught exception in any {@code Thread}
+     * is forwarded to the thread's {@code ThreadGroup} by invoking this
+     * method.
+     *
+     * <p>New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups.
+     *
+     * @param t the Thread that terminated with an uncaught exception
+     * @param e the uncaught exception itself
+     */
+    public void uncaughtException(Thread t, Throwable e) {
+        if (parent != null) {
+            parent.uncaughtException(t, e);
+        } else if (Thread.getDefaultUncaughtExceptionHandler() != null) {
+            // TODO The spec is unclear regarding this. What do we do?
+            Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e);
+        } else if (!(e instanceof ThreadDeath)) {
+            // No parent group, has to be 'system' Thread Group
+            e.printStackTrace(System.err);
+        }
+    }
+
+    /**
+     * Called by the Thread constructor.
+     */
+    final void addThread(Thread thread) throws IllegalThreadStateException {
+        synchronized (threadRefs) {
+            if (isDestroyed) {
+                throw new IllegalThreadStateException();
+            }
+            threadRefs.add(new WeakReference<Thread>(thread));
+        }
+    }
+
+    /**
+     * Called by the VM when a Thread dies.
+     */
+    final void removeThread(Thread thread) throws IllegalThreadStateException {
+        synchronized (threadRefs) {
+            for (Iterator<Thread> i = threads.iterator(); i.hasNext(); ) {
+                if (i.next().equals(thread)) {
+                    i.remove();
+                    break;
+                }
+            }
+        }
+        destroyIfEmptyDaemon();
+    }
+}
diff --git a/libart/src/main/java/java/lang/VMClassLoader.java b/libart/src/main/java/java/lang/VMClassLoader.java
new file mode 100644
index 0000000..d180a4d
--- /dev/null
+++ b/libart/src/main/java/java/lang/VMClassLoader.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 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.lang;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+class VMClassLoader {
+
+    /**
+     * Get a resource from a file in the bootstrap class path.
+     *
+     * It would be simpler to just walk through the class path elements
+     * ourselves, but that would require reopening Jar files.
+     *
+     * We assume that the bootclasspath can't change once the VM has
+     * started.  This assumption seems to be supported by the spec.
+     */
+    static URL getResource(String name) {
+        int numEntries = getBootClassPathSize();
+        for (int i = 0; i < numEntries; i++) {
+            String urlStr = getBootClassPathResource(name, i);
+            if (urlStr != null) {
+                try {
+                    return new URL(urlStr);
+                } catch (MalformedURLException mue) {
+                    mue.printStackTrace();
+                    // unexpected; keep going
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Get an enumeration with all matching resources.
+     */
+    static List<URL> getResources(String name) {
+        ArrayList<URL> list = new ArrayList<URL>();
+        int numEntries = getBootClassPathSize();
+        for (int i = 0; i < numEntries; i++) {
+            String urlStr = getBootClassPathResource(name, i);
+            if (urlStr != null) {
+                try {
+                    list.add(new URL(urlStr));
+                } catch (MalformedURLException mue) {
+                    mue.printStackTrace();
+                    // unexpected; keep going
+                }
+            }
+        }
+        return list;
+    }
+
+    native static Class findLoadedClass(ClassLoader cl, String name);
+
+    /**
+     * Boot class path manipulation, for getResources().
+     */
+    native private static int getBootClassPathSize();
+    native private static String getBootClassPathResource(String name, int index);
+}
diff --git a/libart/src/main/java/java/lang/reflect/AbstractMethod.java b/libart/src/main/java/java/lang/reflect/AbstractMethod.java
new file mode 100644
index 0000000..ff52b4106
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/AbstractMethod.java
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+/*
+ * 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.lang.reflect;
+
+import com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.GenericSignatureParser;
+import libcore.reflect.ListOfTypes;
+import libcore.reflect.Types;
+
+/**
+ * This class represents an abstract method. Abstract methods are either methods or constructors.
+ * @hide
+ */
+public abstract class AbstractMethod extends AccessibleObject {
+
+    protected final ArtMethod artMethod;
+
+    protected AbstractMethod(ArtMethod artMethod) {
+        if (artMethod == null) {
+            throw new NullPointerException("artMethod == null");
+        }
+        this.artMethod = artMethod;
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+        return super.getAnnotation(annotationClass);
+    }
+
+    /**
+     * We insert native method stubs for abstract methods so we don't have to
+     * check the access flags at the time of the method call.  This results in
+     * "native abstract" methods, which can't exist.  If we see the "abstract"
+     * flag set, clear the "native" flag.
+     *
+     * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+     * position, because the callers of this function are trying to convey
+     * the "traditional" meaning of the flags to their callers.
+     */
+    private static int fixMethodFlags(int flags) {
+        if ((flags & Modifier.ABSTRACT) != 0) {
+            flags &= ~Modifier.NATIVE;
+        }
+        flags &= ~Modifier.SYNCHRONIZED;
+        int ACC_DECLARED_SYNCHRONIZED = 0x00020000;
+        if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+            flags |= Modifier.SYNCHRONIZED;
+        }
+        return flags & 0xffff;  // mask out bits not used by Java
+    }
+
+    int getModifiers() {
+        return fixMethodFlags(artMethod.getAccessFlags());
+    }
+
+    boolean isVarArgs() {
+        return (artMethod.getAccessFlags() & Modifier.VARARGS) != 0;
+    }
+
+    boolean isBridge() {
+        return (artMethod.getAccessFlags() & Modifier.BRIDGE) != 0;
+    }
+
+    boolean isSynthetic() {
+        return (artMethod.getAccessFlags() & Modifier.SYNTHETIC) != 0;
+    }
+
+    /**
+     * @hide
+     */
+    public final int getAccessFlags() {
+        return artMethod.getAccessFlags();
+    }
+
+    /**
+     * Returns the class that declares this constructor or method.
+     */
+    Class<?> getDeclaringClass() {
+        return artMethod.getDeclaringClass();
+    }
+
+    /**
+     * Returns the index of this method's ID in its dex file.
+     *
+     * @hide
+     */
+    public final int getDexMethodIndex() {
+        return artMethod.getDexMethodIndex();
+    }
+
+    /**
+     * Returns the name of the method or constructor represented by this
+     * instance.
+     *
+     * @return the name of this method
+     */
+    abstract public String getName();
+
+    /**
+     * Returns an array of {@code Class} objects associated with the parameter types of this
+     * abstract method. If the method was declared with no parameters, an
+     * empty array will be returned.
+     *
+     * @return the parameter types
+     */
+    public Class<?>[] getParameterTypes() {
+        return artMethod.getParameterTypes();
+    }
+
+    /**
+     * Returns true if {@code other} has the same declaring class, name,
+     * parameters and return type as this method.
+     */
+    @Override public boolean equals(Object other) {
+        if (!(other instanceof AbstractMethod)) {
+            return false;
+        }
+        // exactly one instance of each member in this runtime
+        return this.artMethod == ((AbstractMethod) other).artMethod;
+    }
+
+    String toGenericString() {
+        return toGenericStringHelper();
+    }
+
+    Type[] getGenericParameterTypes() {
+        return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false);
+    }
+
+    Type[] getGenericExceptionTypes() {
+        return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false);
+    }
+
+    @Override public Annotation[] getDeclaredAnnotations() {
+        List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+        return result.toArray(new Annotation[result.size()]);
+    }
+
+    @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+    }
+
+    public Annotation[] getAnnotations() {
+        return super.getAnnotations();
+    }
+
+    /**
+     * Returns an array of arrays that represent the annotations of the formal
+     * parameters of this method. If there are no parameters on this method,
+     * then an empty array is returned. If there are no annotations set, then
+     * and array of empty arrays is returned.
+     *
+     * @return an array of arrays of {@code Annotation} instances
+     */
+    public abstract Annotation[][] getParameterAnnotations();
+
+    /**
+     * Returns the constructor's signature in non-printable form. This is called
+     * (only) from IO native code and needed for deriving the serialVersionUID
+     * of the class
+     *
+     * @return The constructor's signature.
+     */
+    @SuppressWarnings("unused")
+    abstract String getSignature();
+
+    static final class GenericInfo {
+        final ListOfTypes genericExceptionTypes;
+        final ListOfTypes genericParameterTypes;
+        final Type genericReturnType;
+        final TypeVariable<?>[] formalTypeParameters;
+
+        GenericInfo(ListOfTypes exceptions, ListOfTypes parameters, Type ret,
+                    TypeVariable<?>[] formal) {
+            genericExceptionTypes = exceptions;
+            genericParameterTypes = parameters;
+            genericReturnType = ret;
+            formalTypeParameters = formal;
+        }
+    }
+
+    /**
+     * Returns generic information associated with this method/constructor member.
+     */
+    final GenericInfo getMethodOrConstructorGenericInfo() {
+        String signatureAttribute = AnnotationAccess.getSignature(this);
+        Member member;
+        Class<?>[] exceptionTypes;
+        boolean method = this instanceof Method;
+        if (method) {
+            Method m = (Method) this;
+            member = m;
+            exceptionTypes = m.getExceptionTypes();
+        } else {
+            Constructor<?> c = (Constructor<?>) this;
+            member = c;
+            exceptionTypes = c.getExceptionTypes();
+        }
+        GenericSignatureParser parser =
+            new GenericSignatureParser(member.getDeclaringClass().getClassLoader());
+        if (method) {
+            parser.parseForMethod((GenericDeclaration) this, signatureAttribute, exceptionTypes);
+        } else {
+            parser.parseForConstructor((GenericDeclaration) this,
+                                       signatureAttribute,
+                                       exceptionTypes);
+        }
+        return new GenericInfo(parser.exceptionTypes, parser.parameterTypes,
+                               parser.returnType, parser.formalTypeParameters);
+    }
+
+    /**
+     * Helper for Method and Constructor for toGenericString
+     */
+    final String toGenericStringHelper() {
+        StringBuilder sb = new StringBuilder(80);
+        GenericInfo info =  getMethodOrConstructorGenericInfo();
+        int modifiers = ((Member)this).getModifiers();
+        // append modifiers if any
+        if (modifiers != 0) {
+            sb.append(Modifier.toString(modifiers & ~Modifier.VARARGS)).append(' ');
+        }
+        // append type parameters
+        if (info.formalTypeParameters != null && info.formalTypeParameters.length > 0) {
+            sb.append('<');
+            for (int i = 0; i < info.formalTypeParameters.length; i++) {
+                Types.appendGenericType(sb, info.formalTypeParameters[i]);
+                if (i < info.formalTypeParameters.length - 1) {
+                    sb.append(",");
+                }
+            }
+            sb.append("> ");
+        }
+        Class<?> declaringClass = ((Member) this).getDeclaringClass();
+        if (this instanceof Constructor) {
+            // append constructor name
+            Types.appendTypeName(sb, declaringClass);
+        } else {
+            // append return type
+            Types.appendGenericType(sb, Types.getType(info.genericReturnType));
+            sb.append(' ');
+            // append method name
+            Types.appendTypeName(sb, declaringClass);
+            sb.append(".").append(((Method) this).getName());
+        }
+        // append parameters
+        sb.append('(');
+        Types.appendArrayGenericType(sb, info.genericParameterTypes.getResolvedTypes());
+        sb.append(')');
+        // append exceptions if any
+        Type[] genericExceptionTypeArray =
+            Types.getTypeArray(info.genericExceptionTypes, false);
+        if (genericExceptionTypeArray.length > 0) {
+            sb.append(" throws ");
+            Types.appendArrayGenericType(sb, genericExceptionTypeArray);
+        }
+        return sb.toString();
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/AccessibleObject.java b/libart/src/main/java/java/lang/reflect/AccessibleObject.java
new file mode 100644
index 0000000..dd57a12
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang.reflect;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * {@code AccessibleObject} is the superclass of all member reflection classes
+ * (Field, Constructor, Method). AccessibleObject provides the ability to toggle
+ * a flag controlling access checks for these objects. By default, accessing a
+ * member (for example, setting a field or invoking a method) checks the
+ * validity of the access (for example, invoking a private method from outside
+ * the defining class is prohibited) and throws IllegalAccessException if the
+ * operation is not permitted. If the accessible flag is set to true, these
+ * checks are omitted. This allows privileged code, such as Java object
+ * serialization, object inspectors, and debuggers to have complete access to
+ * objects.
+ *
+ * @see Field
+ * @see Constructor
+ * @see Method
+ */
+public class AccessibleObject implements AnnotatedElement {
+    protected AccessibleObject() {
+    }
+
+    /**
+     * If true, object is accessible, bypassing normal access checks
+     */
+    private boolean flag = false;
+
+    /**
+     * Returns true if this object is accessible without access checks.
+     */
+    public boolean isAccessible() {
+        return flag;
+    }
+
+    /**
+     * Attempts to set the accessible flag. Setting this to true prevents {@code
+     * IllegalAccessExceptions}.
+     */
+    public void setAccessible(boolean flag) {
+        this.flag = flag;
+     }
+
+    /**
+     * Attempts to set the accessible flag for all objects in {@code objects}.
+     * Setting this to true prevents {@code IllegalAccessExceptions}.
+     */
+    public static void setAccessible(AccessibleObject[] objects, boolean flag) {
+        for (AccessibleObject object : objects) {
+            object.flag = flag;
+        }
+    }
+
+    @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public Annotation[] getDeclaredAnnotations() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public Annotation[] getAnnotations() {
+        // for all but Class, getAnnotations == getDeclaredAnnotations
+        return getDeclaredAnnotations();
+    }
+
+    @Override public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/ArtField.java b/libart/src/main/java/java/lang/reflect/ArtField.java
new file mode 100644
index 0000000..6fdcdb2
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/ArtField.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang.reflect;
+
+import com.android.dex.Dex;
+
+/**
+ * @hide
+ */
+public final class ArtField {
+
+    private Class<?> declaringClass;
+    /** Field access flags (modifiers) */
+    private int accessFlags;
+    /** Index into DexFile's field ids */
+    private int fieldDexIndex;
+    /** Offset of field in object or class */
+    private int offset;
+
+    /**
+     * Only created by art directly.
+     */
+    private ArtField() {}
+
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    int getDexFieldIndex() {
+        return fieldDexIndex;
+    }
+
+    int getOffset() {
+        return offset;
+    }
+
+    public String getName() {
+        if (fieldDexIndex == -1) {
+            // Proxy classes have 1 synthesized static field with no valid dex index
+            if (!declaringClass.isProxy()) {
+                throw new AssertionError();
+            }
+            return "throws";
+        }
+        Dex dex = declaringClass.getDex();
+        int nameIndex = dex.nameIndexFromFieldIndex(fieldDexIndex);
+        return declaringClass.getDexCacheString(dex, nameIndex);
+    }
+
+    Class<?> getDeclaringClass() {
+        return declaringClass;
+    }
+
+    Class<?> getType() {
+        if (fieldDexIndex == -1) {
+            // The type of the synthesized field in a Proxy class is Class[][]
+            if (!declaringClass.isProxy()) {
+                throw new AssertionError();
+            }
+            return Class[][].class;
+        }
+        Dex dex = declaringClass.getDex();
+        int typeIndex = dex.typeIndexFromFieldIndex(fieldDexIndex);
+        return declaringClass.getDexCacheType(dex, typeIndex);
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/ArtMethod.java b/libart/src/main/java/java/lang/reflect/ArtMethod.java
new file mode 100644
index 0000000..fd73331
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/ArtMethod.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+/*
+ * 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.lang.reflect;
+
+import com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import libcore.reflect.AnnotationAccess;
+import libcore.util.EmptyArray;
+
+/**
+ * This class represents methods and constructors.
+ * @hide
+ */
+public final class ArtMethod {
+
+    /** Method's declaring class */
+    private Class<?> declaringClass;
+    /** Method access flags (modifiers) */
+    private int accessFlags;
+    /** DexFile index */
+    private int methodDexIndex;
+    /** Dispatch table entry */
+    private int methodIndex;
+    /** DexFile offset of CodeItem for this Method */
+    private int codeItemOffset;
+    /* ART compiler meta-data */
+    private int frameSizeInBytes;
+    private int coreSpillMask;
+    private int fpSpillMask;
+    private int mappingTable;
+    private int gcMap;
+    private int vmapTable;
+    /** ART: compiled managed code associated with this Method */
+    private int entryPointFromCompiledCode;
+    /** ART: entry point from interpreter associated with this Method */
+    private int entryPointFromInterpreter;
+    /** ART: if this is a native method, the native code that will be invoked */
+    private int nativeMethod;
+    /* ART: dex cache fast access */
+    private String[] dexCacheStrings;
+    Class<?>[] dexCacheResolvedTypes;
+    private ArtMethod[] dexCacheResolvedMethods;
+    private Object[] dexCacheInitializedStaticStorage;
+
+    /**
+     * Only created by art directly.
+     */
+    private ArtMethod() {}
+
+    Class getDeclaringClass() {
+        return declaringClass;
+    }
+
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    int getDexMethodIndex() {
+        return methodDexIndex;
+    }
+
+    public static String getMethodName(ArtMethod artMethod) {
+        artMethod = artMethod.findOverriddenMethodIfProxy();
+        Dex dex = artMethod.getDeclaringClass().getDex();
+        int nameIndex = dex.nameIndexFromMethodIndex(artMethod.getDexMethodIndex());
+        // Note, in the case of a Proxy the dex cache strings are equal.
+        return artMethod.getDexCacheString(dex, nameIndex);
+    }
+
+    /**
+     * Returns true if the given parameters match those of the method in the given order.
+     *
+     * @hide
+     */
+    public static boolean equalConstructorParameters(ArtMethod artMethod, Class<?>[] params) {
+        Dex dex = artMethod.getDeclaringClass().getDex();
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(artMethod.getDexMethodIndex());
+        if (types.length != params.length) {
+            return false;
+        }
+        for (int i = 0; i < types.length; i++) {
+            if (artMethod.getDexCacheType(dex, types[i]) != params[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the given parameters match those of this method in the given order.
+     *
+     * @hide
+     */
+    public static boolean equalMethodParameters(ArtMethod artMethod, Class<?>[] params) {
+        return equalConstructorParameters(artMethod.findOverriddenMethodIfProxy(), params);
+    }
+
+    Class<?>[] getParameterTypes() {
+        Dex dex = getDeclaringClass().getDex();
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+        if (types.length == 0) {
+            return EmptyArray.CLASS;
+        }
+        Class<?>[] parametersArray = new Class[types.length];
+        for (int i = 0; i < types.length; i++) {
+            // Note, in the case of a Proxy the dex cache types are equal.
+            parametersArray[i] = getDexCacheType(dex, types[i]);
+        }
+        return parametersArray;
+    }
+
+    Class<?> getReturnType() {
+        Dex dex = declaringClass.getDex();
+        int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(methodDexIndex);
+        // Note, in the case of a Proxy the dex cache types are equal.
+        return getDexCacheType(dex, returnTypeIndex);
+    }
+
+    /**
+     * Performs a comparison of the parameters to this method with the given parameters.
+     *
+     * @hide
+     */
+    int compareParameters(Class<?>[] params) {
+        Dex dex = getDeclaringClass().getDex();
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+        int length = Math.min(types.length, params.length);
+        for (int i = 0; i < length; i++) {
+            Class<?> aType = getDexCacheType(dex, types[i]);
+            Class<?> bType = params[i];
+            if (aType != bType) {
+                int comparison = aType.getName().compareTo(bType.getName());
+                if (comparison != 0) {
+                    return comparison;
+                }
+            }
+        }
+        return types.length - params.length;
+    }
+
+    Annotation[][] getParameterAnnotations() {
+        return AnnotationAccess.getParameterAnnotations(declaringClass, methodDexIndex);
+    }
+
+    /**
+     * Returns a string from the dex cache, computing the string from the dex file if necessary.
+     * Note this method replicates {@link java.lang.Class#getDexCacheString(Dex, int)}, but in
+     * Method we can avoid one indirection.
+     */
+    private String getDexCacheString(Dex dex, int dexStringIndex) {
+        String s = (String) dexCacheStrings[dexStringIndex];
+        if (s == null) {
+            s = dex.strings().get(dexStringIndex).intern();
+            dexCacheStrings[dexStringIndex] = s;
+        }
+        return s;
+    }
+
+    /**
+     * Returns a resolved type from the dex cache, computing the string from the dex file if
+     * necessary. Note this method delegates to {@link java.lang.Class#getDexCacheType(Dex, int)},
+     * but in Method we can avoid one indirection.
+     */
+    private Class<?> getDexCacheType(Dex dex, int dexTypeIndex) {
+        Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex];
+        if (resolvedType == null) {
+            resolvedType = declaringClass.getDexCacheType(dex, dexTypeIndex);
+        }
+        return resolvedType;
+    }
+
+    /**
+     * Returns the {@code ArtMethod} that this method overrides for
+     * proxy methods, otherwise returns this method. Used to determine
+     * the interface method overridden by a proxy method (as the proxy
+     * method doesn't directly support operations such as {@link
+     * Method#getName}).
+     */
+    ArtMethod findOverriddenMethodIfProxy() {
+        if (declaringClass.isProxy()) {
+            // Proxy method's declaring class' dex cache refers to that of Proxy. The local cache in
+            // Method refers to the original interface's dex cache and is ensured to be resolved by
+            // proxy generation.
+            return dexCacheResolvedMethods[methodDexIndex];
+        }
+        return this;
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Constructor.java b/libart/src/main/java/java/lang/reflect/Constructor.java
new file mode 100644
index 0000000..b3df2f0
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Constructor.java
@@ -0,0 +1,327 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang.reflect;
+
+import com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.Types;
+
+/**
+ * This class represents a constructor. Information about the constructor can be
+ * accessed, and the constructor can be invoked dynamically.
+ *
+ * @param <T> the class that declares this constructor
+ */
+public final class Constructor<T> extends AbstractMethod implements GenericDeclaration, Member {
+
+    private static final Comparator<Method> ORDER_BY_SIGNATURE = null; // Unused; must match Method.
+
+    /**
+     * @hide
+     */
+    public Constructor(ArtMethod artMethod) {
+        super(artMethod);
+    }
+
+    public Annotation[] getAnnotations() {
+        return super.getAnnotations();
+    }
+
+    /**
+     * Returns the modifiers for this constructor. The {@link Modifier} class
+     * should be used to decode the result.
+     */
+    @Override public int getModifiers() {
+        return super.getModifiers();
+    }
+
+    /**
+     * Returns true if this constructor takes a variable number of arguments.
+     */
+    public boolean isVarArgs() {
+        return super.isVarArgs();
+    }
+
+    /**
+     * Returns true if this constructor is synthetic (artificially introduced by the compiler).
+     */
+    @Override public boolean isSynthetic() {
+        return super.isSynthetic();
+    }
+
+    /**
+     * Returns the name of this constructor.
+     */
+    @Override public String getName() {
+        return getDeclaringClass().getName();
+    }
+
+    /**
+     * Returns the class that declares this constructor.
+     */
+    @Override public Class<T> getDeclaringClass() {
+        return (Class<T>) super.getDeclaringClass();
+    }
+
+    /**
+     * Returns the exception types as an array of {@code Class} instances. If
+     * this constructor has no declared exceptions, an empty array will be
+     * returned.
+     */
+    public Class<?>[] getExceptionTypes() {
+        // TODO: use dex cache to speed looking up class
+        return AnnotationAccess.getExceptions(this);
+    }
+
+    /**
+     * Returns an array of the {@code Class} objects associated with the
+     * parameter types of this constructor. If the constructor was declared with
+     * no parameters, an empty array will be returned.
+     */
+    public Class<?>[] getParameterTypes() {
+        return super.getParameterTypes();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code getDeclaringClass().getName().hashCode()}.
+     */
+    @Override public int hashCode() {
+        return getDeclaringClass().getName().hashCode();
+    }
+
+    /**
+     * Returns true if {@code other} has the same declaring class and parameters
+     * as this constructor.
+     */
+    @Override public boolean equals(Object other) {
+        return super.equals(other);
+    }
+
+    @Override public TypeVariable<Constructor<T>>[] getTypeParameters() {
+        GenericInfo info = getMethodOrConstructorGenericInfo();
+        return (TypeVariable<Constructor<T>>[]) info.formalTypeParameters.clone();
+    }
+
+    /**
+     * Returns the string representation of the constructor's declaration,
+     * including the type parameters.
+     *
+     * @return the string representation of the constructor's declaration
+     */
+    public String toGenericString() {
+        return super.toGenericString();
+    }
+
+    /**
+     * Returns the generic parameter types as an array of {@code Type}
+     * instances, in declaration order. If this constructor has no generic
+     * parameters, an empty array is returned.
+     *
+     * @return the parameter types
+     *
+     * @throws GenericSignatureFormatError
+     *             if the generic constructor signature is invalid
+     * @throws TypeNotPresentException
+     *             if any parameter type points to a missing type
+     * @throws MalformedParameterizedTypeException
+     *             if any parameter type points to a type that cannot be
+     *             instantiated for some reason
+     */
+    public Type[] getGenericParameterTypes() {
+        return super.getGenericParameterTypes();
+    }
+
+    /**
+     * Returns the exception types as an array of {@code Type} instances. If
+     * this constructor has no declared exceptions, an empty array will be
+     * returned.
+     *
+     * @return an array of generic exception types
+     *
+     * @throws GenericSignatureFormatError
+     *             if the generic constructor signature is invalid
+     * @throws TypeNotPresentException
+     *             if any exception type points to a missing type
+     * @throws MalformedParameterizedTypeException
+     *             if any exception type points to a type that cannot be
+     *             instantiated for some reason
+     */
+    public Type[] getGenericExceptionTypes() {
+        return super.getGenericExceptionTypes();
+    }
+
+    @Override public Annotation[] getDeclaredAnnotations() {
+        List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+        return result.toArray(new Annotation[result.size()]);
+    }
+
+    @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+    }
+
+    @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.getDeclaredAnnotation(this, annotationType);
+    }
+
+    /**
+     * Returns an array of arrays that represent the annotations of the formal
+     * parameters of this constructor. If there are no parameters on this
+     * constructor, then an empty array is returned. If there are no annotations
+     * set, then an array of empty arrays is returned.
+     *
+     * @return an array of arrays of {@code Annotation} instances
+     */
+    public Annotation[][] getParameterAnnotations() {
+        return artMethod.getParameterAnnotations();
+    }
+
+    /**
+     * Returns the constructor's signature in non-printable form. This is called
+     * (only) from IO native code and needed for deriving the serialVersionUID
+     * of the class
+     *
+     * @return the constructor's signature
+     */
+    @SuppressWarnings("unused")
+    String getSignature() {
+        StringBuilder result = new StringBuilder();
+
+        result.append('(');
+        Class<?>[] parameterTypes = getParameterTypes();
+        for (Class<?> parameterType : parameterTypes) {
+            result.append(Types.getSignature(parameterType));
+        }
+        result.append(")V");
+
+        return result.toString();
+    }
+
+    /**
+     * Returns a new instance of the declaring class, initialized by dynamically
+     * invoking the constructor represented by this {@code Constructor} object.
+     * This reproduces the effect of {@code new declaringClass(arg1, arg2, ... ,
+     * argN)} This method performs the following:
+     * <ul>
+     * <li>A new instance of the declaring class is created. If the declaring
+     * class cannot be instantiated (i.e. abstract class, an interface, an array
+     * type, or a primitive type) then an InstantiationException is thrown.</li>
+     * <li>If this Constructor object is enforcing access control (see
+     * {@link AccessibleObject}) and this constructor is not accessible from the
+     * current context, an IllegalAccessException is thrown.</li>
+     * <li>If the number of arguments passed and the number of parameters do not
+     * match, an IllegalArgumentException is thrown.</li>
+     * <li>For each argument passed:
+     * <ul>
+     * <li>If the corresponding parameter type is a primitive type, the argument
+     * is unboxed. If the unboxing fails, an IllegalArgumentException is
+     * thrown.</li>
+     * <li>If the resulting argument cannot be converted to the parameter type
+     * via a widening conversion, an IllegalArgumentException is thrown.</li>
+     * </ul>
+     * <li>The constructor represented by this {@code Constructor} object is
+     * then invoked. If an exception is thrown during the invocation, it is
+     * caught and wrapped in an InvocationTargetException. This exception is
+     * then thrown. If the invocation completes normally, the newly initialized
+     * object is returned.
+     * </ul>
+     *
+     * @param args
+     *            the arguments to the constructor
+     *
+     * @return the new, initialized, object
+     *
+     * @exception InstantiationException
+     *                if the class cannot be instantiated
+     * @exception IllegalAccessException
+     *                if this constructor is not accessible
+     * @exception IllegalArgumentException
+     *                if an incorrect number of arguments are passed, or an
+     *                argument could not be converted by a widening conversion
+     * @exception InvocationTargetException
+     *                if an exception was thrown by the invoked constructor
+     *
+     * @see AccessibleObject
+     */
+    public native T newInstance(Object... args) throws InstantiationException,
+        IllegalAccessException, IllegalArgumentException, InvocationTargetException;
+
+    /**
+     * Returns a string containing a concise, human-readable description of this
+     * constructor. The format of the string is:
+     *
+     * <ol>
+     *   <li>modifiers (if any)
+     *   <li>declaring class name
+     *   <li>'('
+     *   <li>parameter types, separated by ',' (if any)
+     *   <li>')'
+     *   <li>'throws' plus exception types, separated by ',' (if any)
+     * </ol>
+     *
+     * For example:
+     * {@code public String(byte[],String) throws UnsupportedEncodingException}
+     *
+     * @return a printable representation for this constructor
+     */
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+
+        if (result.length() != 0) {
+            result.append(' ');
+        }
+        result.append(getDeclaringClass().getName());
+        result.append("(");
+        Class<?>[] parameterTypes = getParameterTypes();
+        result.append(Types.toString(parameterTypes));
+        result.append(")");
+        Class<?>[] exceptionTypes = getExceptionTypes();
+        if (exceptionTypes.length > 0) {
+            result.append(" throws ");
+            result.append(Types.toString(exceptionTypes));
+        }
+
+        return result.toString();
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Field.java b/libart/src/main/java/java/lang/reflect/Field.java
new file mode 100644
index 0000000..4e982c7
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Field.java
@@ -0,0 +1,792 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang.reflect;
+
+import com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.GenericSignatureParser;
+import libcore.reflect.Types;
+
+/**
+ * This class represents a field. Information about the field can be accessed,
+ * and the field's value can be accessed dynamically.
+ */
+public final class Field extends AccessibleObject implements Member {
+
+    /**
+     * Orders fields by their name and declaring class.
+     *
+     * @hide
+     */
+    public static final Comparator<Field> ORDER_BY_NAME_AND_DECLARING_CLASS
+            = new Comparator<Field>() {
+        @Override public int compare(Field a, Field b) {
+            if (a == b) {
+                return 0;
+            }
+            int comparison = a.getName().compareTo(b.getName());
+            if (comparison != 0) {
+                return comparison;
+            }
+            Class<?> aType = a.getDeclaringClass();
+            Class<?> bType = b.getDeclaringClass();
+            if (aType == bType) {
+                return 0;
+            } else {
+                return aType.getName().compareTo(bType.getName());
+            }
+        }
+    };
+
+    private final ArtField artField;
+
+    /**
+     * @hide
+     */
+    public Field(ArtField artField) {
+        if (artField == null) {
+            throw new NullPointerException("artField == null");
+        }
+        this.artField = artField;
+    }
+
+    /**
+     * Returns the modifiers for this field. The {@link Modifier} class should
+     * be used to decode the result.
+     *
+     * @return the modifiers for this field
+     * @see Modifier
+     */
+    @Override public int getModifiers() {
+        return artField.getAccessFlags() & 0xffff;  // mask out bits not used by Java
+    }
+
+    /**
+     * Indicates whether or not this field is an enumeration constant.
+     *
+     * @return {@code true} if this field is an enumeration constant, {@code
+     *         false} otherwise
+     */
+    public boolean isEnumConstant() {
+        return (artField.getAccessFlags() & Modifier.ENUM) != 0;
+    }
+
+    /**
+     * Indicates whether or not this field is synthetic.
+     *
+     * @return {@code true} if this field is synthetic, {@code false} otherwise
+     */
+    @Override public boolean isSynthetic() {
+        return (artField.getAccessFlags() & Modifier.SYNTHETIC) != 0;
+    }
+
+    /**
+     * Returns the name of this field.
+     *
+     * @return the name of this field
+     */
+    @Override public String getName() {
+        return artField.getName();
+    }
+
+    @Override public Class<?> getDeclaringClass() {
+        return artField.getDeclaringClass();
+    }
+
+    /**
+     * Return the {@link Class} associated with the type of this field.
+     *
+     * @return the type of this field
+     */
+    public Class<?> getType() {
+        return artField.getType();
+    }
+
+    /**
+     * Returns the index of this field's ID in its dex file.
+     *
+     * @hide
+     */
+    public int getDexFieldIndex() {
+        return artField.getDexFieldIndex();
+    }
+
+    /**
+     * Returns the offset of the field within an instance, or for static fields, the class.
+     *
+     * @hide
+     */
+    public int getOffset() {
+        return artField.getOffset();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code getDeclaringClass().getName().hashCode() ^ getName().hashCode()}.
+     */
+    @Override public int hashCode() {
+        return getDeclaringClass().getName().hashCode() ^ getName().hashCode();
+    }
+
+    /**
+     * Returns true if {@code other} has the same declaring class, name and type
+     * as this field.
+     */
+    @Override public boolean equals(Object other) {
+        if (!(other instanceof Field)) {
+            return false;
+        }
+        // exactly one instance of each member in this runtime
+        return this.artField == ((Field) other).artField;
+    }
+
+    /**
+     * Returns the string representation of this field, including the field's
+     * generic type.
+     *
+     * @return the string representation of this field
+     */
+    public String toGenericString() {
+        StringBuilder sb = new StringBuilder(80);
+        // append modifiers if any
+        int modifier = getModifiers();
+        if (modifier != 0) {
+            sb.append(Modifier.toString(modifier)).append(' ');
+        }
+        // append generic type
+        Types.appendGenericType(sb, getGenericType());
+        sb.append(' ');
+        // append full field name
+        sb.append(getDeclaringClass().getName()).append('.').append(getName());
+        return sb.toString();
+    }
+
+    /**
+     * Returns the generic type of this field.
+     *
+     * @return the generic type
+     * @throws GenericSignatureFormatError
+     *             if the generic field signature is invalid
+     * @throws TypeNotPresentException
+     *             if the generic type points to a missing type
+     * @throws MalformedParameterizedTypeException
+     *             if the generic type points to a type that cannot be
+     *             instantiated for some reason
+     */
+    public Type getGenericType() {
+        String signatureAttribute = AnnotationAccess.getSignature(this);
+        Class<?> declaringClass = getDeclaringClass();
+        ClassLoader cl = declaringClass.getClassLoader();
+        GenericSignatureParser parser = new GenericSignatureParser(cl);
+        parser.parseForField(declaringClass, signatureAttribute);
+        Type genericType = parser.fieldType;
+        if (genericType == null) {
+            genericType = getType();
+        }
+        return genericType;
+    }
+
+    /**
+     * Returns the constructor's signature in non-printable form. This is called
+     * (only) from IO native code and needed for deriving the serialVersionUID
+     * of the class
+     */
+    @SuppressWarnings("unused")
+    private String getSignature() {
+        return Types.getSignature(getType());
+    }
+
+    @Override public Annotation[] getDeclaredAnnotations() {
+        List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+        return result.toArray(new Annotation[result.size()]);
+    }
+
+    @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.getDeclaredAnnotation(this, annotationType);
+    }
+
+    @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+    }
+
+    /**
+     * Returns the value of the field in the specified object. This reproduces
+     * the effect of {@code object.fieldName}
+     *
+     * <p>If the type of this field is a primitive type, the field value is
+     * automatically boxed.
+     *
+     * <p>If this field is static, the object argument is ignored.
+     * Otherwise, if the object is null, a NullPointerException is thrown. If
+     * the object is not an instance of the declaring class of the method, an
+     * IllegalArgumentException is thrown.
+     *
+     * <p>If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value, possibly boxed
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native Object get(Object object) throws IllegalAccessException, IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code
+     * boolean}. This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native boolean getBoolean(Object object) throws IllegalAccessException,
+                                                           IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code byte}.
+     * This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native byte getByte(Object object) throws IllegalAccessException,
+                                                     IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code char}.
+     * This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native char getChar(Object object) throws IllegalAccessException,
+                                                     IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code
+     * double}. This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native double getDouble(Object object) throws IllegalAccessException,
+                                                         IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code float}
+     * . This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native float getFloat(Object object) throws IllegalAccessException,
+                                                       IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as an {@code int}.
+     * This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native int getInt(Object object) throws IllegalAccessException,
+                                                   IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code long}.
+     * This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native long getLong(Object object) throws IllegalAccessException,
+                                                     IllegalArgumentException;
+
+    /**
+     * Returns the value of the field in the specified object as a {@code short}
+     * . This reproduces the effect of {@code object.fieldName}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @return the field value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native short getShort(Object object) throws IllegalAccessException,
+                                                       IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the value. This
+     * reproduces the effect of {@code object.fieldName = value}
+     *
+     * <p>If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     *
+     * <p>If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     *
+     * <p>If the field type is a primitive type, the value is automatically
+     * unboxed. If the unboxing fails, an IllegalArgumentException is thrown. If
+     * the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void set(Object object, Object value) throws IllegalAccessException,
+                                                               IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code
+     * boolean} value. This reproduces the effect of {@code object.fieldName =
+     * value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setBoolean(Object object, boolean value) throws IllegalAccessException,
+                                                                       IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code byte}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setByte(Object object, byte value) throws IllegalAccessException,
+                                                                 IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code char}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setChar(Object object, char value) throws IllegalAccessException,
+                                                                 IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code double}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setDouble(Object object, double value) throws IllegalAccessException,
+                                                                     IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code float}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setFloat(Object object, float value) throws IllegalAccessException,
+                                                                   IllegalArgumentException;
+
+    /**
+     * Set the value of the field in the specified object to the {@code int}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setInt(Object object, int value) throws IllegalAccessException,
+                                                               IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code long}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setLong(Object object, long value) throws IllegalAccessException,
+                                                                 IllegalArgumentException;
+
+    /**
+     * Sets the value of the field in the specified object to the {@code short}
+     * value. This reproduces the effect of {@code object.fieldName = value}
+     * <p>
+     * If this field is static, the object argument is ignored.
+     * Otherwise, if the object is {@code null}, a NullPointerException is
+     * thrown. If the object is not an instance of the declaring class of the
+     * method, an IllegalArgumentException is thrown.
+     * <p>
+     * If this Field object is enforcing access control (see AccessibleObject)
+     * and this field is not accessible from the current context, an
+     * IllegalAccessException is thrown.
+     * <p>
+     * If the value cannot be converted to the field type via a widening
+     * conversion, an IllegalArgumentException is thrown.
+     *
+     * @param object
+     *            the object to access
+     * @param value
+     *            the new value
+     * @throws NullPointerException
+     *             if the object is {@code null} and the field is non-static
+     * @throws IllegalArgumentException
+     *             if the object is not compatible with the declaring class
+     * @throws IllegalAccessException
+     *             if this field is not accessible
+     */
+    public native void setShort(Object object, short value) throws IllegalAccessException,
+                                                                   IllegalArgumentException;
+
+    /**
+     * Returns a string containing a concise, human-readable description of this
+     * field.
+     * <p>
+     * The format of the string is:
+     * <ol>
+     *   <li>modifiers (if any)
+     *   <li>type
+     *   <li>declaring class name
+     *   <li>'.'
+     *   <li>field name
+     * </ol>
+     * <p>
+     * For example: {@code public static java.io.InputStream
+     * java.lang.System.in}
+     *
+     * @return a printable representation for this field
+     */
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+        if (result.length() != 0) {
+            result.append(' ');
+        }
+        Types.appendTypeName(result, getType());
+        result.append(' ');
+        result.append(getDeclaringClass().getName());
+        result.append('.');
+        result.append(getName());
+        return result.toString();
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Method.java b/libart/src/main/java/java/lang/reflect/Method.java
new file mode 100644
index 0000000..3089932
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Method.java
@@ -0,0 +1,439 @@
+/*
+ * 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.
+ */
+/*
+ * 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 java.lang.reflect;
+
+import com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.Types;
+
+/**
+ * This class represents a method. Information about the method can be accessed,
+ * and the method can be invoked dynamically.
+ */
+public final class Method extends AbstractMethod implements GenericDeclaration, Member {
+
+    /**
+     * Orders methods by their name, parameters and return type.
+     *
+     * @hide
+     */
+    public static final Comparator<Method> ORDER_BY_SIGNATURE = new Comparator<Method>() {
+        @Override public int compare(Method a, Method b) {
+            if (a == b) {
+                return 0;
+            }
+            int comparison = a.getName().compareTo(b.getName());
+            if (comparison == 0) {
+                comparison = a.artMethod.findOverriddenMethodIfProxy().compareParameters(
+                        b.getParameterTypes());
+                if (comparison == 0) {
+                    // This is necessary for methods that have covariant return types.
+                    Class<?> aReturnType = a.getReturnType();
+                    Class<?> bReturnType = b.getReturnType();
+                    if (aReturnType == bReturnType) {
+                        comparison = 0;
+                    } else {
+                        comparison = aReturnType.getName().compareTo(bReturnType.getName());
+                    }
+                }
+            }
+            return comparison;
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public Method(ArtMethod artMethod) {
+        super(artMethod);
+    }
+
+    ArtMethod getArtMethod() {
+        return artMethod;
+    }
+
+    public Annotation[] getAnnotations() {
+        return super.getAnnotations();
+    }
+
+    /**
+     * Returns the modifiers for this method. The {@link Modifier} class should
+     * be used to decode the result.
+     *
+     * @return the modifiers for this method
+     *
+     * @see Modifier
+     */
+    @Override public int getModifiers() {
+        return super.getModifiers();
+    }
+
+    /**
+     * Indicates whether or not this method takes a variable number argument.
+     *
+     * @return {@code true} if a vararg is declared, {@code false} otherwise
+     */
+    public boolean isVarArgs() {
+        return super.isVarArgs();
+    }
+
+    /**
+     * Indicates whether or not this method is a bridge.
+     *
+     * @return {@code true} if this method is a bridge, {@code false} otherwise
+     */
+    public boolean isBridge() {
+        return super.isBridge();
+
+    }
+
+    /**
+     * Indicates whether or not this method is synthetic.
+     *
+     * @return {@code true} if this method is synthetic, {@code false} otherwise
+     */
+    @Override public boolean isSynthetic() {
+        return super.isSynthetic();
+    }
+
+    /**
+     * Returns the name of the method represented by this {@code Method}
+     * instance.
+     *
+     * @return the name of this method
+     */
+    @Override public String getName() {
+        return ArtMethod.getMethodName(artMethod);
+    }
+
+    /**
+     * Returns the class that declares this method.
+     */
+    @Override public Class<?> getDeclaringClass() {
+        return super.getDeclaringClass();
+    }
+
+    /**
+     * Returns the exception types as an array of {@code Class} instances. If
+     * this method has no declared exceptions, an empty array is returned.
+     *
+     * @return the declared exception classes
+     */
+    public Class<?>[] getExceptionTypes() {
+        if (getDeclaringClass().isProxy()) {
+            return getExceptionTypesNative();
+        } else {
+            // TODO: use dex cache to speed looking up class
+            return AnnotationAccess.getExceptions(this);
+        }
+    }
+
+    private native Class<?>[] getExceptionTypesNative();
+
+    /**
+     * Returns an array of {@code Class} objects associated with the parameter
+     * types of this method. If the method was declared with no parameters, an
+     * empty array will be returned.
+     *
+     * @return the parameter types
+     */
+    @Override public Class<?>[] getParameterTypes() {
+        return artMethod.findOverriddenMethodIfProxy().getParameterTypes();
+    }
+
+    /**
+     * Returns the {@code Class} associated with the return type of this
+     * method.
+     *
+     * @return the return type
+     */
+    public Class<?> getReturnType() {
+        return artMethod.findOverriddenMethodIfProxy().getReturnType();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code getDeclaringClass().getName().hashCode() ^ getName().hashCode()}.
+     */
+    @Override public int hashCode() {
+        return getDeclaringClass().getName().hashCode() ^ getName().hashCode();
+    }
+
+    /**
+     * Returns true if {@code other} has the same declaring class, name,
+     * parameters and return type as this method.
+     */
+    @Override public boolean equals(Object other) {
+        return super.equals(other);
+    }
+
+    /**
+     * Returns true if this and {@code method} have the same name and the same
+     * parameters in the same order. Such methods can share implementation if
+     * one method's return types is assignable to the other.
+     *
+     * @hide needed by Proxy
+     */
+    boolean equalNameAndParameters(Method m) {
+        return getName().equals(m.getName()) &&
+                ArtMethod.equalMethodParameters(artMethod,m.getParameterTypes());
+    }
+
+    /**
+     * Returns the string representation of the method's declaration, including
+     * the type parameters.
+     *
+     * @return the string representation of this method
+     */
+    public String toGenericString() {
+        return super.toGenericString();
+    }
+
+    @Override public TypeVariable<Method>[] getTypeParameters() {
+        GenericInfo info = getMethodOrConstructorGenericInfo();
+        return (TypeVariable<Method>[]) info.formalTypeParameters.clone();
+    }
+
+    /**
+     * Returns the parameter types as an array of {@code Type} instances, in
+     * declaration order. If this method has no parameters, an empty array is
+     * returned.
+     *
+     * @return the parameter types
+     *
+     * @throws GenericSignatureFormatError
+     *             if the generic method signature is invalid
+     * @throws TypeNotPresentException
+     *             if any parameter type points to a missing type
+     * @throws MalformedParameterizedTypeException
+     *             if any parameter type points to a type that cannot be
+     *             instantiated for some reason
+     */
+    public Type[] getGenericParameterTypes() {
+        return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false);
+    }
+
+    @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+    }
+
+    /**
+     * Returns the exception types as an array of {@code Type} instances. If
+     * this method has no declared exceptions, an empty array will be returned.
+     *
+     * @return an array of generic exception types
+     *
+     * @throws GenericSignatureFormatError
+     *             if the generic method signature is invalid
+     * @throws TypeNotPresentException
+     *             if any exception type points to a missing type
+     * @throws MalformedParameterizedTypeException
+     *             if any exception type points to a type that cannot be
+     *             instantiated for some reason
+     */
+    public Type[] getGenericExceptionTypes() {
+        return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false);
+    }
+
+    /**
+     * Returns the return type of this method as a {@code Type} instance.
+     *
+     * @return the return type of this method
+     *
+     * @throws GenericSignatureFormatError
+     *             if the generic method signature is invalid
+     * @throws TypeNotPresentException
+     *             if the return type points to a missing type
+     * @throws MalformedParameterizedTypeException
+     *             if the return type points to a type that cannot be
+     *             instantiated for some reason
+     */
+    public Type getGenericReturnType() {
+        return Types.getType(getMethodOrConstructorGenericInfo().genericReturnType);
+    }
+
+    @Override public Annotation[] getDeclaredAnnotations() {
+        List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+        return result.toArray(new Annotation[result.size()]);
+    }
+
+    @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+        if (annotationType == null) {
+            throw new NullPointerException("annotationType == null");
+        }
+        return AnnotationAccess.getDeclaredAnnotation(this, annotationType);
+    }
+
+    /**
+     * Returns an array of arrays that represent the annotations of the formal
+     * parameters of this method. If there are no parameters on this method,
+     * then an empty array is returned. If there are no annotations set, then
+     * and array of empty arrays is returned.
+     *
+     * @return an array of arrays of {@code Annotation} instances
+     */
+    public Annotation[][] getParameterAnnotations() {
+        return artMethod.findOverriddenMethodIfProxy().getParameterAnnotations();
+    }
+
+    /**
+     * Returns the default value for the annotation member represented by this
+     * method.
+     *
+     * @return the default value, or {@code null} if none
+     *
+     * @throws TypeNotPresentException
+     *             if this annotation member is of type {@code Class} and no
+     *             definition can be found
+     */
+    public Object getDefaultValue() {
+        return AnnotationAccess.getDefaultValue(this);
+    }
+
+    /**
+     * Returns the result of dynamically invoking this method. Equivalent to
+     * {@code receiver.methodName(arg1, arg2, ... , argN)}.
+     *
+     * <p>If the method is static, the receiver argument is ignored (and may be null).
+     *
+     * <p>If the method takes no arguments, you can pass {@code (Object[]) null} instead of
+     * allocating an empty array.
+     *
+     * <p>If you're calling a varargs method, you need to pass an {@code Object[]} for the
+     * varargs parameter: that conversion is usually done in {@code javac}, not the VM, and
+     * the reflection machinery does not do this for you. (It couldn't, because it would be
+     * ambiguous.)
+     *
+     * <p>Reflective method invocation follows the usual process for method lookup.
+     *
+     * <p>If an exception is thrown during the invocation it is caught and
+     * wrapped in an InvocationTargetException. This exception is then thrown.
+     *
+     * <p>If the invocation completes normally, the return value itself is
+     * returned. If the method is declared to return a primitive type, the
+     * return value is boxed. If the return type is void, null is returned.
+     *
+     * @param receiver
+     *            the object on which to call this method (or null for static methods)
+     * @param args
+     *            the arguments to the method
+     * @return the result
+     *
+     * @throws NullPointerException
+     *             if {@code receiver == null} for a non-static method
+     * @throws IllegalAccessException
+     *             if this method is not accessible (see {@link AccessibleObject})
+     * @throws IllegalArgumentException
+     *             if the number of arguments doesn't match the number of parameters, the receiver
+     *             is incompatible with the declaring class, or an argument could not be unboxed
+     *             or converted by a widening conversion to the corresponding parameter type
+     * @throws InvocationTargetException
+     *             if an exception was thrown by the invoked method
+     */
+    public native Object invoke(Object receiver, Object... args)
+        throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
+
+    /**
+     * Returns a string containing a concise, human-readable description of this
+     * method. The format of the string is:
+     *
+     * <ol>
+     *   <li>modifiers (if any)
+     *   <li>return type or 'void'
+     *   <li>declaring class name
+     *   <li>'('
+     *   <li>parameter types, separated by ',' (if any)
+     *   <li>')'
+     *   <li>'throws' plus exception types, separated by ',' (if any)
+     * </ol>
+     *
+     * For example: {@code public native Object
+     * java.lang.Method.invoke(Object,Object) throws
+     * IllegalAccessException,IllegalArgumentException
+     * ,InvocationTargetException}
+     *
+     * @return a printable representation for this method
+     */
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+
+        if (result.length() != 0) {
+            result.append(' ');
+        }
+        result.append(getReturnType().getName());
+        result.append(' ');
+        result.append(getDeclaringClass().getName());
+        result.append('.');
+        result.append(getName());
+        result.append("(");
+        Class<?>[] parameterTypes = getParameterTypes();
+        result.append(Types.toString(parameterTypes));
+        result.append(")");
+        Class<?>[] exceptionTypes = getExceptionTypes();
+        if (exceptionTypes.length != 0) {
+            result.append(" throws ");
+            result.append(Types.toString(exceptionTypes));
+        }
+        return result.toString();
+    }
+
+    /**
+     * Returns the constructor's signature in non-printable form. This is called
+     * (only) from IO native code and needed for deriving the serialVersionUID
+     * of the class
+     *
+     * @return The constructor's signature.
+     */
+    @SuppressWarnings("unused")
+    String getSignature() {
+        StringBuilder result = new StringBuilder();
+
+        result.append('(');
+        Class<?>[] parameterTypes = getParameterTypes();
+        for (Class<?> parameterType : parameterTypes) {
+            result.append(Types.getSignature(parameterType));
+        }
+        result.append(')');
+        result.append(Types.getSignature(getReturnType()));
+
+        return result.toString();
+    }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Proxy.java b/libart/src/main/java/java/lang/reflect/Proxy.java
new file mode 100644
index 0000000..51b3ad5
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Proxy.java
@@ -0,0 +1,381 @@
+/*
+ *  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 java.lang.reflect;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import libcore.util.EmptyArray;
+
+/**
+ * {@code Proxy} defines methods for creating dynamic proxy classes and instances.
+ * A proxy class implements a declared set of interfaces and delegates method
+ * invocations to an {@code InvocationHandler}.
+ *
+ * @see InvocationHandler
+ * @since 1.3
+ */
+public class Proxy implements Serializable {
+
+    private static final long serialVersionUID = -2222568056686623797L;
+
+    private static int NextClassNameIndex = 0;
+
+    /**
+     * Orders methods by their name, parameters, return type and inheritance relationship.
+     *
+     * @hide
+     */
+    private static final Comparator<Method> ORDER_BY_SIGNATURE_AND_SUBTYPE = new Comparator<Method>() {
+        @Override public int compare(Method a, Method b) {
+            int comparison = Method.ORDER_BY_SIGNATURE.compare(a, b);
+            if (comparison != 0) {
+                return comparison;
+            }
+            Class<?> aClass = a.getDeclaringClass();
+            Class<?> bClass = b.getDeclaringClass();
+            if (aClass == bClass) {
+                return 0;
+            } else if (aClass.isAssignableFrom(bClass)) {
+                return 1;
+            } else if (bClass.isAssignableFrom(aClass)) {
+                return -1;
+            } else {
+                return 0;
+            }
+        }
+    };
+
+    /** The invocation handler on which the method calls are dispatched. */
+    protected InvocationHandler h;
+
+    @SuppressWarnings("unused")
+    private Proxy() {
+    }
+
+    /**
+     * Constructs a new {@code Proxy} instance with the specified invocation
+     * handler.
+     *
+     * @param h
+     *            the invocation handler for the newly created proxy
+     */
+    protected Proxy(InvocationHandler h) {
+        this.h = h;
+    }
+
+    /**
+     * Returns the dynamically built {@code Class} for the specified interfaces.
+     * Creates a new {@code Class} when necessary. The order of the interfaces
+     * is relevant. Invocations of this method with the same interfaces but
+     * different order result in different generated classes. The interfaces
+     * must be visible from the supplied class loader; no duplicates are
+     * permitted. All non-public interfaces must be defined in the same package.
+     *
+     * @param loader
+     *            the class loader that will define the proxy class
+     * @param interfaces
+     *            an array of {@code Class} objects, each one identifying an
+     *            interface that will be implemented by the returned proxy
+     *            class
+     * @return a proxy class that implements all of the interfaces referred to
+     *         in the contents of {@code interfaces}
+     * @throws IllegalArgumentException
+     *                if any of the interface restrictions are violated
+     * @throws NullPointerException
+     *                if either {@code interfaces} or any of its elements are
+     *                {@code null}
+     */
+    public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
+            throws IllegalArgumentException {
+        if (loader == null) {
+            loader = ClassLoader.getSystemClassLoader();
+        }
+
+        if (interfaces == null) {
+            throw new NullPointerException("interfaces == null");
+        }
+
+        Set<Class<?>> interfacesSet = new CopyOnWriteArraySet<Class<?>>(Arrays.asList(interfaces));
+
+        Class<?> proxy = loader.proxyCache.get(interfacesSet);
+        if (proxy != null) {
+            return proxy;
+        }
+
+        if (interfacesSet.size() != interfaces.length) {
+            throw new IllegalArgumentException(
+                    "interfaces has duplicates: " + Arrays.toString(interfaces));
+        }
+        String commonPackageName = null;
+        for (Class<?> c : interfaces) {
+            if (c == null) {
+                throw new NullPointerException("interfaces contained a null element");
+            }
+            if (!c.isInterface()) {
+                throw new IllegalArgumentException(c + " is not an interface");
+            }
+            if (!isVisibleToClassLoader(loader, c)) {
+                throw new IllegalArgumentException(c + " is not visible from class loader");
+            }
+            if (!Modifier.isPublic(c.getModifiers())) {
+                String packageName = c.getPackageName$();
+                if (packageName == null) {
+                    packageName = "";
+                }
+                if (commonPackageName != null && !commonPackageName.equals(packageName)) {
+                    throw new IllegalArgumentException(
+                            "non-public interfaces must be in the same package");
+                }
+                commonPackageName = packageName;
+            }
+        }
+
+        String baseName = commonPackageName != null && !commonPackageName.isEmpty()
+                ? commonPackageName + ".$Proxy"
+                : "$Proxy";
+        String name = baseName + NextClassNameIndex++;
+
+        List<Method> methods = getMethods(interfaces);
+        Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
+        validateReturnTypes(methods);
+        List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
+
+        ArtMethod[] methodsArray = new ArtMethod[methods.size()];
+        for (int i = 0; i < methodsArray.length; i++) {
+            methodsArray[i] = methods.get(i).getArtMethod();
+        }
+        Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
+        Class<?> result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
+        loader.proxyCache.put(interfacesSet, result);
+        return result;
+    }
+
+    private static boolean isVisibleToClassLoader(ClassLoader loader, Class<?> c) {
+        try {
+            return loader == c.getClassLoader() || c == Class.forName(c.getName(), false, loader);
+        } catch (ClassNotFoundException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns an instance of the dynamically built class for the specified
+     * interfaces. Method invocations on the returned instance are forwarded to
+     * the specified invocation handler. The interfaces must be visible from the
+     * supplied class loader; no duplicates are permitted. All non-public
+     * interfaces must be defined in the same package.
+     *
+     * @param loader
+     *            the class loader that will define the proxy class
+     * @param interfaces
+     *            an array of {@code Class} objects, each one identifying an
+     *            interface that will be implemented by the returned proxy
+     *            object
+     * @param invocationHandler
+     *            the invocation handler that handles the dispatched method
+     *            invocations
+     * @return a new proxy object that delegates to the handler {@code h}
+     * @throws IllegalArgumentException
+     *                if any of the interface restrictions are violated
+     * @throws NullPointerException
+     *                if the interfaces or any of its elements are null
+     */
+    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
+                                          InvocationHandler invocationHandler)
+            throws IllegalArgumentException {
+
+        if (invocationHandler == null) {
+            throw new NullPointerException("invocationHandler == null");
+        }
+        Exception cause;
+        try {
+            return getProxyClass(loader, interfaces)
+                    .getConstructor(InvocationHandler.class)
+                    .newInstance(invocationHandler);
+        } catch (NoSuchMethodException e) {
+            cause = e;
+        } catch (IllegalAccessException e) {
+            cause = e;
+        } catch (InstantiationException e) {
+            cause = e;
+        } catch (InvocationTargetException e) {
+            cause = e;
+        }
+        AssertionError error = new AssertionError();
+        error.initCause(cause);
+        throw error;
+    }
+
+    /**
+     * Indicates whether or not the specified class is a dynamically generated
+     * proxy class.
+     *
+     * @param cl
+     *            the class
+     * @return {@code true} if the class is a proxy class, {@code false}
+     *         otherwise
+     * @throws NullPointerException
+     *                if the class is {@code null}
+     */
+    public static boolean isProxyClass(Class<?> cl) {
+        return cl.isProxy();
+    }
+
+    /**
+     * Returns the invocation handler of the specified proxy instance.
+     *
+     * @param proxy
+     *            the proxy instance
+     * @return the invocation handler of the specified proxy instance
+     * @throws IllegalArgumentException
+     *                if the supplied {@code proxy} is not a proxy object
+     */
+    public static InvocationHandler getInvocationHandler(Object proxy)
+            throws IllegalArgumentException {
+        // TODO: return false for subclasses of Proxy not created by generateProxy()
+        if (!(proxy instanceof Proxy)) {
+            throw new IllegalArgumentException("not a proxy instance");
+        }
+        return ((Proxy) proxy).h;
+    }
+
+    private static List<Method> getMethods(Class<?>[] interfaces) {
+        List<Method> result = new ArrayList<Method>();
+        try {
+            result.add(Object.class.getMethod("equals", Object.class));
+            result.add(Object.class.getMethod("hashCode", EmptyArray.CLASS));
+            result.add(Object.class.getMethod("toString", EmptyArray.CLASS));
+        } catch (NoSuchMethodException e) {
+            throw new AssertionError();
+        }
+
+        getMethodsRecursive(interfaces, result);
+        return result;
+    }
+
+    /**
+     * Fills {@code proxiedMethods} with the methods of {@code interfaces} and
+     * the interfaces they extend. May contain duplicates.
+     */
+    private static void getMethodsRecursive(Class<?>[] interfaces, List<Method> methods) {
+        for (Class<?> i : interfaces) {
+            getMethodsRecursive(i.getInterfaces(), methods);
+            Collections.addAll(methods, i.getDeclaredMethods());
+        }
+    }
+
+    /**
+     * Throws if any two methods in {@code methods} have the same name and
+     * parameters but incompatible return types.
+     *
+     * @param methods the methods to find exceptions for, ordered by name and
+     *     signature.
+     */
+    private static void validateReturnTypes(List<Method> methods) {
+        Method vs = null;
+        for (Method method : methods) {
+            if (vs == null || !vs.equalNameAndParameters(method)) {
+                vs = method; // this has a different name or parameters
+                continue;
+            }
+            Class<?> returnType = method.getReturnType();
+            Class<?> vsReturnType = vs.getReturnType();
+            if (returnType.isInterface() && vsReturnType.isInterface()) {
+                // all interfaces are mutually compatible
+            } else if (vsReturnType.isAssignableFrom(returnType)) {
+                vs = method; // the new return type is a subtype; use it instead
+            } else if (!returnType.isAssignableFrom(vsReturnType)) {
+                throw new IllegalArgumentException("proxied interface methods have incompatible "
+                        + "return types:\n  " + vs + "\n  " + method);
+            }
+        }
+    }
+
+    /**
+     * Remove methods that have the same name, parameters and return type. This
+     * computes the exceptions of each method; this is the intersection of the
+     * exceptions of equivalent methods.
+     *
+     * @param methods the methods to find exceptions for, ordered by name and
+     *     signature.
+     */
+    private static List<Class<?>[]> deduplicateAndGetExceptions(List<Method> methods) {
+        List<Class<?>[]> exceptions = new ArrayList<Class<?>[]>(methods.size());
+
+        for (int i = 0; i < methods.size(); ) {
+            Method method = methods.get(i);
+            Class<?>[] exceptionTypes = method.getExceptionTypes();
+
+            if (i > 0 && Method.ORDER_BY_SIGNATURE.compare(method, methods.get(i - 1)) == 0) {
+                exceptions.set(i - 1, intersectExceptions(exceptions.get(i - 1), exceptionTypes));
+                methods.remove(i);
+            } else {
+                exceptions.add(exceptionTypes);
+                i++;
+            }
+        }
+        return exceptions;
+    }
+
+    /**
+     * Returns the exceptions that are declared in both {@code aExceptions} and
+     * {@code bExceptions}. If an exception type in one array is a subtype of an
+     * exception from the other, the subtype is included in the intersection.
+     */
+    private static Class<?>[] intersectExceptions(Class<?>[] aExceptions, Class<?>[] bExceptions) {
+        if (aExceptions.length == 0 || bExceptions.length == 0) {
+            return EmptyArray.CLASS;
+        }
+        if (Arrays.equals(aExceptions, bExceptions)) {
+            return aExceptions;
+        }
+        Set<Class<?>> intersection = new HashSet<Class<?>>();
+        for (Class<?> a : aExceptions) {
+            for (Class<?> b : bExceptions) {
+                if (a.isAssignableFrom(b)) {
+                    intersection.add(b);
+                } else if (b.isAssignableFrom(a)) {
+                    intersection.add(a);
+                }
+            }
+        }
+        return intersection.toArray(new Class<?>[intersection.size()]);
+    }
+
+    private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
+                                                 ClassLoader loader, ArtMethod[] methods,
+                                                 Class<?>[][] exceptions);
+
+    /*
+     * The VM clones this method's descriptor when generating a proxy class.
+     * There is no implementation.
+     */
+    private static native void constructorPrototype(InvocationHandler h);
+
+    static Object invoke(Proxy proxy, ArtMethod method, Object[] args) throws Throwable {
+        InvocationHandler h = proxy.h;
+        return h.invoke(proxy, new Method(method), args);
+    }
+}
diff --git a/libart/src/main/java/sun/misc/Unsafe.java b/libart/src/main/java/sun/misc/Unsafe.java
new file mode 100644
index 0000000..0a4d8b2
--- /dev/null
+++ b/libart/src/main/java/sun/misc/Unsafe.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2007 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 sun.misc;
+
+import dalvik.system.VMStack;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * The package name notwithstanding, this class is the quasi-standard
+ * way for Java code to gain access to and use functionality which,
+ * when unsupervised, would allow one to break the pointer/type safety
+ * of Java.
+ */
+public final class Unsafe {
+    /** Traditional dalvik name. */
+    private static final Unsafe THE_ONE = new Unsafe();
+    /** Traditional RI name. */
+    private static final Unsafe theUnsafe = THE_ONE;
+
+    /**
+     * This class is only privately instantiable.
+     */
+    private Unsafe() {}
+
+    /**
+     * Gets the unique instance of this class. This is only allowed in
+     * very limited situations.
+     */
+    public static Unsafe getUnsafe() {
+        /*
+         * Only code on the bootclasspath is allowed to get at the
+         * Unsafe instance.
+         */
+        ClassLoader calling = VMStack.getCallingClassLoader();
+        if ((calling != null) && (calling != Unsafe.class.getClassLoader())) {
+            throw new SecurityException("Unsafe access denied");
+        }
+
+        return THE_ONE;
+    }
+
+    /**
+     * Gets the raw byte offset from the start of an object's memory to
+     * the memory used to store the indicated instance field.
+     *
+     * @param field non-null; the field in question, which must be an
+     * instance field
+     * @return the offset to the field
+     */
+    public long objectFieldOffset(Field field) {
+        if (Modifier.isStatic(field.getModifiers())) {
+            throw new IllegalArgumentException("valid for instance fields only");
+        }
+        return field.getOffset();
+    }
+
+    /**
+     * Gets the offset from the start of an array object's memory to
+     * the memory used to store its initial (zeroeth) element.
+     *
+     * @param clazz non-null; class in question; must be an array class
+     * @return the offset to the initial element
+     */
+    public int arrayBaseOffset(Class clazz) {
+        Class<?> component = clazz.getComponentType();
+        if (component == null) {
+            throw new IllegalArgumentException("Valid for array classes only: " + clazz);
+        }
+        // TODO: make the following not specific to the object model.
+        int offset = 12;
+        if (component == long.class || component == double.class) {
+            offset += 4;  // 4 bytes of padding.
+        }
+        return offset;
+    }
+
+    /**
+     * Gets the size of each element of the given array class.
+     *
+     * @param clazz non-null; class in question; must be an array class
+     * @return &gt; 0; the size of each element of the array
+     */
+    public int arrayIndexScale(Class clazz) {
+      Class<?> component = clazz.getComponentType();
+      if (component == null) {
+          throw new IllegalArgumentException("Valid for array classes only: " + clazz);
+      }
+      // TODO: make the following not specific to the object model.
+      if (!component.isPrimitive()) {
+          return 4;
+      } else  if (component == long.class || component == double.class) {
+          return 8;
+      } else if (component == int.class || component == float.class) {
+          return 4;
+      } else if (component == char.class || component == short.class) {
+          return 2;
+      } else {
+          // component == byte.class || component == boolean.class.
+          return 1;
+      }
+    }
+
+    /**
+     * Performs a compare-and-set operation on an <code>int</code>
+     * field within the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param expectedValue expected value of the field
+     * @param newValue new value to store in the field if the contents are
+     * as expected
+     * @return <code>true</code> if the new value was in fact stored, and
+     * <code>false</code> if not
+     */
+    public native boolean compareAndSwapInt(Object obj, long offset,
+            int expectedValue, int newValue);
+
+    /**
+     * Performs a compare-and-set operation on a <code>long</code>
+     * field within the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param expectedValue expected value of the field
+     * @param newValue new value to store in the field if the contents are
+     * as expected
+     * @return <code>true</code> if the new value was in fact stored, and
+     * <code>false</code> if not
+     */
+    public native boolean compareAndSwapLong(Object obj, long offset,
+            long expectedValue, long newValue);
+
+    /**
+     * Performs a compare-and-set operation on an <code>Object</code>
+     * field (that is, a reference field) within the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param expectedValue expected value of the field
+     * @param newValue new value to store in the field if the contents are
+     * as expected
+     * @return <code>true</code> if the new value was in fact stored, and
+     * <code>false</code> if not
+     */
+    public native boolean compareAndSwapObject(Object obj, long offset,
+            Object expectedValue, Object newValue);
+
+    /**
+     * Gets an <code>int</code> field from the given object,
+     * using <code>volatile</code> semantics.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @return the retrieved value
+     */
+    public native int getIntVolatile(Object obj, long offset);
+
+    /**
+     * Stores an <code>int</code> field into the given object,
+     * using <code>volatile</code> semantics.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param newValue the value to store
+     */
+    public native void putIntVolatile(Object obj, long offset, int newValue);
+
+    /**
+     * Gets a <code>long</code> field from the given object,
+     * using <code>volatile</code> semantics.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @return the retrieved value
+     */
+    public native long getLongVolatile(Object obj, long offset);
+
+    /**
+     * Stores a <code>long</code> field into the given object,
+     * using <code>volatile</code> semantics.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param newValue the value to store
+     */
+    public native void putLongVolatile(Object obj, long offset, long newValue);
+
+    /**
+     * Gets an <code>Object</code> field from the given object,
+     * using <code>volatile</code> semantics.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @return the retrieved value
+     */
+    public native Object getObjectVolatile(Object obj, long offset);
+
+    /**
+     * Stores an <code>Object</code> field into the given object,
+     * using <code>volatile</code> semantics.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param newValue the value to store
+     */
+    public native void putObjectVolatile(Object obj, long offset,
+            Object newValue);
+
+    /**
+     * Gets an <code>int</code> field from the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @return the retrieved value
+     */
+    public native int getInt(Object obj, long offset);
+
+    /**
+     * Stores an <code>int</code> field into the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param newValue the value to store
+     */
+    public native void putInt(Object obj, long offset, int newValue);
+
+    /**
+     * Lazy set an int field.
+     */
+    public native void putOrderedInt(Object obj, long offset, int newValue);
+
+    /**
+     * Gets a <code>long</code> field from the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @return the retrieved value
+     */
+    public native long getLong(Object obj, long offset);
+
+    /**
+     * Stores a <code>long</code> field into the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param newValue the value to store
+     */
+    public native void putLong(Object obj, long offset, long newValue);
+
+    /**
+     * Lazy set a long field.
+     */
+    public native void putOrderedLong(Object obj, long offset, long newValue);
+
+    /**
+     * Gets an <code>Object</code> field from the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @return the retrieved value
+     */
+    public native Object getObject(Object obj, long offset);
+
+    /**
+     * Stores an <code>Object</code> field into the given object.
+     *
+     * @param obj non-null; object containing the field
+     * @param offset offset to the field within <code>obj</code>
+     * @param newValue the value to store
+     */
+    public native void putObject(Object obj, long offset, Object newValue);
+
+    /**
+     * Lazy set an object field.
+     */
+    public native void putOrderedObject(Object obj, long offset,
+            Object newValue);
+
+    /**
+     * Parks the calling thread for the specified amount of time,
+     * unless the "permit" for the thread is already available (due to
+     * a previous call to {@link #unpark}. This method may also return
+     * spuriously (that is, without the thread being told to unpark
+     * and without the indicated amount of time elapsing).
+     *
+     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+     * in-depth information of the behavior of this method.</p>
+     *
+     * @param absolute whether the given time value is absolute
+     * milliseconds-since-the-epoch (<code>true</code>) or relative
+     * nanoseconds-from-now (<code>false</code>)
+     * @param time the (absolute millis or relative nanos) time value
+     */
+    public void park(boolean absolute, long time) {
+        if (absolute) {
+            Thread.currentThread().parkUntil(time);
+        } else {
+            Thread.currentThread().parkFor(time);
+        }
+    }
+
+    /**
+     * Unparks the given object, which must be a {@link Thread}.
+     *
+     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+     * in-depth information of the behavior of this method.</p>
+     *
+     * @param obj non-null; the object to unpark
+     */
+    public void unpark(Object obj) {
+        if (obj instanceof Thread) {
+            ((Thread) obj).unpark();
+        } else {
+            throw new IllegalArgumentException("valid for Threads only");
+        }
+    }
+
+    /**
+     * Allocates an instance of the given class without running the constructor.
+     * The class' <clinit> will be run, if necessary.
+     */
+    public native Object allocateInstance(Class<?> c);
+}
diff --git a/libdvm/src/main/java/dalvik/system/VMRuntime.java b/libdvm/src/main/java/dalvik/system/VMRuntime.java
index 71098be..e1b4a00 100644
--- a/libdvm/src/main/java/dalvik/system/VMRuntime.java
+++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java
@@ -68,6 +68,11 @@
     public native String vmVersion();
 
     /**
+     * Returns the name of the shared library providing the VM implementation.
+     */
+    public native String vmLibrary();
+
+    /**
      * Gets the current ideal heap utilization, represented as a number
      * between zero and one.  After a GC happens, the Dalvik heap may
      * be resized so that (size of live objects) / (size of heap) is
@@ -117,7 +122,7 @@
 
     /**
      * This method exists for binary compatibility.  It was part of a
-     * heap sizing API which was removed in Honeycomb.
+     * heap sizing API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public long getMinimumHeapSize() {
@@ -126,7 +131,7 @@
 
     /**
      * This method exists for binary compatibility.  It was part of a
-     * heap sizing API which was removed in Honeycomb.
+     * heap sizing API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public long setMinimumHeapSize(long size) {
@@ -159,7 +164,7 @@
 
     /**
      * This method exists for binary compatibility.  It was part of
-     * the external allocation API which was removed in Honeycomb.
+     * the external allocation API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public boolean trackExternalAllocation(long size) {
@@ -168,14 +173,14 @@
 
     /**
      * This method exists for binary compatibility.  It was part of
-     * the external allocation API which was removed in Honeycomb.
+     * the external allocation API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public void trackExternalFree(long size) {}
 
     /**
      * This method exists for binary compatibility.  It was part of
-     * the external allocation API which was removed in Honeycomb.
+     * the external allocation API which was removed in Android 3.0 (Honeycomb).
      */
     @Deprecated
     public long getExternalBytesAllocated() {
@@ -217,4 +222,24 @@
      * Returns true if either a Java debugger or native debugger is active.
      */
     public native boolean isDebuggerActive();
+
+    /**
+     * Registers a native allocation so that the heap knows about it and performs GC as required.
+     * If the number of native allocated bytes exceeds the native allocation watermark, the
+     * function requests a concurrent GC. If the native bytes allocated exceeds a second higher
+     * watermark, it is determined that the application is registering native allocations at an
+     * unusually high rate and a GC is performed inside of the function to prevent memory usage
+     * from excessively increasing.
+     */
+    public native void registerNativeAllocation(int bytes);
+
+    /**
+     * Registers a native free by reducing the number of native bytes accounted for.
+     */
+    public native void registerNativeFree(int bytes);
+
+    /**
+     * Fill in dex caches with classes, fields, and methods that are already loaded.
+     */
+    public native void preloadDexCaches();
 }
diff --git a/libdvm/src/main/java/java/lang/Class.java b/libdvm/src/main/java/java/lang/Class.java
index 2f26688..4b36f3c 100644
--- a/libdvm/src/main/java/java/lang/Class.java
+++ b/libdvm/src/main/java/java/lang/Class.java
@@ -54,12 +54,14 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
-import libcore.util.CollectionUtils;
-import libcore.util.EmptyArray;
 import org.apache.harmony.kernel.vm.StringUtils;
 import libcore.reflect.AnnotationAccess;
 import libcore.reflect.GenericSignatureParser;
+import libcore.reflect.InternalNames;
 import libcore.reflect.Types;
+import libcore.util.BasicLruCache;
+import libcore.util.CollectionUtils;
+import libcore.util.EmptyArray;
 
 /**
  * The in-memory representation of a Java class. This representation serves as
@@ -121,12 +123,24 @@
     private static final long serialVersionUID = 3206093459760846163L;
 
     /**
+     * Class def index from dex file. An index of -1 indicates that there is no class definition,
+     * for example for an array type.
+     */
+    private transient int dexClassDefIndex;
+
+    /** The type index of this class within the dex file that defines it. */
+    private transient int dexTypeIndex;
+
+    /**
+     * Have we computed the type and class def indices? Volatile to avoid double check locking bugs.
+     */
+    private transient volatile boolean dexIndicesInitialized;
+
+    /**
      * Lazily computed name of this class; always prefer calling getName().
      */
     private transient String name;
 
-    private transient int dexTypeIndex;
-
     private Class() {
         // Prevent this class to be instantiated, instance
         // should be created by JVM only
@@ -138,44 +152,48 @@
      */
     public native Dex getDex();
 
+    /** Lazily compute indices in to Dex */
+    private synchronized void computeDexIndices() {
+        if (!dexIndicesInitialized) {
+            Dex dex = getDex();
+            dexTypeIndex = dex.findTypeIndex(InternalNames.getInternalName(this));
+            if (dexTypeIndex < 0) {
+                dexTypeIndex = -1;
+                dexClassDefIndex = -1;
+            } else {
+                dexClassDefIndex = dex.findClassDefIndexFromTypeIndex(dexTypeIndex);
+            }
+            dexIndicesInitialized = true;
+        }
+    }
+
     /**
-     * The type index of this class in its own Dex, or 0 if it is unknown. If a
-     * class is referenced by multiple Dex files, it will have a different type
-     * index in each. Dex files support 65534 type indices, with 65535
-     * representing no index.
-     *
-     * TODO: 0 is a valid index; this should be -1 if it is unknown
+     * The class def of this class in its own Dex, or -1 if there is no class def.
      *
      * @hide
      */
-    public int getTypeIndex() {
-        int result = dexTypeIndex;
-        if (result == 0) {  // uncomputed => Dalvik
-            result = AnnotationAccess.computeTypeIndex(getDex(), this);
-            dexTypeIndex = result;
+    public int getDexClassDefIndex() {
+        if (!dexIndicesInitialized) {
+            computeDexIndices();
         }
-        return result;
+        return dexClassDefIndex;
     }
 
     /**
-     * Get the Signature attribute for this class.  Returns null if not found.
+     * The type index of this class in its own Dex, or -1 if it is unknown. If a class is referenced
+     * by multiple Dex files, it will have a different type index in each. Dex files support 65534
+     * type indices, with 65535 representing no index.
+     *
+     * @hide
      */
-    private String getSignatureAttribute() {
-        Object[] annotation = getSignatureAnnotation();
-
-        if (annotation == null) {
-            return null;
+    public int getDexTypeIndex() {
+        if (!dexIndicesInitialized) {
+            computeDexIndices();
         }
-
-        return StringUtils.combineStrings(annotation);
+        return dexTypeIndex;
     }
 
     /**
-     * Get the Signature annotation for this class.  Returns null if not found.
-     */
-    native private Object[] getSignatureAnnotation();
-
-    /**
      * Returns a {@code Class} object which represents the class with the
      * given name. The name should be the name of a non-primitive class, as described in
      * the {@link Class class definition}.
@@ -779,9 +797,22 @@
      * void} then an empty array is returned.
      */
     public Type[] getGenericInterfaces() {
-        GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
-        parser.parseForClass(this, getSignatureAttribute());
-        return Types.getClonedTypeArray(parser.interfaceTypes);
+        Type[] result;
+        synchronized (Caches.genericInterfaces) {
+            result = Caches.genericInterfaces.get(this);
+            if (result == null) {
+                String annotationSignature = AnnotationAccess.getSignature(this);
+                if (annotationSignature == null) {
+                    result = getInterfaces();
+                } else {
+                    GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+                    parser.parseForClass(this, annotationSignature);
+                    result = Types.getTypeArray(parser.interfaceTypes, false);
+                }
+                Caches.genericInterfaces.put(this, result);
+            }
+        }
+        return (result.length == 0) ? result : result.clone();
     }
 
     /**
@@ -789,9 +820,14 @@
      * class}.
      */
     public Type getGenericSuperclass() {
-        GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
-        parser.parseForClass(this, getSignatureAttribute());
-        return Types.getType(parser.superclassType);
+        Type genericSuperclass = getSuperclass();
+        String annotationSignature = AnnotationAccess.getSignature(this);
+        if (annotationSignature != null) {
+            GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+            parser.parseForClass(this, annotationSignature);
+            genericSuperclass = parser.superclassType;
+        }
+        return Types.getType(genericSuperclass);
     }
 
     /**
@@ -1031,9 +1067,13 @@
      */
     @SuppressWarnings("unchecked")
     public synchronized TypeVariable<Class<T>>[] getTypeParameters() {
+        String annotationSignature = AnnotationAccess.getSignature(this);
+        if (annotationSignature == null) {
+            return EmptyArray.TYPE_VARIABLE;
+        }
         GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
-        parser.parseForClass(this, getSignatureAttribute());
-        return parser.formalTypeParameters.clone();
+        parser.parseForClass(this, annotationSignature);
+        return parser.formalTypeParameters;
     }
 
     /**
@@ -1095,7 +1135,11 @@
      * {@code enum}.
      */
     public boolean isEnum() {
-        return ((getModifiers() & 0x4000) != 0) && (getSuperclass() == Enum.class);
+        if (getSuperclass() != Enum.class) {
+            return false;
+        }
+        int mod = getModifiers(this, true);
+        return (mod & 0x4000) != 0;
     }
 
     /**
@@ -1170,9 +1214,8 @@
     public String toString() {
         if (isPrimitive()) {
             return getSimpleName();
-        } else {
-            return (isInterface() ? "interface " : "class ") + getName();
         }
+        return (isInterface() ? "interface " : "class ") + getName();
     }
 
     /**
@@ -1258,8 +1301,48 @@
      *
      * @hide
      */
-    public int getAnnotationDirectoryOffset() {
-        return AnnotationAccess.typeIndexToAnnotationDirectoryOffset(getDex(), getTypeIndex());
+    public int getDexAnnotationDirectoryOffset() {
+        Dex dex = getDex();
+        if (dex == null) {
+            return 0;
+        }
+        int classDefIndex = getDexClassDefIndex();
+        if (classDefIndex < 0) {
+            return 0;
+        }
+        return dex.annotationDirectoryOffsetFromClassDefIndex(classDefIndex);
     }
 
+
+    /**
+     * Returns a resolved type from the dex cache, computing the type from the dex file if
+     * necessary.
+     * TODO: use Dalvik's dex cache.
+     * @hide
+     */
+    public Class<?> getDexCacheType(Dex dex, int typeIndex) {
+        String internalName = dex.typeNames().get(typeIndex);
+        return InternalNames.getClass(getClassLoader(), internalName);
+    }
+
+    /**
+     * Returns a string from the dex cache, computing the string from the dex file if necessary.
+     *
+     * @hide
+     */
+    public String getDexCacheString(Dex dex, int dexStringIndex) {
+        return dex.strings().get(dexStringIndex);
+    }
+
+
+    private static class Caches {
+        /**
+         * Cache to avoid frequent recalculation of generic interfaces, which is generally uncommon.
+         * Sized sufficient to allow ConcurrentHashMapTest to run without recalculating its generic
+         * interfaces (required to avoid time outs). Validated by running reflection heavy code
+         * such as applications using Guice-like frameworks.
+         */
+        private static final BasicLruCache<Class, Type[]> genericInterfaces
+            = new BasicLruCache<Class, Type[]>(8);
+    }
 }
diff --git a/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java b/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java
index 9c6b8c7..f3d96af 100644
--- a/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java
+++ b/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -54,7 +54,9 @@
  */
 public class AccessibleObject implements AnnotatedElement {
 
-    // If true, object is accessible, bypassing normal access checks
+    /**
+     * If true, object is accessible, bypassing normal access checks
+     */
     boolean flag = false;
 
     // Holds a mapping from Java type names to native type codes.
@@ -87,10 +89,8 @@
      * @see #setAccessible(boolean)
      */
     public static void setAccessible(AccessibleObject[] objects, boolean flag) {
-        synchronized(AccessibleObject.class) {
-            for (AccessibleObject object : objects) {
-                object.flag = flag;
-            }
+        for (AccessibleObject object : objects) {
+            object.flag = flag;
         }
     }
 
diff --git a/libdvm/src/main/java/java/lang/reflect/Constructor.java b/libdvm/src/main/java/java/lang/reflect/Constructor.java
index 531f269..ab417d2 100644
--- a/libdvm/src/main/java/java/lang/reflect/Constructor.java
+++ b/libdvm/src/main/java/java/lang/reflect/Constructor.java
@@ -153,12 +153,10 @@
         appendTypeName(sb, getDeclaringClass());
         // append parameters
         sb.append('(');
-        appendArrayGenericType(sb,
-                Types.getClonedTypeArray(genericParameterTypes));
+        appendArrayGenericType(sb, Types.getTypeArray(genericParameterTypes, false));
         sb.append(')');
         // append exceptions if any
-        Type[] genericExceptionTypeArray =
-                Types.getClonedTypeArray(genericExceptionTypes);
+        Type[] genericExceptionTypeArray = Types.getTypeArray(genericExceptionTypes, false);
         if (genericExceptionTypeArray.length > 0) {
             sb.append(" throws ");
             appendArrayGenericType(sb, genericExceptionTypeArray);
@@ -183,7 +181,7 @@
      */
     public Type[] getGenericParameterTypes() {
         initGenericTypes();
-        return Types.getClonedTypeArray(genericParameterTypes);
+        return Types.getTypeArray(genericParameterTypes, true);
     }
 
     /**
@@ -203,7 +201,7 @@
      */
     public Type[] getGenericExceptionTypes() {
         initGenericTypes();
-        return Types.getClonedTypeArray(genericExceptionTypes);
+        return Types.getTypeArray(genericExceptionTypes, true);
     }
 
     @Override
diff --git a/libdvm/src/main/java/java/lang/reflect/Method.java b/libdvm/src/main/java/java/lang/reflect/Method.java
index 028b9ca..f8efbf4 100644
--- a/libdvm/src/main/java/java/lang/reflect/Method.java
+++ b/libdvm/src/main/java/java/lang/reflect/Method.java
@@ -185,12 +185,10 @@
         sb.append(".").append(getName());
         // append parameters
         sb.append('(');
-        appendArrayGenericType(sb,
-                Types.getClonedTypeArray(genericParameterTypes));
+        appendArrayGenericType(sb, Types.getTypeArray(genericParameterTypes, false));
         sb.append(')');
         // append exceptions if any
-        Type[] genericExceptionTypeArray = Types.getClonedTypeArray(
-                genericExceptionTypes);
+        Type[] genericExceptionTypeArray = Types.getTypeArray(genericExceptionTypes, false);
         if (genericExceptionTypeArray.length > 0) {
             sb.append(" throws ");
             appendArrayGenericType(sb, genericExceptionTypeArray);
@@ -215,7 +213,7 @@
      */
     public Type[] getGenericParameterTypes() {
         initGenericTypes();
-        return Types.getClonedTypeArray(genericParameterTypes);
+        return Types.getTypeArray(genericParameterTypes, true);
     }
 
     /**
@@ -234,7 +232,7 @@
      */
     public Type[] getGenericExceptionTypes() {
         initGenericTypes();
-        return Types.getClonedTypeArray(genericExceptionTypes);
+        return Types.getTypeArray(genericExceptionTypes, true);
     }
 
     /**
diff --git a/luni/src/main/files/cacerts/3c9a4d3b.0 b/luni/src/main/files/cacerts/3c9a4d3b.0
new file mode 100644
index 0000000..c6e312c
--- /dev/null
+++ b/luni/src/main/files/cacerts/3c9a4d3b.0
@@ -0,0 +1,152 @@
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
+AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
+CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
+BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
+VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
+qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
+HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
+G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
+lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
+IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
+0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
+k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
+4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
+m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
+cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
+uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
+KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
+ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
+AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
+VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
+CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
+cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
+QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
+7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
+cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
+QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
+czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
+aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
+aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
+DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
+BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
+D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
+JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
+AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
+vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
+tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
+7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
+h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
+d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
+pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 6828503384748696800 (0x5ec3b7a6437fa4e0)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES
+        Validity
+            Not Before: May  5 09:37:37 2011 GMT
+            Not After : Dec 31 09:37:37 2030 GMT
+        Subject: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:9b:a9:ab:bf:61:4a:97:af:2f:97:66:9a:74:5f:
+                    d0:d9:96:fd:cf:e2:e4:66:ef:1f:1f:47:33:c2:44:
+                    a3:df:9a:de:1f:b5:54:dd:15:7c:69:35:11:6f:bb:
+                    c8:0c:8e:6a:18:1e:d8:8f:d9:16:bc:10:48:36:5c:
+                    f0:63:b3:90:5a:5c:24:37:d7:a3:d6:cb:09:71:b9:
+                    f1:01:72:84:b0:7d:db:4d:80:cd:fc:d3:6f:c9:f8:
+                    da:b6:0e:82:d2:45:85:a8:1b:68:a8:3d:e8:f4:44:
+                    6c:bd:a1:c2:cb:03:be:8c:3e:13:00:84:df:4a:48:
+                    c0:e3:22:0a:e8:e9:37:a7:18:4c:b1:09:0d:23:56:
+                    7f:04:4d:d9:17:84:18:a5:c8:da:40:94:73:eb:ce:
+                    0e:57:3c:03:81:3a:9d:0a:a1:57:43:69:ac:57:6d:
+                    79:90:78:e5:b5:b4:3b:d8:bc:4c:8d:28:a1:a7:a3:
+                    a7:ba:02:4e:25:d1:2a:ae:ed:ae:03:22:b8:6b:20:
+                    0f:30:28:54:95:7f:e0:ee:ce:0a:66:9d:d1:40:2d:
+                    6e:22:af:9d:1a:c1:05:19:d2:6f:c0:f2:9f:f8:7b:
+                    b3:02:42:fb:50:a9:1d:2d:93:0f:23:ab:c6:c1:0f:
+                    92:ff:d0:a2:15:f5:53:09:71:1c:ff:45:13:84:e6:
+                    26:5e:f8:e0:88:1c:0a:fc:16:b6:a8:73:06:b8:f0:
+                    63:84:02:a0:c6:5a:ec:e7:74:df:70:ae:a3:83:25:
+                    ea:d6:c7:97:87:93:a7:c6:8a:8a:33:97:60:37:10:
+                    3e:97:3e:6e:29:15:d6:a1:0f:d1:88:2c:12:9f:6f:
+                    aa:a4:c6:42:eb:41:a2:e3:95:43:d3:01:85:6d:8e:
+                    bb:3b:f3:23:36:c7:fe:3b:e0:a1:25:07:48:ab:c9:
+                    89:74:ff:08:8f:80:bf:c0:96:65:f3:ee:ec:4b:68:
+                    bd:9d:88:c3:31:b3:40:f1:e8:cf:f6:38:bb:9c:e4:
+                    d1:7f:d4:e5:58:9b:7c:fa:d4:f3:0e:9b:75:91:e4:
+                    ba:52:2e:19:7e:d1:f5:cd:5a:19:fc:ba:06:f6:fb:
+                    52:a8:4b:99:04:dd:f8:f9:b4:8b:50:a3:4e:62:89:
+                    f0:87:24:fa:83:42:c1:87:fa:d5:2d:29:2a:5a:71:
+                    7a:64:6a:d7:27:60:63:0d:db:ce:49:f5:8d:1f:90:
+                    89:32:17:f8:73:43:b8:d2:5a:93:86:61:d6:e1:75:
+                    0a:ea:79:66:76:88:4f:71:eb:04:25:d6:0a:5a:7a:
+                    93:e5:b9:4b:17:40:0f:b1:b6:b9:f5:de:4f:dc:e0:
+                    b3:ac:3b:11:70:60:84:4a:43:6e:99:20:c0:29:71:
+                    0a:c0:65
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            Authority Information Access: 
+                CA Issuers - URI:http://www.accv.es/fileadmin/Archivos/certificados/raizaccv1.crt
+                OCSP - URI:http://ocsp.accv.es
+
+            X509v3 Subject Key Identifier: 
+                D2:87:B4:E3:DF:37:27:93:55:F6:56:EA:81:E5:36:CC:8C:1E:3F:BD
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Authority Key Identifier: 
+                keyid:D2:87:B4:E3:DF:37:27:93:55:F6:56:EA:81:E5:36:CC:8C:1E:3F:BD
+
+            X509v3 Certificate Policies: 
+                Policy: X509v3 Any Policy
+                  User Notice:
+                    Explicit Text: 
+                  CPS: http://www.accv.es/legislacion_c.htm
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://www.accv.es/fileadmin/Archivos/certificados/raizaccv1_der.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Subject Alternative Name: 
+                email:accv@accv.es
+    Signature Algorithm: sha1WithRSAEncryption
+         97:31:02:9f:e7:fd:43:67:48:44:14:e4:29:87:ed:4c:28:66:
+         d0:8f:35:da:4d:61:b7:4a:97:4d:b5:db:90:e0:05:2e:0e:c6:
+         79:d0:f2:97:69:0f:bd:04:47:d9:be:db:b5:29:da:9b:d9:ae:
+         a9:99:d5:d3:3c:30:93:f5:8d:a1:a8:fc:06:8d:44:f4:ca:16:
+         95:7c:33:dc:62:8b:a8:37:f8:27:d8:09:2d:1b:ef:c8:14:27:
+         20:a9:64:44:ff:2e:d6:75:aa:6c:4d:60:40:19:49:43:54:63:
+         da:e2:cc:ba:66:e5:4f:44:7a:5b:d9:6a:81:2b:40:d5:7f:f9:
+         01:27:58:2c:c8:ed:48:91:7c:3f:a6:00:cf:c4:29:73:11:36:
+         de:86:19:3e:9d:ee:19:8a:1b:d5:b0:ed:8e:3d:9c:2a:c0:0d:
+         d8:3d:66:e3:3c:0d:bd:d5:94:5c:e2:e2:a7:35:1b:04:00:f6:
+         3f:5a:8d:ea:43:bd:5f:89:1d:a9:c1:b0:cc:99:e2:4d:00:0a:
+         da:c9:27:5b:e7:13:90:5c:e4:f5:33:a2:55:6d:dc:e0:09:4d:
+         2f:b1:26:5b:27:75:00:09:c4:62:77:29:08:5f:9e:59:ac:b6:
+         7e:ad:9f:54:30:22:03:c1:1e:71:64:fe:f9:38:0a:96:18:dd:
+         02:14:ac:23:cb:06:1c:1e:a4:7d:8d:0d:de:27:41:e8:ad:da:
+         15:b7:b0:23:dd:2b:a8:d3:da:25:87:ed:e8:55:44:4d:88:f4:
+         36:7e:84:9a:78:ac:f7:0e:56:49:0e:d6:33:25:d6:84:50:42:
+         6c:20:12:1d:2a:d5:be:bc:f2:70:81:a4:70:60:be:05:b5:9b:
+         9e:04:44:be:61:23:ac:e9:a5:24:8c:11:80:94:5a:a2:a2:b9:
+         49:d2:c1:dc:d1:a7:ed:31:11:2c:9e:19:a6:ee:e1:55:e1:c0:
+         ea:cf:0d:84:e4:17:b7:a2:7c:a5:de:55:25:06:ee:cc:c0:87:
+         5c:40:da:cc:95:3f:55:e0:35:c7:b8:84:be:b4:5d:cd:7a:83:
+         01:72:ee:87:e6:5f:1d:ae:b5:85:c6:26:df:e6:c1:9a:e9:1e:
+         02:47:9f:2a:a8:6d:a9:5b:cf:ec:45:77:7f:98:27:9a:32:5d:
+         2a:e3:84:ee:c5:98:66:2f:96:20:1d:dd:d8:c3:27:d7:b0:f9:
+         fe:d9:7d:cd:d0:9f:8f:0b:14:58:51:9f:2f:8b:c3:38:2d:de:
+         e8:8f:d6:8d:87:a4:f5:56:43:16:99:2c:f4:a4:56:b4:34:b8:
+         61:37:c9:c2:58:80:1b:a0:97:a1:fc:59:8d:e9:11:f6:d1:0f:
+         4b:55:34:46:2a:8b:86:3b
+SHA1 Fingerprint=93:05:7A:88:15:C6:4F:CE:88:2F:FA:91:16:52:28:78:BC:53:64:17
diff --git a/luni/src/main/files/cacerts/40dc992e.0 b/luni/src/main/files/cacerts/40dc992e.0
new file mode 100644
index 0000000..847cec6
--- /dev/null
+++ b/luni/src/main/files/cacerts/40dc992e.0
@@ -0,0 +1,93 @@
+-----BEGIN CERTIFICATE-----
+MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
+RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
+dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
+YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
+NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
+EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
+cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
+c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
+dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
+fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
+bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
+75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
+FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
+HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
+5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
+b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
+A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
+6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
+TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
+dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
+Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
+l7WdmplNsDz4SgCbZN2fOUvRJ9e4
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 0 (0x0)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=GR, O=Hellenic Academic and Research Institutions Cert. Authority, CN=Hellenic Academic and Research Institutions RootCA 2011
+        Validity
+            Not Before: Dec  6 13:49:52 2011 GMT
+            Not After : Dec  1 13:49:52 2031 GMT
+        Subject: C=GR, O=Hellenic Academic and Research Institutions Cert. Authority, CN=Hellenic Academic and Research Institutions RootCA 2011
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a9:53:00:e3:2e:a6:f6:8e:fa:60:d8:2d:95:3e:
+                    f8:2c:2a:54:4e:cd:b9:84:61:94:58:4f:8f:3d:8b:
+                    e4:43:f3:75:89:8d:51:e4:c3:37:d2:8a:88:4d:79:
+                    1e:b7:12:dd:43:78:4a:8a:92:e6:d7:48:d5:0f:a4:
+                    3a:29:44:35:b8:07:f6:68:1d:55:cd:38:51:f0:8c:
+                    24:31:85:af:83:c9:7d:e9:77:af:ed:1a:7b:9d:17:
+                    f9:b3:9d:38:50:0f:a6:5a:79:91:80:af:37:ae:a6:
+                    d3:31:fb:b5:26:09:9d:3c:5a:ef:51:c5:2b:df:96:
+                    5d:eb:32:1e:02:da:70:49:ec:6e:0c:c8:9a:37:8d:
+                    f7:f1:36:60:4b:26:2c:82:9e:d0:78:f3:0d:0f:63:
+                    a4:51:30:e1:f9:2b:27:12:07:d8:ea:bd:18:62:98:
+                    b0:59:37:7d:be:ee:f3:20:51:42:5a:83:ef:93:ba:
+                    69:15:f1:62:9d:9f:99:39:82:a1:b7:74:2e:8b:d4:
+                    c5:0b:7b:2f:f0:c8:0a:da:3d:79:0a:9a:93:1c:a5:
+                    28:72:73:91:43:9a:a7:d1:4d:85:84:b9:a9:74:8f:
+                    14:40:c7:dc:de:ac:41:64:6c:b4:19:9b:02:63:6d:
+                    24:64:8f:44:b2:25:ea:ce:5d:74:0c:63:32:5c:8d:
+                    87:e5
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Key Usage: 
+                Certificate Sign, CRL Sign
+            X509v3 Subject Key Identifier: 
+                A6:91:42:FD:13:61:4A:23:9E:08:A4:29:E5:D8:13:04:23:EE:41:25
+            X509v3 Name Constraints: 
+                Permitted:
+                  DNS:.gr
+                  DNS:.eu
+                  DNS:.edu
+                  DNS:.org
+                  email:.gr
+                  email:.eu
+                  email:.edu
+                  email:.org
+
+    Signature Algorithm: sha1WithRSAEncryption
+         1f:ef:79:41:e1:7b:6e:3f:b2:8c:86:37:42:4a:4e:1c:37:1e:
+         8d:66:ba:24:81:c9:4f:12:0f:21:c0:03:97:86:25:6d:5d:d3:
+         22:29:a8:6c:a2:0d:a9:eb:3d:06:5b:99:3a:c7:cc:c3:9a:34:
+         7f:ab:0e:c8:4e:1c:e1:fa:e4:dc:cd:0d:be:bf:24:fe:6c:e7:
+         6b:c2:0d:c8:06:9e:4e:8d:61:28:a6:6a:fd:e5:f6:62:ea:18:
+         3c:4e:a0:53:9d:b2:3a:9c:eb:a5:9c:91:16:b6:4d:82:e0:0c:
+         05:48:a9:6c:f5:cc:f8:cb:9d:49:b4:f0:02:a5:fd:70:03:ed:
+         8a:21:a5:ae:13:86:49:c3:33:73:be:87:3b:74:8b:17:45:26:
+         4c:16:91:83:fe:67:7d:cd:4d:63:67:fa:f3:03:12:96:78:06:
+         8d:b1:67:ed:8e:3f:be:9f:4f:02:f5:b3:09:2f:f3:4c:87:df:
+         2a:cb:95:7c:01:cc:ac:36:7a:bf:a2:73:7a:f7:8f:c1:b5:9a:
+         a1:14:b2:8f:33:9f:0d:ef:22:dc:66:7b:84:bd:45:17:06:3d:
+         3c:ca:b9:77:34:8f:ca:ea:cf:3f:31:3e:e3:88:e3:80:49:25:
+         c8:97:b5:9d:9a:99:4d:b0:3c:f8:4a:00:9b:64:dd:9f:39:4b:
+         d1:27:d7:b8
+SHA1 Fingerprint=FE:45:65:9B:79:03:5B:98:A1:61:B5:51:2E:AC:DA:58:09:48:22:4D
diff --git a/luni/src/main/files/cacerts/b3fb433b.0 b/luni/src/main/files/cacerts/b3fb433b.0
new file mode 100644
index 0000000..de880c1
--- /dev/null
+++ b/luni/src/main/files/cacerts/b3fb433b.0
@@ -0,0 +1,56 @@
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
+A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
+d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
+dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq
+RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy
+MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD
+VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
+L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g
+Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi
+A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt
+ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH
+Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
+BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC
+R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX
+hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            a6:8b:79:29:00:00:00:00:50:d0:91:f9
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - EC1
+        Validity
+            Not Before: Dec 18 15:25:36 2012 GMT
+            Not After : Dec 18 15:55:36 2037 GMT
+        Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - EC1
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (384 bit)
+                pub: 
+                    04:84:13:c9:d0:ba:6d:41:7b:e2:6c:d0:eb:55:5f:
+                    66:02:1a:24:f4:5b:89:69:47:e3:b8:c2:7d:f1:f2:
+                    02:c5:9f:a0:f6:5b:d5:8b:06:19:86:4f:53:10:6d:
+                    07:24:27:a1:a0:f8:d5:47:19:61:4c:7d:ca:93:27:
+                    ea:74:0c:ef:6f:96:09:fe:63:ec:70:5d:36:ad:67:
+                    77:ae:c9:9d:7c:55:44:3a:a2:63:51:1f:f5:e3:62:
+                    d4:a9:47:07:3e:cc:20
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                B7:63:E7:1A:DD:8D:E9:08:A6:55:83:A4:E0:6A:50:41:65:11:42:49
+    Signature Algorithm: ecdsa-with-SHA384
+         30:64:02:30:61:79:d8:e5:42:47:df:1c:ae:53:99:17:b6:6f:
+         1c:7d:e1:bf:11:94:d1:03:88:75:e4:8d:89:a4:8a:77:46:de:
+         6d:61:ef:02:f5:fb:b5:df:cc:fe:4e:ff:fe:a9:e6:a7:02:30:
+         5b:99:d7:85:37:06:b5:7b:08:fd:eb:27:8b:4a:94:f9:e1:fa:
+         a7:8e:26:08:e8:7c:92:68:6d:73:d8:6f:26:ac:21:02:b8:99:
+         b7:26:41:5b:25:60:ae:d0:48:1a:ee:06
+SHA1 Fingerprint=20:D8:06:40:DF:9B:25:F5:12:25:3A:11:EA:F7:59:8A:EB:14:B5:47
diff --git a/luni/src/main/files/cacerts/ee90b008.0 b/luni/src/main/files/cacerts/ee90b008.0
new file mode 100644
index 0000000..f017edc
--- /dev/null
+++ b/luni/src/main/files/cacerts/ee90b008.0
@@ -0,0 +1,119 @@
+-----BEGIN CERTIFICATE-----
+MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1
+OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG
+A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ
+JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD
+vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo
+D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/
+Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW
+RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK
+HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN
+nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM
+0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i
+UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9
+Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg
+TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL
+BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
+2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX
+UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl
+6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK
+9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ
+HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI
+wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY
+XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l
+IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo
+hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr
+so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 59 (0x3b)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=IL, O=StartCom Ltd., CN=StartCom Certification Authority G2
+        Validity
+            Not Before: Jan  1 01:00:01 2010 GMT
+            Not After : Dec 31 23:59:01 2039 GMT
+        Subject: C=IL, O=StartCom Ltd., CN=StartCom Certification Authority G2
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:b6:89:36:5b:07:b7:20:36:bd:82:bb:e1:16:20:
+                    03:95:7a:af:0e:a3:55:c9:25:99:4a:c5:d0:56:41:
+                    87:90:4d:21:60:a4:14:87:3b:cd:fd:b2:3e:b4:67:
+                    03:6a:ed:e1:0f:4b:c0:91:85:70:45:e0:42:9e:de:
+                    29:23:d4:01:0d:a0:10:79:b8:db:03:bd:f3:a9:2f:
+                    d1:c6:e0:0f:cb:9e:8a:14:0a:b8:bd:f6:56:62:f1:
+                    c5:72:b6:32:25:d9:b2:f3:bd:65:c5:0d:2c:6e:d5:
+                    92:6f:18:8b:00:41:14:82:6f:40:20:26:7a:28:0f:
+                    f5:1e:7f:27:f7:94:b1:37:3d:b7:c7:91:f7:e2:01:
+                    ec:fd:94:89:e1:cc:6e:d3:36:d6:0a:19:79:ae:d7:
+                    34:82:65:ff:7c:42:bb:b6:dd:0b:a6:34:af:4b:60:
+                    fe:7f:43:49:06:8b:8c:43:b8:56:f2:d9:7f:21:43:
+                    17:ea:a7:48:95:01:75:75:ea:2b:a5:43:95:ea:15:
+                    84:9d:08:8d:26:6e:55:9b:ab:dc:d2:39:d2:31:1d:
+                    60:e2:ac:cc:56:45:24:f5:1c:54:ab:ee:86:dd:96:
+                    32:85:f8:4c:4f:e8:95:76:b6:05:dd:36:23:67:bc:
+                    ff:15:e2:ca:3b:e6:a6:ec:3b:ec:26:11:34:48:8d:
+                    f6:80:2b:1a:23:02:eb:8a:1c:3a:76:2a:7b:56:16:
+                    1c:72:2a:b3:aa:e3:60:a5:00:9f:04:9b:e2:6f:1e:
+                    14:58:5b:a5:6c:8b:58:3c:c3:ba:4e:3a:5c:f7:e1:
+                    96:2b:3e:ef:07:bc:a4:e5:5d:cc:4d:9f:0d:e1:dc:
+                    aa:bb:e1:6e:1a:ec:8f:e1:b6:4c:4d:79:72:5d:17:
+                    35:0b:1d:d7:c1:47:da:96:24:e0:d0:72:a8:5a:5f:
+                    66:2d:10:dc:2f:2a:13:ae:26:fe:0a:1c:19:cc:d0:
+                    3e:0b:9c:c8:09:2e:f9:5b:96:7a:47:9c:e9:7a:f3:
+                    05:50:74:95:73:9e:30:09:f3:97:82:5e:e6:8f:39:
+                    08:1e:59:e5:35:14:42:13:ff:00:9c:f7:be:aa:50:
+                    cf:e2:51:48:d7:b8:6f:af:f8:4e:7e:33:98:92:14:
+                    62:3a:75:63:cf:7b:fa:de:82:3b:a9:bb:39:e2:c4:
+                    bd:2c:00:0e:c8:17:ac:13:ef:4d:25:8e:d8:b3:90:
+                    2f:a9:da:29:7d:1d:af:74:3a:b2:27:c0:c1:1e:3e:
+                    75:a3:16:a9:af:7a:22:5d:9f:13:1a:cf:a7:a0:eb:
+                    e3:86:0a:d3:fd:e6:96:95:d7:23:c8:37:dd:c4:7c:
+                    aa:36:ac:98:1a:12:b1:e0:4e:e8:b1:3b:f5:d6:6f:
+                    f1:30:d7
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Subject Key Identifier: 
+                4B:C5:B4:40:6B:AD:1C:B3:A5:1C:65:6E:46:36:89:87:05:0C:0E:B6
+    Signature Algorithm: sha256WithRSAEncryption
+         73:57:3f:2c:d5:95:32:7e:37:db:96:92:eb:19:5e:7e:53:e7:
+         41:ec:11:b6:47:ef:b5:de:ed:74:5c:c5:f1:8e:49:e0:fc:6e:
+         99:13:cd:9f:8a:da:cd:3a:0a:d8:3a:5a:09:3f:5f:34:d0:2f:
+         03:d2:66:1d:1a:bd:9c:90:37:c8:0c:8e:07:5a:94:45:46:2a:
+         e6:be:7a:da:a1:a9:a4:69:12:92:b0:7d:36:d4:44:87:d7:51:
+         f1:29:63:d6:75:cd:16:e4:27:89:1d:f8:c2:32:48:fd:db:99:
+         d0:8f:5f:54:74:cc:ac:67:34:11:62:d9:0c:0a:37:87:d1:a3:
+         17:48:8e:d2:17:1d:f6:d7:fd:db:65:eb:fd:a8:d4:f5:d6:4f:
+         a4:5b:75:e8:c5:d2:60:b2:db:09:7e:25:8b:7b:ba:52:92:9e:
+         3e:e8:c5:77:a1:3c:e0:4a:73:6b:61:cf:86:dc:43:ff:ff:21:
+         fe:23:5d:24:4a:f5:d3:6d:0f:62:04:05:57:82:da:6e:a4:33:
+         25:79:4b:2e:54:19:8b:cc:2c:3d:30:e9:d1:06:ff:e8:32:46:
+         be:b5:33:76:77:a8:01:5d:96:c1:c1:d5:be:ae:25:c0:c9:1e:
+         0a:09:20:88:a1:0e:c9:f3:6f:4d:82:54:00:20:a7:d2:8f:e4:
+         39:54:17:2e:8d:1e:b8:1b:bb:1b:bd:9a:4e:3b:10:34:dc:9c:
+         88:53:ef:a2:31:5b:58:4f:91:62:c8:c2:9a:9a:cd:15:5d:38:
+         a9:d6:be:f8:13:b5:9f:12:69:f2:50:62:ac:fb:17:37:f4:ee:
+         b8:75:67:60:10:fb:83:50:f9:44:b5:75:9c:40:17:b2:fe:fd:
+         79:5d:6e:58:58:5f:30:fc:00:ae:af:33:c1:0e:4e:6c:ba:a7:
+         a6:a1:7f:32:db:38:e0:b1:72:17:0a:2b:91:ec:6a:63:26:ed:
+         89:d4:78:cc:74:1e:05:f8:6b:fe:8c:6a:76:39:29:ae:65:23:
+         12:95:08:22:1c:97:ce:5b:06:ee:0c:e2:bb:bc:1f:44:93:f6:
+         d8:38:45:05:21:ed:e4:ad:ab:12:b6:03:a4:42:2e:2d:c4:09:
+         3a:03:67:69:84:9a:e1:59:90:8a:28:85:d5:5d:74:b1:d1:0e:
+         20:58:9b:13:a5:b0:63:a6:ed:7b:47:fd:45:55:30:a4:ee:9a:
+         d4:e6:e2:87:ef:98:c9:32:82:11:29:22:bc:00:0a:31:5e:2d:
+         0f:c0:8e:e9:6b:b2:8f:2e:06:d8:d1:91:c7:c6:12:f4:4c:fd:
+         30:17:c3:c1:da:38:5b:e3:a9:ea:e6:a1:ba:79:ef:73:d8:b6:
+         53:57:2d:f6:d0:e1:d7:48
+SHA1 Fingerprint=31:F1:FD:68:22:63:20:EE:C6:3B:3F:9D:EA:4A:3E:53:7C:7C:39:17
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index c825118..68ca506 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -173,7 +173,7 @@
      * local system, in nanoseconds. Equivalent to Linux's {@code CLOCK_MONOTONIC}.
      *
      * <p>This timestamp should only be used to measure a duration by comparing it
-     * against another timestamp from the same process on the same device.
+     * against another timestamp on the same device.
      * Values returned by this method do not have a defined correspondence to
      * wall clock times; the zero value is typically whenever the device last booted.
      * Use {@link #currentTimeMillis} if you want to know what time it is.
diff --git a/luni/src/main/java/java/lang/UnsafeByteSequence.java b/luni/src/main/java/java/lang/UnsafeByteSequence.java
deleted file mode 100644
index 228bb01..0000000
--- a/luni/src/main/java/java/lang/UnsafeByteSequence.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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 java.lang;
-
-import java.nio.charset.Charset;
-
-/**
- * A cheaper ByteArrayOutputStream for internal use. This class is unsynchronized,
- * and returns its internal array if it's the right size. This makes String.getBytes("UTF-8")
- * 10x faster than the baseline non-fast-path implementation instead of 8x faster when using
- * ByteArrayOutputStream. When GC and uncontended synchronization become cheap, we should be
- * able to get rid of this class. In the meantime, if you need to add further API, please try
- * to keep it plug-compatible with ByteArrayOutputStream with an eye to future obsolescence.
- *
- * @hide
- */
-public class UnsafeByteSequence {
-    private byte[] bytes;
-    private int count;
-
-    public UnsafeByteSequence(int initialCapacity) {
-        this.bytes = new byte[initialCapacity];
-    }
-
-    public int size() {
-        return count;
-    }
-
-    /**
-     * Moves the write pointer back to the beginning of the sequence,
-     * but without resizing or reallocating the buffer.
-     */
-    public void rewind() {
-        count = 0;
-    }
-
-    public void write(byte[] buffer, int offset, int length) {
-        if (count + length >= bytes.length) {
-            byte[] newBytes = new byte[(count + length) * 2];
-            System.arraycopy(bytes, 0, newBytes, 0, count);
-            bytes = newBytes;
-        }
-        System.arraycopy(buffer, offset, bytes, count, length);
-        count += length;
-    }
-
-    public void write(int b) {
-        if (count == bytes.length) {
-            byte[] newBytes = new byte[count * 2];
-            System.arraycopy(bytes, 0, newBytes, 0, count);
-            bytes = newBytes;
-        }
-        bytes[count++] = (byte) b;
-    }
-
-    @FindBugsSuppressWarnings("EI_EXPOSE_REP")
-    public byte[] toByteArray() {
-        if (count == bytes.length) {
-            return bytes;
-        }
-        byte[] result = new byte[count];
-        System.arraycopy(bytes, 0, result, 0, count);
-        return result;
-    }
-
-    public String toString(Charset cs) {
-        return new String(bytes, 0, count, cs);
-    }
-}
diff --git a/luni/src/main/java/java/lang/reflect/Modifier.java b/luni/src/main/java/java/lang/reflect/Modifier.java
index fdbe3bb..5f973d5 100644
--- a/luni/src/main/java/java/lang/reflect/Modifier.java
+++ b/luni/src/main/java/java/lang/reflect/Modifier.java
@@ -109,6 +109,13 @@
     public static final int MIRANDA = 0x8000;
 
     /**
+     * Dex addition to mark instance constructors and static class
+     * initializer methods.
+     * @hide
+     */
+    public static final int CONSTRUCTOR = 0x10000;
+
+    /**
      * Constructs a new {@code Modifier} instance.
      */
     public Modifier() {
@@ -239,6 +246,14 @@
     }
 
     /**
+     * Returns true if the given modifiers contain {@link Modifier#CONSTRUCTOR}.
+     * @hide
+     */
+    public static boolean isConstructor(int modifiers) {
+        return ((modifiers & Modifier.CONSTRUCTOR) != 0);
+    }
+
+    /**
      * Returns a string containing the string representation of all modifiers
      * present in the specified modifiers. Modifiers appear in the order
      * specified by the Java Language Specification.
diff --git a/luni/src/main/java/java/net/HttpURLConnection.java b/luni/src/main/java/java/net/HttpURLConnection.java
index d6b2435..89a4bc4 100644
--- a/luni/src/main/java/java/net/HttpURLConnection.java
+++ b/luni/src/main/java/java/net/HttpURLConnection.java
@@ -235,9 +235,9 @@
  * until a connection is established.
  *
  * <h3>Response Caching</h3>
- * 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.
+ * Android 4.0 (Ice Cream Sandwich, API level 15) 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
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index b35424d..3128b98 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -17,10 +17,8 @@
 
 package java.net;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -28,7 +26,6 @@
 import java.util.Enumeration;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
@@ -41,6 +38,8 @@
  * system or to identify the local interface of a joined multicast group.
  */
 public final class NetworkInterface extends Object {
+    private static final File SYS_CLASS_NET = new File("/sys/class/net");
+
     private final String name;
     private final int interfaceIndex;
     private final List<InterfaceAddress> interfaceAddresses;
@@ -110,17 +109,42 @@
             return null;
         }
 
+        return getByNameInternal(interfaceName, readIfInet6Lines());
+    }
+
+    /**
+     * Similar to {@link #getByName(String)} except that {@code interfaceName}
+     * is assumed to be valid.
+     */
+    private static NetworkInterface getByNameInternal(String interfaceName,
+            String[] ifInet6Lines) throws SocketException {
         int interfaceIndex = readIntFile("/sys/class/net/" + interfaceName + "/ifindex");
         List<InetAddress> addresses = new ArrayList<InetAddress>();
         List<InterfaceAddress> interfaceAddresses = new ArrayList<InterfaceAddress>();
-        collectIpv6Addresses(interfaceName, interfaceIndex, addresses, interfaceAddresses);
+
+        collectIpv6Addresses(interfaceName, interfaceIndex, addresses, interfaceAddresses,
+                ifInet6Lines);
         collectIpv4Address(interfaceName, addresses, interfaceAddresses);
 
         return new NetworkInterface(interfaceName, interfaceIndex, addresses, interfaceAddresses);
     }
 
-    private static void collectIpv6Addresses(String interfaceName, int interfaceIndex,
-            List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException {
+    private static String[] readIfInet6Lines() throws SocketException {
+        try {
+            return IoUtils.readFileAsString("/proc/net/if_inet6").split("\n");
+        } catch (IOException ioe) {
+            throw rethrowAsSocketException(ioe);
+        }
+    }
+
+    /**
+     * Visible for testing only.
+     *
+     * @hide
+     */
+    public static void collectIpv6Addresses(String interfaceName, int interfaceIndex,
+            List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses,
+            String[] ifInet6Lines) throws SocketException {
         // Format of /proc/net/if_inet6.
         // All numeric fields are implicit hex,
         // but not necessarily two-digit (http://code.google.com/p/android/issues/detail?id=34022).
@@ -132,12 +156,9 @@
         // 6. interface name
         // "00000000000000000000000000000001 01 80 10 80       lo"
         // "fe800000000000000000000000000000 407 40 20 80    wlan0"
-        BufferedReader in = null;
+        final String suffix = " " + interfaceName;
         try {
-            in = new BufferedReader(new FileReader("/proc/net/if_inet6"));
-            String suffix = " " + interfaceName;
-            String line;
-            while ((line = in.readLine()) != null) {
+            for (String line : ifInet6Lines) {
                 if (!line.endsWith(suffix)) {
                     continue;
                 }
@@ -160,10 +181,8 @@
                 addresses.add(inet6Address);
                 interfaceAddresses.add(new InterfaceAddress(inet6Address, prefixLength));
             }
-        } catch (Exception ex) {
+        } catch (NumberFormatException ex) {
             throw rethrowAsSocketException(ex);
-        } finally {
-            IoUtils.closeQuietly(in);
         }
     }
 
@@ -197,8 +216,14 @@
 
     @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
     private static boolean isValidInterfaceName(String interfaceName) {
+        final String[] interfaceList = SYS_CLASS_NET.list();
+        // We have no interfaces listed under /sys/class/net
+        if (interfaceList == null) {
+            return false;
+        }
+
         // Don't just stat because a crafty user might have / or .. in the supposed interface name.
-        for (String validName : new File("/sys/class/net").list()) {
+        for (String validName : interfaceList) {
             if (interfaceName.equals(validName)) {
                 return true;
             }
@@ -275,11 +300,13 @@
 
     @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
     private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
-        String[] interfaceNames = new File("/sys/class/net").list();
+        String[] interfaceNames = SYS_CLASS_NET.list();
         NetworkInterface[] interfaces = new NetworkInterface[interfaceNames.length];
         boolean[] done = new boolean[interfaces.length];
+
+        String[] ifInet6Lines = readIfInet6Lines();
         for (int i = 0; i < interfaceNames.length; ++i) {
-            interfaces[i] = NetworkInterface.getByName(interfaceNames[i]);
+            interfaces[i] = NetworkInterface.getByNameInternal(interfaceNames[i], ifInet6Lines);
             // 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) {
@@ -293,9 +320,9 @@
             if (done[counter]) {
                 continue;
             }
-            int counter2 = counter;
+
             // Checks whether the following interfaces are children.
-            for (; counter2 < interfaces.length; counter2++) {
+            for (int counter2 = counter; counter2 < interfaces.length; counter2++) {
                 if (done[counter2]) {
                     continue;
                 }
@@ -304,7 +331,7 @@
                     interfaces[counter2].parent = interfaces[counter];
                     interfaces[counter].addresses.addAll(interfaces[counter2].addresses);
                     done[counter2] = true;
-                  }
+                }
             }
             result.add(interfaces[counter]);
             done[counter] = true;
diff --git a/luni/src/main/java/java/nio/ByteBuffer.java b/luni/src/main/java/java/nio/ByteBuffer.java
index 324369a..456a309 100644
--- a/luni/src/main/java/java/nio/ByteBuffer.java
+++ b/luni/src/main/java/java/nio/ByteBuffer.java
@@ -156,10 +156,8 @@
      * The new buffer is direct if this byte buffer is direct.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a char buffer which is based on the content of this byte buffer.
      */
     public abstract CharBuffer asCharBuffer();
 
@@ -173,11 +171,8 @@
      * The new buffer is direct if this byte buffer is direct.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a double buffer which is based on the content of this byte
-     *         buffer.
      */
     public abstract DoubleBuffer asDoubleBuffer();
 
@@ -191,10 +186,8 @@
      * The new buffer is direct if this byte buffer is direct.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a float buffer which is based on the content of this byte buffer.
      */
     public abstract FloatBuffer asFloatBuffer();
 
@@ -208,10 +201,8 @@
      * The new buffer is direct if this byte buffer is direct.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a int buffer which is based on the content of this byte buffer.
      */
     public abstract IntBuffer asIntBuffer();
 
@@ -225,10 +216,8 @@
      * The new buffer is direct if this byte buffer is direct.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a long buffer which is based on the content of this byte buffer.
      */
     public abstract LongBuffer asLongBuffer();
 
@@ -257,10 +246,8 @@
      * The new buffer is direct if this byte buffer is direct.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a short buffer which is based on the content of this byte buffer.
      */
     public abstract ShortBuffer asShortBuffer();
 
@@ -313,14 +300,16 @@
      * Returns a duplicated buffer that shares its content with this buffer.
      * <p>
      * The duplicated buffer's position, limit, capacity and mark are the same
-     * as this buffer's. The duplicated buffer's read-only property and byte
-     * order are the same as this buffer's too.
-     * <p>
-     * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
-     * position, limit and mark are independent.
+     * as this buffer's. The duplicated buffer's read-only property is the same
+     * as this buffer's.
      *
-     * @return a duplicated buffer that shares its content with this buffer.
+     * <p>Note that <i>in contrast to all non-{@code byte} buffers</i>,
+     * byte order is not preserved in the duplicate, and is instead set to
+     * big-endian.
+     *
+     * <p>The new buffer shares its content with this buffer, which means either
+     * buffer's change of content will be visible to the other. The two buffers'
+     * position, limit and mark are independent.
      */
     public abstract ByteBuffer duplicate();
 
@@ -1020,10 +1009,8 @@
      * read-only property and byte order are the same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract ByteBuffer slice();
 }
diff --git a/luni/src/main/java/java/nio/CharBuffer.java b/luni/src/main/java/java/nio/CharBuffer.java
index 57070a0..92cab01 100644
--- a/luni/src/main/java/java/nio/CharBuffer.java
+++ b/luni/src/main/java/java/nio/CharBuffer.java
@@ -236,10 +236,8 @@
      * byte order are the same as this buffer's, too.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a duplicated buffer that shares its content with this buffer.
      */
     public abstract CharBuffer duplicate();
 
@@ -597,10 +595,8 @@
      * same as this buffer.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract CharBuffer slice();
 
@@ -614,7 +610,7 @@
      * buffer.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
      *
      * @param start
diff --git a/luni/src/main/java/java/nio/DoubleBuffer.java b/luni/src/main/java/java/nio/DoubleBuffer.java
index 401536d..2fa74d2 100644
--- a/luni/src/main/java/java/nio/DoubleBuffer.java
+++ b/luni/src/main/java/java/nio/DoubleBuffer.java
@@ -173,10 +173,8 @@
      * order are the same as this buffer's, too.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a duplicated buffer that shares its content with this buffer.
      */
     public abstract DoubleBuffer duplicate();
 
@@ -478,10 +476,8 @@
      * the same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract DoubleBuffer slice();
 }
diff --git a/luni/src/main/java/java/nio/FloatBuffer.java b/luni/src/main/java/java/nio/FloatBuffer.java
index ac5e572..cb7e55e 100644
--- a/luni/src/main/java/java/nio/FloatBuffer.java
+++ b/luni/src/main/java/java/nio/FloatBuffer.java
@@ -174,10 +174,8 @@
      * are same as this buffer too.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a duplicated buffer that shares its content with this buffer.
      */
     public abstract FloatBuffer duplicate();
 
@@ -477,10 +475,8 @@
      * same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract FloatBuffer slice();
 }
diff --git a/luni/src/main/java/java/nio/IntBuffer.java b/luni/src/main/java/java/nio/IntBuffer.java
index bbcc2e3..a20f6c2 100644
--- a/luni/src/main/java/java/nio/IntBuffer.java
+++ b/luni/src/main/java/java/nio/IntBuffer.java
@@ -168,10 +168,8 @@
      * are the same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a duplicated buffer that shares its content with this buffer.
      */
     public abstract IntBuffer duplicate();
 
@@ -466,10 +464,8 @@
      * same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract IntBuffer slice();
 }
diff --git a/luni/src/main/java/java/nio/LongBuffer.java b/luni/src/main/java/java/nio/LongBuffer.java
index 58d7518..55adab6 100644
--- a/luni/src/main/java/java/nio/LongBuffer.java
+++ b/luni/src/main/java/java/nio/LongBuffer.java
@@ -170,10 +170,8 @@
      * are same as this buffer's, too.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a duplicated buffer that shares its content with this buffer.
      */
     public abstract LongBuffer duplicate();
 
@@ -467,10 +465,8 @@
      * same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract LongBuffer slice();
 }
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 41a2a52..d63fa63 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -84,7 +84,7 @@
         super(selectorProvider);
 
         /*
-         * Create a pipes to trigger wakeup. We can't use a NIO pipe because it
+         * Create a pipe to trigger wakeup. We can't use a NIO pipe because it
          * would be closed if the selecting thread is interrupted. Also
          * configure the pipe so we can fully drain it without blocking.
          */
@@ -255,6 +255,11 @@
 
             int ops = key.interestOpsNoCheck();
             int selectedOps = 0;
+            if ((pollFd.revents & POLLHUP) != 0) {
+                // If there was an error condition, we definitely want to wake listeners,
+                // regardless of what they're waiting for. Failure is always interesting.
+                selectedOps |= ops;
+            }
             if ((pollFd.revents & POLLIN) != 0) {
                 selectedOps |= ops & (OP_ACCEPT | OP_READ);
             }
@@ -289,7 +294,7 @@
 
     /**
      * Removes cancelled keys from the key set and selected key set, and
-     * deregisters the corresponding channels. Returns the number of keys
+     * unregisters the corresponding channels. Returns the number of keys
      * removed from the selected key set.
      */
     private int doCancel() {
diff --git a/luni/src/main/java/java/nio/ShortBuffer.java b/luni/src/main/java/java/nio/ShortBuffer.java
index ea20bed..8da01ed 100644
--- a/luni/src/main/java/java/nio/ShortBuffer.java
+++ b/luni/src/main/java/java/nio/ShortBuffer.java
@@ -170,10 +170,8 @@
      * are the same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a duplicated buffer that shares its content with this buffer.
      */
     public abstract ShortBuffer duplicate();
 
@@ -466,10 +464,8 @@
      * same as this buffer's.
      * <p>
      * The new buffer shares its content with this buffer, which means either
-     * buffer's change of content will be visible to the other. The two buffer's
+     * buffer's change of content will be visible to the other. The two buffers'
      * position, limit and mark are independent.
-     *
-     * @return a sliced buffer that shares its content with this buffer.
      */
     public abstract ShortBuffer slice();
 }
diff --git a/luni/src/main/java/java/nio/charset/Charset.java b/luni/src/main/java/java/nio/charset/Charset.java
index 02cd42f..b3e8d0b 100644
--- a/luni/src/main/java/java/nio/charset/Charset.java
+++ b/luni/src/main/java/java/nio/charset/Charset.java
@@ -175,10 +175,11 @@
      *             <code>aliases</code>.
      */
     protected Charset(String canonicalName, String[] aliases) {
-        // check whether the given canonical name is legal
+        // Check whether the given canonical name is legal.
         checkCharsetName(canonicalName);
         this.canonicalName = canonicalName;
-        // check each alias and put into a set
+
+        // Collect and check each unique alias.
         this.aliasesSet = new HashSet<String>();
         if (aliases != null) {
             for (String alias : aliases) {
@@ -192,15 +193,21 @@
         if (name.isEmpty()) {
             throw new IllegalCharsetNameException(name);
         }
-        int length = name.length();
-        for (int i = 0; i < length; ++i) {
-            if (!isValidCharsetNameCharacter(name.charAt(i))) {
+        if (!isValidCharsetNameStart(name.charAt(0))) {
+            throw new IllegalCharsetNameException(name);
+        }
+        for (int i = 1; i < name.length(); ++i) {
+            if (!isValidCharsetNamePart(name.charAt(i))) {
                 throw new IllegalCharsetNameException(name);
             }
         }
     }
 
-    private static boolean isValidCharsetNameCharacter(char c) {
+    private static boolean isValidCharsetNameStart(char c) {
+        return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
+    }
+
+    private static boolean isValidCharsetNamePart(char c) {
         return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
                 c == '-' || c == '.' || c == ':' || c == '_';
     }
@@ -210,7 +217,6 @@
      * If multiple charsets have the same canonical name, it is unspecified which is returned in
      * the map. This method may be slow. If you know which charset you're looking for, use
      * {@link #forName}.
-     * @return an immutable case-insensitive map from canonical names to {@code Charset} instances
      */
     public static SortedMap<String, Charset> availableCharsets() {
         // Start with a copy of the built-in charsets...
@@ -357,67 +363,56 @@
     public abstract boolean contains(Charset charset);
 
     /**
-     * Gets a new instance of an encoder for this charset.
-     *
-     * @return a new instance of an encoder for this charset.
+     * Returns a new instance of an encoder for this charset.
      */
     public abstract CharsetEncoder newEncoder();
 
     /**
-     * Gets a new instance of a decoder for this charset.
-     *
-     * @return a new instance of a decoder for this charset.
+     * Returns a new instance of a decoder for this charset.
      */
     public abstract CharsetDecoder newDecoder();
 
     /**
      * Returns the canonical name of this charset.
+     *
+     * <p>If a charset is in the IANA registry, this will be the MIME-preferred name (a charset
+     * may have multiple IANA-registered names). Otherwise the canonical name will begin with "x-"
+     * or "X-".
      */
     public final String name() {
         return this.canonicalName;
     }
 
     /**
-     * Gets the set of this charset's aliases.
-     *
-     * @return an unmodifiable set of this charset's aliases.
+     * Returns an unmodifiable set of this charset's aliases.
      */
     public final Set<String> aliases() {
         return Collections.unmodifiableSet(this.aliasesSet);
     }
 
     /**
-     * Gets the name of this charset for the default locale.
+     * Returns the name of this charset for the default locale.
      *
      * <p>The default implementation returns the canonical name of this charset.
      * Subclasses may return a localized display name.
-     *
-     * @return the name of this charset for the default locale.
      */
     public String displayName() {
         return this.canonicalName;
     }
 
     /**
-     * Gets the name of this charset for the specified locale.
+     * Returns the name of this charset for the specified locale.
      *
      * <p>The default implementation returns the canonical name of this charset.
      * Subclasses may return a localized display name.
-     *
-     * @param l
-     *            a certain locale
-     * @return the name of this charset for the specified locale
      */
     public String displayName(Locale l) {
         return this.canonicalName;
     }
 
     /**
-     * Indicates whether this charset is known to be registered in the IANA
+     * Returns true if this charset is known to be registered in the IANA
      * Charset Registry.
-     *
-     * @return true if the charset is known to be registered, otherwise returns
-     *         false.
      */
     public final boolean isRegistered() {
         return !canonicalName.startsWith("x-") && !canonicalName.startsWith("X-");
@@ -425,8 +420,6 @@
 
     /**
      * Returns true if this charset supports encoding, false otherwise.
-     *
-     * @return true if this charset supports encoding, false otherwise.
      */
     public boolean canEncode() {
         return true;
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
index 7aa546c..7b53ceb 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
@@ -82,22 +82,22 @@
  * @see java.nio.charset.CharsetEncoder
  */
 public abstract class CharsetDecoder {
-    private static final int INIT = 0;
-    private static final int ONGOING = 1;
-    private static final int END = 2;
-    private static final int FLUSH = 3;
+    private static final String RESET = "RESET";
+    private static final String ONGOING = "ONGOING";
+    private static final String END_OF_INPUT = "END_OF_INPUT";
+    private static final String FLUSHED = "FLUSHED";
+
+    private final Charset charset;
 
     private final float averageCharsPerByte;
     private final float maxCharsPerByte;
 
-    private final Charset cs;
+    private String replacementChars = "\ufffd";
 
-    private CodingErrorAction malformedInputAction;
-    private CodingErrorAction unmappableCharacterAction;
+    private String state = RESET;
 
-    private String replacementChars;
-
-    private int status;
+    private CodingErrorAction malformedInputAction = CodingErrorAction.REPORT;
+    private CodingErrorAction unmappableCharacterAction = CodingErrorAction.REPORT;
 
     /**
      * Constructs a new <code>CharsetDecoder</code> using the given
@@ -114,8 +114,7 @@
      *            the maximum number of characters created by this decoder for
      *            one input byte, must be positive.
      * @throws IllegalArgumentException
-     *             if <code>averageCharsPerByte</code> or
-     *             <code>maxCharsPerByte</code> is negative.
+     *     if {@code averageCharsPerByte <= 0 || maxCharsPerByte <= 0 || averageCharsPerByte > maxCharsPerByte}.
      */
     protected CharsetDecoder(Charset charset, float averageCharsPerByte, float maxCharsPerByte) {
         if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) {
@@ -126,11 +125,7 @@
         }
         this.averageCharsPerByte = averageCharsPerByte;
         this.maxCharsPerByte = maxCharsPerByte;
-        cs = charset;
-        status = INIT;
-        malformedInputAction = CodingErrorAction.REPORT;
-        unmappableCharacterAction = CodingErrorAction.REPORT;
-        replacementChars = "\ufffd";
+        this.charset = charset;
     }
 
     /**
@@ -145,7 +140,7 @@
      * Returns the {@link Charset} which this decoder uses.
      */
     public final Charset charset() {
-        return cs;
+        return charset;
     }
 
     /**
@@ -182,35 +177,30 @@
      *             if another exception happened during the decode operation.
      */
     public final CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
-        reset();
         int length = (int) (in.remaining() * averageCharsPerByte);
-        CharBuffer output = CharBuffer.allocate(length);
-        CoderResult result = null;
-        while (true) {
-            result = decode(in, output, false);
-            checkCoderResult(result);
-            if (result.isUnderflow()) {
-                break;
-            } else if (result.isOverflow()) {
-                output = allocateMore(output);
-            }
-        }
-        result = decode(in, output, true);
-        checkCoderResult(result);
+        CharBuffer out = CharBuffer.allocate(length);
 
-        while (true) {
-            result = flush(output);
-            checkCoderResult(result);
-            if (result.isOverflow()) {
-                output = allocateMore(output);
+        reset();
+
+        while (state != FLUSHED) {
+            CoderResult result = decode(in, out, true);
+            if (result == CoderResult.OVERFLOW) {
+                out = allocateMore(out);
+                continue; // No point trying to flush to an already-full buffer.
             } else {
-                break;
+                checkCoderResult(result);
+            }
+
+            result = flush(out);
+            if (result == CoderResult.OVERFLOW) {
+                out = allocateMore(out);
+            } else {
+                checkCoderResult(result);
             }
         }
 
-        output.flip();
-        status = FLUSH;
-        return output;
+        out.flip();
+        return out;
     }
 
     /*
@@ -304,54 +294,42 @@
      *             <code>BufferOverflowException</code>.
      */
     public final CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput) {
-        if (status == FLUSH || (!endOfInput && status == END)) {
-            throw new IllegalStateException();
+        if (state != RESET && state != ONGOING && !(endOfInput && state == END_OF_INPUT)) {
+            throw illegalStateException();
         }
 
-        CoderResult result = null;
+        state = endOfInput ? END_OF_INPUT : ONGOING;
 
-        // begin to decode
         while (true) {
-            CodingErrorAction action = null;
+            CoderResult result;
             try {
                 result = decodeLoop(in, out);
             } catch (BufferOverflowException ex) {
-                // unexpected exception
                 throw new CoderMalfunctionError(ex);
             } catch (BufferUnderflowException ex) {
-                // unexpected exception
                 throw new CoderMalfunctionError(ex);
             }
 
-            /*
-             * result handling
-             */
-            if (result.isUnderflow()) {
-                int remaining = in.remaining();
-                status = endOfInput ? END : ONGOING;
-                if (endOfInput && remaining > 0) {
-                    result = CoderResult.malformedForLength(remaining);
+            if (result == CoderResult.UNDERFLOW) {
+                if (endOfInput && in.hasRemaining()) {
+                    result = CoderResult.malformedForLength(in.remaining());
                 } else {
                     return result;
                 }
-            }
-            if (result.isOverflow()) {
+            } else if (result == CoderResult.OVERFLOW) {
                 return result;
             }
-            // set coding error handle action
-            action = malformedInputAction;
-            if (result.isUnmappable()) {
-                action = unmappableCharacterAction;
-            }
-            // If the action is IGNORE or REPLACE, we should continue decoding.
-            if (action == CodingErrorAction.REPLACE) {
+
+            // We have a real error, so do what the appropriate action tells us what to do...
+            CodingErrorAction action =
+                    result.isUnmappable() ? unmappableCharacterAction : malformedInputAction;
+            if (action == CodingErrorAction.REPORT) {
+                return result;
+            } else if (action == CodingErrorAction.REPLACE) {
                 if (out.remaining() < replacementChars.length()) {
                     return CoderResult.OVERFLOW;
                 }
                 out.put(replacementChars);
-            } else {
-                if (action != CodingErrorAction.IGNORE)
-                    return result;
             }
             in.position(in.position() + result.length());
         }
@@ -442,20 +420,15 @@
      * @return <code>CoderResult.UNDERFLOW</code> or
      *         <code>CoderResult.OVERFLOW</code>.
      * @throws IllegalStateException
-     *             if this decoder hasn't read all input bytes during one
-     *             decoding process, which means neither after calling
-     *             {@link #decode(ByteBuffer) decode(ByteBuffer)} nor after
-     *             calling {@link #decode(ByteBuffer, CharBuffer, boolean)
-     *             decode(ByteBuffer, CharBuffer, boolean)} with true as value
-     *             for the last boolean parameter.
+     *             if this decoder isn't already flushed or at end of input.
      */
     public final CoderResult flush(CharBuffer out) {
-        if (status != END && status != INIT) {
-            throw new IllegalStateException();
+        if (state != FLUSHED && state != END_OF_INPUT) {
+            throw illegalStateException();
         }
         CoderResult result = implFlush(out);
         if (result == CoderResult.UNDERFLOW) {
-            status = FLUSH;
+            state = FLUSHED;
         }
         return result;
     }
@@ -582,7 +555,7 @@
      *            the new action on malformed input error.
      * @return this decoder.
      * @throws IllegalArgumentException
-     *             if {@code newAction} is {@code null}.
+     *             if {@code newAction == null}.
      */
     public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) {
         if (newAction == null) {
@@ -604,7 +577,7 @@
      *            the new action on unmappable character error.
      * @return this decoder.
      * @throws IllegalArgumentException
-     *             if {@code newAction} is {@code null}.
+     *             if {@code newAction == null}.
      */
     public final CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction) {
         if (newAction == null) {
@@ -631,8 +604,8 @@
      * new replacement as argument.
      *
      * @param replacement
-     *            the replacement string, cannot be null or empty. Its length
-     *            cannot be larger than {@link #maxCharsPerByte()}.
+     *            the replacement string cannot be null, empty, or longer
+     *            than {@link #maxCharsPerByte()}.
      * @return this decoder.
      * @throws IllegalArgumentException
      *             if the given replacement cannot satisfy the requirement
@@ -655,14 +628,12 @@
     }
 
     /**
-     * Resets this decoder. This method will reset the internal status, and then
-     * calls <code>implReset()</code> to reset any status related to the
+     * Resets this decoder. This method will reset the internal state, and then
+     * calls {@link #implReset} to reset any state related to the
      * specific charset.
-     *
-     * @return this decoder.
      */
     public final CharsetDecoder reset() {
-        status = INIT;
+        state = RESET;
         implReset();
         return this;
     }
@@ -674,4 +645,8 @@
     public CodingErrorAction unmappableCharacterAction() {
         return unmappableCharacterAction;
     }
+
+    private IllegalStateException illegalStateException() {
+        throw new IllegalStateException("State: " + state);
+    }
 }
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index c2d74a1..9217bba 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -76,25 +76,22 @@
  * @see java.nio.charset.CharsetDecoder
  */
 public abstract class CharsetEncoder {
-    private static final int READY = 0;
-    private static final int ONGOING = 1;
-    private static final int END = 2;
-    private static final int FLUSH = 3;
-    private static final int INIT = 4;
+    private static final String RESET = "RESET";
+    private static final String ONGOING = "ONGOING";
+    private static final String END_OF_INPUT = "END_OF_INPUT";
+    private static final String FLUSHED = "FLUSHED";
 
-    private final Charset cs;
+    private final Charset charset;
 
     private final float averageBytesPerChar;
     private final float maxBytesPerChar;
 
     private byte[] replacementBytes;
 
-    private int status;
-    // internal status indicates encode(CharBuffer) operation is finished
-    private boolean finished;
+    private String state = RESET;
 
-    private CodingErrorAction malformedInputAction;
-    private CodingErrorAction unmappableCharacterAction;
+    private CodingErrorAction malformedInputAction = CodingErrorAction.REPORT;
+    private CodingErrorAction unmappableCharacterAction = CodingErrorAction.REPORT;
 
     // decoder instance for this encoder's charset, used for replacement value checking
     private CharsetDecoder decoder;
@@ -139,12 +136,9 @@
         if (averageBytesPerChar > maxBytesPerChar) {
             throw new IllegalArgumentException("averageBytesPerChar is greater than maxBytesPerChar");
         }
-        this.cs = cs;
+        this.charset = cs;
         this.averageBytesPerChar = averageBytesPerChar;
         this.maxBytesPerChar = maxBytesPerChar;
-        status = INIT;
-        malformedInputAction = CodingErrorAction.REPORT;
-        unmappableCharacterAction = CodingErrorAction.REPORT;
         if (trusted) {
             // The RI enforces unnecessary restrictions on the replacement bytes. We trust ICU to
             // know what it's doing. Doing so lets us support ICU's EUC-JP, SCSU, and Shift_JIS.
@@ -165,7 +159,7 @@
     /**
      * Tests whether the given character can be encoded by this encoder.
      *
-     * <p>Note that this method may change the internal status of this encoder, so
+     * <p>Note that this method may change the internal state of this encoder, so
      * it should not be called when another encoding process is ongoing,
      * otherwise it will throw an <code>IllegalStateException</code>.
      *
@@ -179,7 +173,7 @@
      * Tests whether the given <code>CharSequence</code> can be encoded by this
      * encoder.
      *
-     * <p>Note that this method may change the internal status of this encoder, so
+     * <p>Note that this method may change the internal state of this encoder, so
      * it should not be called when another encode process is ongoing, otherwise
      * it will throw an <code>IllegalStateException</code>.
      *
@@ -193,18 +187,19 @@
             cb = CharBuffer.wrap(sequence);
         }
 
-        if (status == FLUSH || status == INIT) {
-            status = READY;
+        if (state == FLUSHED) {
+            reset();
         }
-        if (status != READY) {
-            throw new IllegalStateException();
+        if (state != RESET) {
+            throw illegalStateException();
         }
+
         CodingErrorAction originalMalformedInputAction = malformedInputAction;
         CodingErrorAction originalUnmappableCharacterAction = unmappableCharacterAction;
         onMalformedInput(CodingErrorAction.REPORT);
         onUnmappableCharacter(CodingErrorAction.REPORT);
         try {
-            this.encode(cb);
+            encode(cb);
             return true;
         } catch (CharacterCodingException e) {
             return false;
@@ -219,7 +214,7 @@
      * Returns the {@link Charset} which this encoder uses.
      */
     public final Charset charset() {
-        return cs;
+        return charset;
     }
 
     /**
@@ -256,43 +251,30 @@
     public final ByteBuffer encode(CharBuffer in) throws CharacterCodingException {
         int length = (int) (in.remaining() * averageBytesPerChar);
         ByteBuffer out = ByteBuffer.allocate(length);
-        if (in.hasRemaining() == false) {
-            return out;
-        }
 
         reset();
 
-        while (in.hasRemaining()) {
+        while (state != FLUSHED) {
             CoderResult result = encode(in, out, true);
-            if (result == CoderResult.UNDERFLOW) {
-                break;
-            } else if (result == CoderResult.OVERFLOW) {
+            if (result == CoderResult.OVERFLOW) {
                 out = allocateMore(out);
-                continue;
+                continue; // No point trying to flush to an already-full buffer.
             } else {
                 checkCoderResult(result);
             }
 
             result = flush(out);
-            if (result == CoderResult.UNDERFLOW) {
-                break;
-            } else if (result == CoderResult.OVERFLOW) {
+            if (result == CoderResult.OVERFLOW) {
                 out = allocateMore(out);
-                continue;
             } else {
                 checkCoderResult(result);
             }
         }
 
         out.flip();
-        status = READY;
-        finished = true;
         return out;
     }
 
-    /*
-     * checks the result whether it needs to throw CharacterCodingException.
-     */
     private void checkCoderResult(CoderResult result) throws CharacterCodingException {
         if (malformedInputAction == CodingErrorAction.REPORT && result.isMalformed()) {
             throw new MalformedInputException(result.length());
@@ -301,7 +283,6 @@
         }
     }
 
-    // allocate more spaces to the given ByteBuffer
     private ByteBuffer allocateMore(ByteBuffer output) {
         if (output.capacity() == 0) {
             return ByteBuffer.allocate(1);
@@ -375,26 +356,22 @@
      *             <code>BufferUnderflowException</code>.
      */
     public final CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) {
-        // If the previous step is encode(CharBuffer), then no more input is needed
-        // thus endOfInput should not be false
-        if (status == READY && finished && !endOfInput) {
-            throw new IllegalStateException();
-        }
-        if (status == FLUSH || (!endOfInput && status == END)) {
-            throw new IllegalStateException();
+        if (state != RESET && state != ONGOING && !(endOfInput && state == END_OF_INPUT)) {
+            throw illegalStateException();
         }
 
-        status = endOfInput ? END : ONGOING;
+        state = endOfInput ? END_OF_INPUT : ONGOING;
 
         while (true) {
             CoderResult result;
             try {
                 result = encodeLoop(in, out);
-            } catch (BufferOverflowException e) {
-                throw new CoderMalfunctionError(e);
-            } catch (BufferUnderflowException e) {
-                throw new CoderMalfunctionError(e);
+            } catch (BufferOverflowException ex) {
+                throw new CoderMalfunctionError(ex);
+            } catch (BufferUnderflowException ex) {
+                throw new CoderMalfunctionError(ex);
             }
+
             if (result == CoderResult.UNDERFLOW) {
                 if (endOfInput && in.hasRemaining()) {
                     result = CoderResult.malformedForLength(in.remaining());
@@ -404,12 +381,13 @@
             } else if (result == CoderResult.OVERFLOW) {
                 return result;
             }
+
+            // We have a real error, so do what the appropriate action tells us what to do...
             CodingErrorAction action =
                     result.isUnmappable() ? unmappableCharacterAction : malformedInputAction;
             if (action == CodingErrorAction.REPORT) {
                 return result;
-            }
-            if (action == CodingErrorAction.REPLACE) {
+            } else if (action == CodingErrorAction.REPLACE) {
                 if (out.remaining() < replacementBytes.length) {
                     return CoderResult.OVERFLOW;
                 }
@@ -477,20 +455,15 @@
      * @return <code>CoderResult.UNDERFLOW</code> or
      *         <code>CoderResult.OVERFLOW</code>.
      * @throws IllegalStateException
-     *             if this encoder hasn't read all input characters during one
-     *             encoding process, which means neither after calling
-     *             {@link #encode(CharBuffer) encode(CharBuffer)} nor after
-     *             calling {@link #encode(CharBuffer, ByteBuffer, boolean)
-     *             encode(CharBuffer, ByteBuffer, boolean)} with {@code true}
-     *             for the last boolean parameter.
+     *             if this encoder isn't already flushed or at end of input.
      */
     public final CoderResult flush(ByteBuffer out) {
-        if (status != END && status != READY) {
-            throw new IllegalStateException();
+        if (state != FLUSHED && state != END_OF_INPUT) {
+            throw illegalStateException();
         }
         CoderResult result = implFlush(out);
         if (result == CoderResult.UNDERFLOW) {
-            status = FLUSH;
+            state = FLUSHED;
         }
         return result;
     }
@@ -559,7 +532,7 @@
      */
     public boolean isLegalReplacement(byte[] replacement) {
         if (decoder == null) {
-            decoder = cs.newDecoder();
+            decoder = charset.newDecoder();
             decoder.onMalformedInput(CodingErrorAction.REPORT);
             decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
         }
@@ -675,14 +648,12 @@
     }
 
     /**
-     * Resets this encoder. This method will reset the internal status and then
-     * calls <code>implReset()</code> to reset any status related to the
+     * Resets this encoder. This method will reset the internal state and then
+     * calls {@link #implReset} to reset any state related to the
      * specific charset.
-     *
-     * @return this encoder.
      */
     public final CharsetEncoder reset() {
-        status = INIT;
+        state = RESET;
         implReset();
         return this;
     }
@@ -694,4 +665,8 @@
     public CodingErrorAction unmappableCharacterAction() {
         return unmappableCharacterAction;
     }
+
+    private IllegalStateException illegalStateException() {
+        throw new IllegalStateException("State: " + state);
+    }
 }
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 04287ae..5fd8a56 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -94,7 +94,7 @@
  * <p>Fractional seconds are handled specially: they're zero-padded on the <i>right</i>.
  *
  * <p>The two pattern characters {@code L} and {@code c} are ICU-compatible extensions, not
- * available in the RI or in Android before Android 2.3 "Gingerbread" (API level 9). These
+ * available in the RI or in Android before Android 2.3 (Gingerbread, API level 9). These
  * extensions are necessary for correct localization in languages such as Russian
  * that make a grammatical distinction between, say, the word "June" in the sentence "June" and
  * in the sentence "June 10th"; the former is the stand-alone form, the latter the regular
@@ -102,7 +102,7 @@
  * and {@code c} is equivalent, but for weekday names.
  *
  * <p>Five-count patterns (such as "MMMMM") used for the shortest non-numeric
- * representation of a field were introduced in Jelly Bean MR2 (API level 18).
+ * representation of a field were introduced in Android 4.3 (Jelly Bean MR2, API level 18).
  *
  * <p>When two numeric fields are directly adjacent with no intervening delimiter
  * characters, they constitute a run of adjacent numeric fields. Such runs are
@@ -1130,22 +1130,35 @@
         return position.getIndex();
     }
 
-    private int parseText(String string, int offset, String[] text, int field) {
-        int found = -1;
-        for (int i = 0; i < text.length; i++) {
-            if (text[i].isEmpty()) {
+    private int parseText(String string, int offset, String[] options, int field) {
+        // We search for the longest match, in case some entries are substrings of others.
+        int bestIndex = -1;
+        int bestLength = -1;
+        for (int i = 0; i < options.length; ++i) {
+            String option = options[i];
+            int optionLength = option.length();
+            if (optionLength == 0) {
                 continue;
             }
-            if (string.regionMatches(true, offset, text[i], 0, text[i].length())) {
-                // Search for the longest match, in case some fields are subsets
-                if (found == -1 || text[i].length() > text[found].length()) {
-                    found = i;
+            if (string.regionMatches(true, offset, option, 0, optionLength)) {
+                if (bestIndex == -1 || optionLength > bestLength) {
+                    bestIndex = i;
+                    bestLength = optionLength;
+                }
+            } else if (option.charAt(optionLength - 1) == '.') {
+                // If CLDR has abbreviated forms like "Aug.", we should accept "Aug" too.
+                // https://code.google.com/p/android/issues/detail?id=59383
+                if (string.regionMatches(true, offset, option, 0, optionLength - 1)) {
+                    if (bestIndex == -1 || optionLength - 1 > bestLength) {
+                        bestIndex = i;
+                        bestLength = optionLength - 1;
+                    }
                 }
             }
         }
-        if (found != -1) {
-            calendar.set(field, found);
-            return offset + text[found].length();
+        if (bestIndex != -1) {
+            calendar.set(field, bestIndex);
+            return offset + bestLength;
         }
         return -offset - 1;
     }
diff --git a/luni/src/main/java/java/util/Arrays.java b/luni/src/main/java/java/util/Arrays.java
index 2c84795..d3e05a0 100644
--- a/luni/src/main/java/java/util/Arrays.java
+++ b/luni/src/main/java/java/util/Arrays.java
@@ -1963,32 +1963,22 @@
     /**
      * Sorts the specified array in ascending natural order.
      *
-     * @param array
-     *            the {@code Object} array to be sorted.
-     * @throws ClassCastException
-     *                if an element in the array does not implement {@code Comparable}
-     *                or if some elements cannot be compared to each other.
-     * @see #sort(Object[], int, int)
+     * @throws ClassCastException if any element does not implement {@code Comparable},
+     *     or if {@code compareTo} throws for any pair of elements.
      */
     public static void sort(Object[] array) {
         ComparableTimSort.sort(array);
     }
 
     /**
-     * Sorts the specified range in the array in ascending natural order. All
-     * elements must implement the {@code Comparable} interface and must be
-     * comparable to each other without a {@code ClassCastException} being
-     * thrown.
+     * Sorts the specified range in the array in ascending natural order.
      *
-     * @param array
-     *            the {@code Object} array to be sorted.
      * @param start
      *            the start index to sort.
      * @param end
      *            the last + 1 index to sort.
-     * @throws ClassCastException
-     *                if an element in the array does not implement {@code Comparable}
-     *                or some elements cannot be compared to each other.
+     * @throws ClassCastException if any element does not implement {@code Comparable},
+     *     or if {@code compareTo} throws for any pair of elements.
      * @throws IllegalArgumentException
      *                if {@code start > end}.
      * @throws ArrayIndexOutOfBoundsException
@@ -2003,8 +1993,6 @@
      * All elements must be comparable to each other without a
      * {@code ClassCastException} being thrown.
      *
-     * @param array
-     *            the {@code Object} array to be sorted.
      * @param start
      *            the start index to sort.
      * @param end
@@ -2013,7 +2001,7 @@
      *            the {@code Comparator}.
      * @throws ClassCastException
      *                if elements in the array cannot be compared to each other
-     *                using the {@code Comparator}.
+     *                using the given {@code Comparator}.
      * @throws IllegalArgumentException
      *                if {@code start > end}.
      * @throws ArrayIndexOutOfBoundsException
@@ -2027,10 +2015,6 @@
      * Sorts the specified array using the specified {@code Comparator}. All elements
      * must be comparable to each other without a {@code ClassCastException} being thrown.
      *
-     * @param array
-     *            the {@code Object} array to be sorted.
-     * @param comparator
-     *            the {@code Comparator}.
      * @throws ClassCastException
      *                if elements in the array cannot be compared to each other
      *                using the {@code Comparator}.
diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java
index 932dcc1..ce4e579 100644
--- a/luni/src/main/java/java/util/Collections.java
+++ b/luni/src/main/java/java/util/Collections.java
@@ -1852,14 +1852,11 @@
     }
 
     /**
-     * Sorts the specified list in ascending natural order. The algorithm is
+     * Sorts the given list in ascending natural order. The algorithm is
      * stable which means equal elements don't get reordered.
      *
-     * @param list
-     *            the list to be sorted.
-     * @throws ClassCastException
-     *             when an element in the List does not implement Comparable or
-     *             elements cannot be compared to each other.
+     * @throws ClassCastException if any element does not implement {@code Comparable},
+     *     or if {@code compareTo} throws for any pair of elements.
      */
     @SuppressWarnings("unchecked")
     public static <T extends Comparable<? super T>> void sort(List<T> list) {
@@ -1874,16 +1871,11 @@
     }
 
     /**
-     * Sorts the specified list using the specified comparator. The algorithm is
+     * Sorts the given list using the given comparator. The algorithm is
      * stable which means equal elements don't get reordered.
      *
-     * @param list
-     *            the list to be sorted.
-     * @param comparator
-     *            the comparator.
-     * @throws ClassCastException
-     *             when elements in the list cannot be compared to each other
-     *             using the comparator.
+     * @throws ClassCastException if any element does not implement {@code Comparable},
+     *     or if {@code compareTo} throws for any pair of elements.
      */
     @SuppressWarnings("unchecked")
     public static <T> void sort(List<T> list, Comparator<? super T> comparator) {
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index 23db9dc..fc8f7c6 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -66,12 +66,34 @@
  * <p>Here are the versions of ICU (and the corresponding CLDR and Unicode versions) used in
  * various Android releases:
  * <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
- * <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>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>
- * <tr><td>Jelly Bean MR2</td>       <td>ICU 50</td>  <td><a href="http://cldr.unicode.org/index/downloads/cldr-21-1">CLDR 22.1</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
+ * <tr><td>Android 1.5 (Cupcake)/Android 1.6 (Donut)/Android 2.0 (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>Android 2.2 (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>Android 2.3 (Gingerbread)/Android 3.0 (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>Android 4.0 (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>Android 4.1 (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>
+ * <tr><td>Android 4.3 (Jelly Bean MR2)</td>
+ *     <td>ICU 50</td>
+ *     <td><a href="http://cldr.unicode.org/index/downloads/cldr-22-1">CLDR 22.1</a></td>
+ *     <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
+ * <tr><td>Android 4.4 (KitKat)</td>
+ *     <td>ICU 51</td>
+ *     <td><a href="http://cldr.unicode.org/index/downloads/cldr-23">CLDR 23</a></td>
+ *     <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</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/PriorityQueue.java b/luni/src/main/java/java/util/PriorityQueue.java
index cc9bfe6..690c1f8 100644
--- a/luni/src/main/java/java/util/PriorityQueue.java
+++ b/luni/src/main/java/java/util/PriorityQueue.java
@@ -26,8 +26,8 @@
  * construction time. If the queue uses natural ordering, only elements that are
  * comparable are permitted to be inserted into the queue.
  * <p>
- * The least element of the specified ordering is stored at the head of the
- * queue and the greatest element is stored at the tail of the queue.
+ * The least element of the specified ordering is the first retrieved with
+ * {@link #poll()} and the greatest element is the last.
  * <p>
  * A PriorityQueue is not synchronized. If multiple threads will have to access
  * it concurrently, use the {@link java.util.concurrent.PriorityBlockingQueue}.
@@ -341,9 +341,13 @@
 
     private void removeAt(int index) {
         size--;
-        elements[index] = elements[size];
+        E moved = elements[size];
+        elements[index] = moved;
         siftDown(index);
         elements[size] = null;
+        if (moved == elements[index]) {
+            siftUp(index);
+        }
     }
 
     private int compare(E o1, E o2) {
diff --git a/luni/src/main/java/java/util/Scanner.java b/luni/src/main/java/java/util/Scanner.java
index 972e6f9..7d504b7 100644
--- a/luni/src/main/java/java/util/Scanner.java
+++ b/luni/src/main/java/java/util/Scanner.java
@@ -76,7 +76,7 @@
     private static final Pattern MULTI_LINE_TERMINATOR = Pattern.compile("(" + NL + ")+");
 
     // Pattern used to recognize a line with a line terminator.
-    private static final Pattern LINE_PATTERN = Pattern.compile(".*(" + NL + ")|.+(" + NL + ")?");
+    private static final Pattern LINE_PATTERN = Pattern.compile(".*(" + NL + ")|.+$");
 
     // The pattern matches anything.
     private static final Pattern ANY_PATTERN = Pattern.compile("(?s).*");
@@ -86,7 +86,7 @@
     // The input source of scanner.
     private Readable input;
 
-    private CharBuffer buffer;
+    private CharBuffer buffer = CharBuffer.allocate(1024);
 
     private Pattern delimiter = DEFAULT_DELIMITER;
 
@@ -125,17 +125,6 @@
     private int cachedIntegerPatternRadix = -1;
     private Pattern cachedIntegerPattern = null;
 
-    private enum DataType {
-        /*
-         * Stands for Integer
-         */
-        INT,
-        /*
-         * Stands for Float
-         */
-        FLOAT;
-    }
-
     /**
      * Creates a {@code Scanner} with the specified {@code File} as input. The default charset
      * is applied when reading the file.
@@ -171,12 +160,11 @@
             throw new IllegalArgumentException("charsetName == null");
         }
         try {
-            input = new InputStreamReader(fis, charsetName);
+            setInput(new InputStreamReader(fis, charsetName));
         } catch (UnsupportedEncodingException e) {
             IoUtils.closeQuietly(fis);
             throw new IllegalArgumentException(e.getMessage());
         }
-        initialization();
     }
 
     /**
@@ -186,8 +174,7 @@
      *            the string to be scanned.
      */
     public Scanner(String src) {
-        input = new StringReader(src);
-        initialization();
+        setInput(new StringReader(src));
     }
 
     /**
@@ -217,11 +204,10 @@
             throw new NullPointerException("src == null");
         }
         try {
-            input = new InputStreamReader(src, charsetName);
+            setInput(new InputStreamReader(src, charsetName));
         } catch (UnsupportedEncodingException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
-        initialization();
     }
 
     /**
@@ -234,8 +220,7 @@
         if (src == null) {
             throw new NullPointerException("src == null");
         }
-        input = src;
-        initialization();
+        setInput(src);
     }
 
     /**
@@ -267,8 +252,13 @@
         if (charsetName == null) {
             throw new IllegalArgumentException("charsetName == null");
         }
-        input = Channels.newReader(src, charsetName);
-        initialization();
+        setInput(Channels.newReader(src, charsetName));
+    }
+
+    private void setInput(Readable input) {
+        this.input = input;
+        buffer.limit(0);
+        matcher = delimiter.matcher(buffer);
     }
 
     /**
@@ -454,20 +444,12 @@
         matcher.usePattern(pattern);
 
         String result = null;
-        int findEndIndex = 0;
-        int horizonEndIndex = 0;
-        if (horizon == 0) {
-            horizonEndIndex = Integer.MAX_VALUE;
-        } else {
-            horizonEndIndex = findStartIndex + horizon;
-        }
+        int horizonEndIndex = (horizon == 0) ? Integer.MAX_VALUE : findStartIndex + horizon;
         while (true) {
-            findEndIndex = bufferLength;
-
             // If horizon > 0, then search up to
             // min( bufferLength, findStartIndex + horizon).
             // Otherwise search until readable is exhausted.
-            findEndIndex = Math.min(horizonEndIndex, bufferLength);
+            int findEndIndex = Math.min(horizonEndIndex, bufferLength);
             // If horizon == 0, consider horizon as always outside buffer.
             boolean isHorizonInBuffer = (horizonEndIndex <= bufferLength);
             // First, try to find pattern within buffer. If pattern can not be
@@ -475,7 +457,7 @@
             // util horizonEndIndex is exceeded or no more input left.
             matcher.region(findStartIndex, findEndIndex);
             if (matcher.find()) {
-                if (isHorizonInBuffer || inputExhausted) {
+                if ((horizon == 0 && !matcher.hitEnd()) || isHorizonInBuffer || inputExhausted) {
                     result = matcher.group();
                     break;
                 }
@@ -644,7 +626,7 @@
         boolean isBigIntegerValue = false;
         if (hasNext(integerPattern)) {
             String intString = matcher.group();
-            intString = removeLocaleInfo(intString, DataType.INT);
+            intString = removeLocaleInfo(intString, int.class);
             try {
                 cachedNextValue = new BigInteger(intString, radix);
                 isBigIntegerValue = true;
@@ -698,7 +680,7 @@
         boolean isByteValue = false;
         if (hasNext(integerPattern)) {
             String intString = matcher.group();
-            intString = removeLocaleInfo(intString, DataType.INT);
+            intString = removeLocaleInfo(intString, int.class);
             try {
                 cachedNextValue = Byte.valueOf(intString, radix);
                 isByteValue = true;
@@ -790,7 +772,7 @@
         boolean isIntValue = false;
         if (hasNext(integerPattern)) {
             String intString = matcher.group();
-            intString = removeLocaleInfo(intString, DataType.INT);
+            intString = removeLocaleInfo(intString, int.class);
             try {
                 cachedNextValue = Integer.valueOf(intString, radix);
                 isIntValue = true;
@@ -802,39 +784,16 @@
     }
 
     /**
-     * Returns whether there is a line terminator in the input.
+     * Returns true if there is a line terminator in the input.
      * This method may block.
      *
-     * @return {@code true} if there is a line terminator in the input,
-     *         otherwise, {@code false}.
-     * @throws IllegalStateException
-     *             if the {@code Scanner} is closed.
+     * @throws IllegalStateException if this {@code Scanner} is closed.
      */
     public boolean hasNextLine() {
-        checkOpen();
-        matcher.usePattern(LINE_PATTERN);
-        matcher.region(findStartIndex, bufferLength);
-
-        boolean hasNextLine = false;
-        while (true) {
-            if (matcher.find()) {
-                if (inputExhausted || matcher.end() != bufferLength) {
-                    matchSuccessful = true;
-                    hasNextLine = true;
-                    break;
-                }
-            } else {
-                if (inputExhausted) {
-                    matchSuccessful = false;
-                    break;
-                }
-            }
-            if (!inputExhausted) {
-                readMore();
-                resetMatcher();
-            }
-        }
-        return hasNextLine;
+        saveCurrentStatus();
+        String result = findWithinHorizon(LINE_PATTERN, 0);
+        recoverPreviousStatus();
+        return result != null;
     }
 
     /**
@@ -867,7 +826,7 @@
         boolean isLongValue = false;
         if (hasNext(integerPattern)) {
             String intString = matcher.group();
-            intString = removeLocaleInfo(intString, DataType.INT);
+            intString = removeLocaleInfo(intString, int.class);
             try {
                 cachedNextValue = Long.valueOf(intString, radix);
                 isLongValue = true;
@@ -908,7 +867,7 @@
         boolean isShortValue = false;
         if (hasNext(integerPattern)) {
             String intString = matcher.group();
-            intString = removeLocaleInfo(intString, DataType.INT);
+            intString = removeLocaleInfo(intString, int.class);
             try {
                 cachedNextValue = Short.valueOf(intString, radix);
                 isShortValue = true;
@@ -1122,7 +1081,7 @@
         }
         Pattern integerPattern = getIntegerPattern(radix);
         String intString = next(integerPattern);
-        intString = removeLocaleInfo(intString, DataType.INT);
+        intString = removeLocaleInfo(intString, int.class);
         BigInteger bigIntegerValue;
         try {
             bigIntegerValue = new BigInteger(intString, radix);
@@ -1200,7 +1159,7 @@
         }
         Pattern integerPattern = getIntegerPattern(radix);
         String intString = next(integerPattern);
-        intString = removeLocaleInfo(intString, DataType.INT);
+        intString = removeLocaleInfo(intString, int.class);
         byte byteValue = 0;
         try {
             byteValue = Byte.parseByte(intString, radix);
@@ -1350,7 +1309,7 @@
         }
         Pattern integerPattern = getIntegerPattern(radix);
         String intString = next(integerPattern);
-        intString = removeLocaleInfo(intString, DataType.INT);
+        intString = removeLocaleInfo(intString, int.class);
         int intValue = 0;
         try {
             intValue = Integer.parseInt(intString, radix);
@@ -1380,6 +1339,7 @@
 
         matcher.usePattern(LINE_PATTERN);
         matcher.region(findStartIndex, bufferLength);
+
         String result = null;
         while (true) {
             if (matcher.find()) {
@@ -1461,7 +1421,7 @@
         }
         Pattern integerPattern = getIntegerPattern(radix);
         String intString = next(integerPattern);
-        intString = removeLocaleInfo(intString, DataType.INT);
+        intString = removeLocaleInfo(intString, int.class);
         long longValue = 0;
         try {
             longValue = Long.parseLong(intString, radix);
@@ -1523,7 +1483,7 @@
         }
         Pattern integerPattern = getIntegerPattern(radix);
         String intString = next(integerPattern);
-        intString = removeLocaleInfo(intString, DataType.INT);
+        intString = removeLocaleInfo(intString, int.class);
         short shortValue = 0;
         try {
             shortValue = Short.parseShort(intString, radix);
@@ -1689,15 +1649,6 @@
         throw new UnsupportedOperationException();
     }
 
-    /*
-     * Initialize some components.
-     */
-    private void initialization() {
-        buffer = CharBuffer.allocate(1024);
-        buffer.limit(0);
-        matcher = delimiter.matcher(buffer);
-    }
-
     private void checkOpen() {
         if (closed) {
             throw new IllegalStateException();
@@ -1719,6 +1670,8 @@
         } else {
             matcher.reset(buffer);
         }
+        matcher.useTransparentBounds(true);
+        matcher.useAnchoringBounds(false);
         matcher.region(findStartIndex, bufferLength);
     }
 
@@ -1852,17 +1805,17 @@
         if ((exponentIndex = floatString.indexOf('e')) != -1 || (exponentIndex = floatString.indexOf('E')) != -1) {
             String decimalNumeralString = floatString.substring(0, exponentIndex);
             String exponentString = floatString.substring(exponentIndex + 1, floatString.length());
-            decimalNumeralString = removeLocaleInfo(decimalNumeralString, DataType.FLOAT);
+            decimalNumeralString = removeLocaleInfo(decimalNumeralString, float.class);
             return decimalNumeralString + "e" + exponentString;
         }
-        return removeLocaleInfo(floatString, DataType.FLOAT);
+        return removeLocaleInfo(floatString, float.class);
     }
 
     /*
      * Remove the locale specific prefixes, group separators, and locale
      * specific suffixes from input string
      */
-    private String removeLocaleInfo(String token, DataType type) {
+    private String removeLocaleInfo(String token, Class<?> type) {
         DecimalFormatSymbols dfs = decimalFormat.getDecimalFormatSymbols();
 
         StringBuilder tokenBuilder = new StringBuilder(token);
@@ -1877,14 +1830,13 @@
         String decimalSeparator = String.valueOf(dfs.getDecimalSeparator());
         separatorIndex = tokenBuilder.indexOf(decimalSeparator);
         StringBuilder result = new StringBuilder("");
-        if (DataType.INT == type) {
+        if (type == int.class) {
             for (int i = 0; i < tokenBuilder.length(); i++) {
                 if (Character.digit(tokenBuilder.charAt(i), Character.MAX_RADIX) != -1) {
                     result.append(tokenBuilder.charAt(i));
                 }
             }
-        }
-        if (DataType.FLOAT == type) {
+        } else if (type == float.class) {
             if (tokenBuilder.toString().equals(dfs.getNaN())) {
                 result.append("NaN");
             } else if (tokenBuilder.toString().equals(dfs.getInfinity())) {
@@ -1896,6 +1848,8 @@
                     }
                 }
             }
+        } else {
+            throw new AssertionError("Unsupported type: " + type);
         }
         // Token is NaN or Infinity
         if (result.length() == 0) {
@@ -1968,7 +1922,7 @@
         }
         tokenEndIndex = findDelimiterAfter();
         // If the second delimiter is not found
-        if (-1 == tokenEndIndex) {
+        if (tokenEndIndex == -1) {
             // Just first Delimiter Exists
             if (findStartIndex == bufferLength) {
                 return false;
@@ -2022,7 +1976,7 @@
         int tokenEndIndex;
         boolean setSuccess = false;
         // If no delimiter exists, but something exists in this scanner
-        if (-1 == findIndex && preStartIndex != bufferLength) {
+        if (findIndex == -1 && preStartIndex != bufferLength) {
             tokenStartIndex = preStartIndex;
             tokenEndIndex = bufferLength;
             findStartIndex = bufferLength;
@@ -2088,20 +2042,16 @@
         } catch (IOException e) {
             // Consider the scenario: readable puts 4 chars into
             // buffer and then an IOException is thrown out. In this case,
-            // buffer is
-            // actually grown, but readable.read() will never return.
+            // buffer is actually grown, but readable.read() will never return.
             bufferLength = buffer.position();
-            /*
-             * Uses -1 to record IOException occurring, and no more input can be
-             * read.
-             */
+            // Use -1 to record IOException occurring, and no more input can be read.
             readCount = -1;
             lastIOException = e;
         }
 
         buffer.flip();
         buffer.position(oldPosition);
-        if (-1 == readCount) {
+        if (readCount == -1) {
             inputExhausted = true;
         } else {
             bufferLength = readCount + bufferLength;
diff --git a/luni/src/main/java/java/util/jar/ManifestReader.java b/luni/src/main/java/java/util/jar/ManifestReader.java
index 2faaa90..8760884 100644
--- a/luni/src/main/java/java/util/jar/ManifestReader.java
+++ b/luni/src/main/java/java/util/jar/ManifestReader.java
@@ -17,6 +17,7 @@
 
 package java.util.jar;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
@@ -31,7 +32,7 @@
     // but a manifest might have thousands of entries.
     private final HashMap<String, Attributes.Name> attributeNameCache = new HashMap<String, Attributes.Name>();
 
-    private final UnsafeByteSequence valueBuffer = new UnsafeByteSequence(80);
+    private final ByteArrayOutputStream valueBuffer = new ByteArrayOutputStream(80);
 
     private final byte[] buf;
 
@@ -141,7 +142,7 @@
         boolean lastCr = false;
         int mark = pos;
         int last = pos;
-        valueBuffer.rewind();
+        valueBuffer.reset();
         while (pos < buf.length) {
             byte next = buf[pos++];
             switch (next) {
@@ -175,6 +176,8 @@
         }
 
         valueBuffer.write(buf, mark, last - mark);
-        value = valueBuffer.toString(StandardCharsets.UTF_8);
+        // A bit frustrating that that Charset.forName will be called
+        // again.
+        value = valueBuffer.toString(StandardCharsets.UTF_8.name());
     }
 }
diff --git a/luni/src/main/java/java/util/regex/Matcher.java b/luni/src/main/java/java/util/regex/Matcher.java
index d58d092..02531d7 100644
--- a/luni/src/main/java/java/util/regex/Matcher.java
+++ b/luni/src/main/java/java/util/regex/Matcher.java
@@ -557,12 +557,10 @@
     }
 
     /**
-     * Indicates whether this matcher has anchoring bounds enabled. When
+     * Returns true if this matcher has anchoring bounds enabled. When
      * anchoring bounds are enabled, the start and end of the input match the
      * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled
      * by default.
-     *
-     * @return true if (and only if) the {@code Matcher} uses anchoring bounds.
      */
     public boolean hasAnchoringBounds() {
         return anchoringBounds;
@@ -600,12 +598,10 @@
     }
 
     /**
-     * Indicates whether this matcher has transparent bounds enabled. When
+     * Returns true if this matcher has transparent bounds enabled. When
      * transparent bounds are enabled, the parts of the input outside the region
      * are subject to lookahead and lookbehind, otherwise they are not.
      * Transparent bounds are disabled by default.
-     *
-     * @return true if (and only if) the {@code Matcher} uses anchoring bounds.
      */
     public boolean hasTransparentBounds() {
         return transparentBounds;
@@ -628,8 +624,10 @@
     }
 
     /**
-     * Returns true if and only if more input might change a successful match into an
-     * unsuccessful one.
+     * Returns true if the most recent match succeeded and additional input could cause
+     * it to fail. If this method returns false and a match was found, then more input
+     * might change the match but the match won't be lost. If a match was not found,
+     * then requireEnd has no meaning.
      */
     public boolean requireEnd() {
         synchronized (this) {
@@ -638,7 +636,9 @@
     }
 
     /**
-     * Returns true if and only if the last match hit the end of the input.
+     * Returns true if the most recent matching operation attempted to access
+     * additional text beyond the available input, meaning that additional input
+     * could change the results of the match.
      */
     public boolean hitEnd() {
         synchronized (this) {
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index e91c73e..f64c717 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -343,19 +343,21 @@
 
     /*
      * Internal constructor.  Creates a new ZipEntry by reading the
-     * Central Directory Entry from "in", which must be positioned at
-     * the CDE signature.
+     * Central Directory Entry (CDE) from "in", which must be positioned
+     * at the CDE signature.
      *
-     * On exit, "in" will be positioned at the start of the next entry.
+     * On exit, "in" will be positioned at the start of the next entry
+     * in the Central Directory.
      */
-    ZipEntry(byte[] hdrBuf, InputStream in) throws IOException {
-        Streams.readFully(in, hdrBuf, 0, hdrBuf.length);
+    ZipEntry(byte[] cdeHdrBuf, InputStream cdStream) throws IOException {
+        Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
 
-        BufferIterator it = HeapBufferIterator.iterator(hdrBuf, 0, hdrBuf.length, ByteOrder.LITTLE_ENDIAN);
+        BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
+                ByteOrder.LITTLE_ENDIAN);
 
         int sig = it.readInt();
         if (sig != CENSIG) {
-             throw new ZipException("Central Directory Entry not found");
+            ZipFile.throwZipException("Central Directory Entry", sig);
         }
 
         it.seek(8);
@@ -383,24 +385,24 @@
         localHeaderRelOffset = ((long) it.readInt()) & 0xffffffffL;
 
         byte[] nameBytes = new byte[nameLength];
-        Streams.readFully(in, nameBytes, 0, nameBytes.length);
+        Streams.readFully(cdStream, nameBytes, 0, nameBytes.length);
         if (containsNulByte(nameBytes)) {
             throw new ZipException("Filename contains NUL byte: " + Arrays.toString(nameBytes));
         }
         name = new String(nameBytes, 0, nameBytes.length, StandardCharsets.UTF_8);
 
+        if (extraLength > 0) {
+            extra = new byte[extraLength];
+            Streams.readFully(cdStream, extra, 0, extraLength);
+        }
+
         // The RI has always assumed UTF-8. (If GPBF_UTF8_FLAG isn't set, the encoding is
         // actually IBM-437.)
         if (commentByteCount > 0) {
             byte[] commentBytes = new byte[commentByteCount];
-            Streams.readFully(in, commentBytes, 0, commentByteCount);
+            Streams.readFully(cdStream, commentBytes, 0, commentByteCount);
             comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
         }
-
-        if (extraLength > 0) {
-            extra = new byte[extraLength];
-            Streams.readFully(in, extra, 0, extraLength);
-        }
     }
 
     private static boolean containsNulByte(byte[] bytes) {
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index 47bc48e..c25bbc1 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -20,7 +20,6 @@
 import dalvik.system.CloseGuard;
 import java.io.BufferedInputStream;
 import java.io.Closeable;
-import java.io.EOFException;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.IOException;
@@ -40,7 +39,7 @@
  * the zip file's central directory up front (from the constructor), but if you're using
  * {@link #getEntry} to look up multiple files by name, you get the benefit of this index.
  *
- * <p>If you only want to iterate through all the files (using {@link #entries}, you should
+ * <p>If you only want to iterate through all the files (using {@link #entries()}, you should
  * consider {@link ZipInputStream}, which provides stream-like read access to a zip file and
  * has a lower up-front cost because you don't pay to build an in-memory index.
  *
@@ -274,11 +273,19 @@
         RandomAccessFile localRaf = raf;
         synchronized (localRaf) {
             // We don't know the entry data's start position. All we have is the
-            // position of the entry's local header. At position 6 we find the
-            // General Purpose Bit Flag.
+            // position of the entry's local header.
             // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
-            RAFStream rafStream= new RAFStream(localRaf, entry.localHeaderRelOffset + 6);
+            RAFStream rafStream = new RAFStream(localRaf, entry.localHeaderRelOffset);
             DataInputStream is = new DataInputStream(rafStream);
+
+            final int localMagic = Integer.reverseBytes(is.readInt());
+            if (localMagic != LOCSIG) {
+                throwZipException("Local File Header", localMagic);
+            }
+
+            is.skipBytes(2);
+
+            // At position 6 we find the General Purpose Bit Flag.
             int gpbf = Short.reverseBytes(is.readShort()) & 0xffff;
             if ((gpbf & ZipFile.GPBF_UNSUPPORTED_MASK) != 0) {
                 throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
@@ -294,14 +301,13 @@
             // Skip the variable-size file name and extra field data.
             rafStream.skip(fileNameLength + extraFieldLength);
 
-            // The compressed or stored file data follows immediately after.
-            if (entry.compressionMethod == ZipEntry.DEFLATED) {
-                rafStream.length = rafStream.offset + entry.compressedSize;
+            if (entry.compressionMethod == ZipEntry.STORED) {
+                rafStream.endOffset = rafStream.offset + entry.size;
+                return rafStream;
+            } else {
+                rafStream.endOffset = rafStream.offset + entry.compressedSize;
                 int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L));
                 return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry);
-            } else {
-                rafStream.length = rafStream.offset + entry.size;
-                return rafStream;
             }
         }
     }
@@ -349,21 +355,26 @@
             throw new ZipException("File too short to be a zip file: " + raf.length());
         }
 
+        raf.seek(0);
+        final int headerMagic = Integer.reverseBytes(raf.readInt());
+        if (headerMagic != LOCSIG) {
+            throw new ZipException("Not a zip archive");
+        }
+
         long stopOffset = scanOffset - 65536;
         if (stopOffset < 0) {
             stopOffset = 0;
         }
 
-        final int ENDHEADERMAGIC = 0x06054b50;
         while (true) {
             raf.seek(scanOffset);
-            if (Integer.reverseBytes(raf.readInt()) == ENDHEADERMAGIC) {
+            if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {
                 break;
             }
 
             scanOffset--;
             if (scanOffset < stopOffset) {
-                throw new ZipException("EOCD not found; not a zip file?");
+                throw new ZipException("End Of Central Directory signature not found");
             }
         }
 
@@ -383,7 +394,7 @@
         int commentLength = it.readShort() & 0xffff;
 
         if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
-            throw new ZipException("spanned archives not supported");
+            throw new ZipException("Spanned archives not supported");
         }
 
         if (commentLength > 0) {
@@ -401,6 +412,9 @@
         byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.
         for (int i = 0; i < numEntries; ++i) {
             ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream);
+            if (newEntry.localHeaderRelOffset >= centralDirOffset) {
+                throw new ZipException("Local file header offset is after central directory");
+            }
             String entryName = newEntry.getName();
             if (entries.put(entryName, newEntry) != null) {
                 throw new ZipException("Duplicate entry name: " + entryName);
@@ -408,6 +422,11 @@
         }
     }
 
+    static void throwZipException(String msg, int magic) throws ZipException {
+        final String hexString = IntegralToString.intToHexString(magic, true, 8);
+        throw new ZipException(msg + " signature not found; was " + hexString);
+    }
+
     /**
      * Wrap a stream around a RandomAccessFile.  The RandomAccessFile is shared
      * among all streams returned by getInputStream(), so we have to synchronize
@@ -418,17 +437,17 @@
      */
     static class RAFStream extends InputStream {
         private final RandomAccessFile sharedRaf;
-        private long length;
+        private long endOffset;
         private long offset;
 
         public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException {
             sharedRaf = raf;
             offset = initialOffset;
-            length = raf.length();
+            endOffset = raf.length();
         }
 
         @Override public int available() throws IOException {
-            return (offset < length ? 1 : 0);
+            return (offset < endOffset ? 1 : 0);
         }
 
         @Override public int read() throws IOException {
@@ -437,10 +456,11 @@
 
         @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
             synchronized (sharedRaf) {
-                sharedRaf.seek(offset);
-                if (byteCount > length - offset) {
-                    byteCount = (int) (length - offset);
+                final long length = endOffset - offset;
+                if (byteCount > length) {
+                    byteCount = (int) length;
                 }
+                sharedRaf.seek(offset);
                 int count = sharedRaf.read(buffer, byteOffset, byteCount);
                 if (count > 0) {
                     offset += count;
@@ -452,8 +472,8 @@
         }
 
         @Override public long skip(long byteCount) throws IOException {
-            if (byteCount > length - offset) {
-                byteCount = length - offset;
+            if (byteCount > endOffset - offset) {
+                byteCount = endOffset - offset;
             }
             offset += byteCount;
             return byteCount;
@@ -461,7 +481,7 @@
 
         public int fill(Inflater inflater, int nativeEndBufSize) throws IOException {
             synchronized (sharedRaf) {
-                int len = Math.min((int) (length - offset), nativeEndBufSize);
+                int len = Math.min((int) (endOffset - offset), nativeEndBufSize);
                 int cnt = inflater.setFileInput(sharedRaf.getFD(), offset, nativeEndBufSize);
                 // setFileInput read from the file, so we need to get the OS and RAFStream back
                 // in sync...
@@ -481,8 +501,19 @@
         }
 
         @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-            int i = super.read(buffer, byteOffset, byteCount);
-            if (i != -1) {
+            final int i;
+            try {
+                i = super.read(buffer, byteOffset, byteCount);
+            } catch (IOException e) {
+                throw new IOException("Error reading data for " + entry.getName() + " near offset "
+                        + bytesRead, e);
+            }
+            if (i == -1) {
+                if (entry.size != bytesRead) {
+                    throw new IOException("Size mismatch on inflated file: " + bytesRead + " vs "
+                            + entry.size);
+                }
+            } else {
                 bytesRead += i;
             }
             return i;
diff --git a/luni/src/main/java/javax/crypto/CipherInputStream.java b/luni/src/main/java/javax/crypto/CipherInputStream.java
index a59a425..f2f59c1 100644
--- a/luni/src/main/java/javax/crypto/CipherInputStream.java
+++ b/luni/src/main/java/javax/crypto/CipherInputStream.java
@@ -104,7 +104,7 @@
                 try {
                     outputLength = cipher.doFinal(outputBuffer, 0);
                 } catch (Exception e) {
-                    throw new IOException(e.getMessage());
+                    throw new IOException("Error while finalizing cipher", e);
                 }
                 finished = true;
                 break;
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
index a3e1e5e..ab9085f 100644
--- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java
+++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
@@ -19,6 +19,7 @@
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.TimeZone;
+import libcore.util.BasicLruCache;
 
 /**
  * Exposes icu4c's DateIntervalFormat.
@@ -27,22 +28,37 @@
 
   // These are all public API in DateUtils. There are others, but they're either for use with
   // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
-  // or have never been implemented anyway (like FORMAT_NO_YEAR).
-  public static final int FORMAT_SHOW_TIME = 0x00001;
-  public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
-  public static final int FORMAT_SHOW_YEAR = 0x00004;
-  public static final int FORMAT_SHOW_DATE = 0x00010;
-  public static final int FORMAT_NO_MONTH_DAY = 0x00020;
-  public static final int FORMAT_12HOUR = 0x00040;
-  public static final int FORMAT_24HOUR = 0x00080;
-  public static final int FORMAT_UTC = 0x02000;
-  public static final int FORMAT_ABBREV_TIME = 0x04000;
+  // or have never been implemented anyway.
+  public static final int FORMAT_SHOW_TIME      = 0x00001;
+  public static final int FORMAT_SHOW_WEEKDAY   = 0x00002;
+  public static final int FORMAT_SHOW_YEAR      = 0x00004;
+  public static final int FORMAT_NO_YEAR        = 0x00008;
+  public static final int FORMAT_SHOW_DATE      = 0x00010;
+  public static final int FORMAT_NO_MONTH_DAY   = 0x00020;
+  public static final int FORMAT_12HOUR         = 0x00040;
+  public static final int FORMAT_24HOUR         = 0x00080;
+  public static final int FORMAT_UTC            = 0x02000;
+  public static final int FORMAT_ABBREV_TIME    = 0x04000;
   public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
-  public static final int FORMAT_ABBREV_MONTH = 0x10000;
-  public static final int FORMAT_NUMERIC_DATE = 0x20000;
-  public static final int FORMAT_ABBREV_ALL = 0x80000;
+  public static final int FORMAT_ABBREV_MONTH   = 0x10000;
+  public static final int FORMAT_NUMERIC_DATE   = 0x20000;
+  public static final int FORMAT_ABBREV_ALL     = 0x80000;
 
-  // TODO: check whether icu4c's DateIntervalFormat is expensive enough to warrant a native peer.
+  private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
+  private static final int EPOCH_JULIAN_DAY = 2440588;
+
+  private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+  static class FormatterCache extends BasicLruCache<String, Long> {
+    FormatterCache() {
+      super(8);
+    }
+
+    protected void entryEvicted(String key, Long value) {
+      destroyDateIntervalFormat(value);
+    }
+  };
+
   private DateIntervalFormat() {
   }
 
@@ -58,11 +74,6 @@
   // This is our slightly more sensible internal API. (A truly sane replacement would take a
   // skeleton instead of int flags.)
   public static String formatDateRange(Locale locale, TimeZone tz, long startMs, long endMs, int flags) {
-    String skeleton = toSkeleton(tz, startMs, endMs, flags);
-    return formatDateInterval(skeleton, locale.toString(), tz.getID(), startMs, endMs);
-  }
-
-  private static String toSkeleton(TimeZone tz, long startMs, long endMs, int flags) {
     Calendar startCalendar = Calendar.getInstance(tz);
     startCalendar.setTimeInMillis(startMs);
 
@@ -74,6 +85,36 @@
       endCalendar.setTimeInMillis(endMs);
     }
 
+    boolean endsAtMidnight = isMidnight(endCalendar);
+
+    // If we're not showing the time or the start and end times are on the same day, and the
+    // end time is midnight, fudge the end date so we don't count the day that's about to start.
+    // This is not the behavior of icu4c's DateIntervalFormat, but it's the historical behavior
+    // of Android's DateUtils.formatDateRange.
+    if (startMs != endMs && endsAtMidnight &&
+        ((flags & FORMAT_SHOW_TIME) == 0 || julianDay(startCalendar) == julianDay(endCalendar))) {
+      endCalendar.roll(Calendar.DAY_OF_MONTH, false);
+      endMs -= DAY_IN_MS;
+    }
+
+    String skeleton = toSkeleton(startCalendar, endCalendar, flags);
+    synchronized (CACHED_FORMATTERS) {
+      return formatDateInterval(getFormatter(skeleton, locale.toString(), tz.getID()), startMs, endMs);
+    }
+  }
+
+  private static long getFormatter(String skeleton, String localeName, String tzName) {
+    String key = skeleton + "\t" + localeName + "\t" + tzName;
+    Long formatter = CACHED_FORMATTERS.get(key);
+    if (formatter != null) {
+      return formatter;
+    }
+    long address = createDateIntervalFormat(skeleton, localeName, tzName);
+    CACHED_FORMATTERS.put(key, address);
+    return address;
+  }
+
+  private static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
     if ((flags & FORMAT_ABBREV_ALL) != 0) {
       flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
     }
@@ -96,8 +137,18 @@
     } else if ((flags & FORMAT_12HOUR) != 0) {
       timePart = "h";
     }
-    if ((flags & FORMAT_ABBREV_TIME) == 0 || !onTheHour(startCalendar) || !onTheHour(endCalendar)) {
-      timePart = timePart + "m";
+
+    // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
+    // never makes sense to leave out the minutes), include minutes. This gets us times like
+    // "4 PM" while avoiding times like "16" (for "16:00").
+    if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
+      timePart += "m";
+    } else {
+      // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
+      // if they're not both "00".
+      if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
+        timePart = timePart + "m";
+      }
     }
 
     if (fallOnDifferentDates(startCalendar, endCalendar)) {
@@ -113,6 +164,17 @@
       flags |= FORMAT_SHOW_DATE;
     }
 
+    // If we've been asked to show the date, work out whether we think we should show the year.
+    if ((flags & FORMAT_SHOW_DATE) != 0) {
+      if ((flags & FORMAT_SHOW_YEAR) != 0) {
+        // The caller explicitly wants us to show the year.
+      } else if ((flags & FORMAT_NO_YEAR) != 0) {
+        // The caller explicitly doesn't want us to show the year, even if we otherwise would.
+      } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
+        flags |= FORMAT_SHOW_YEAR;
+      }
+    }
+
     StringBuilder builder = new StringBuilder();
     if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
       if ((flags & FORMAT_SHOW_YEAR) != 0) {
@@ -132,6 +194,13 @@
     return builder.toString();
   }
 
+  private static boolean isMidnight(Calendar c) {
+    return c.get(Calendar.HOUR_OF_DAY) == 0 &&
+        c.get(Calendar.MINUTE) == 0 &&
+        c.get(Calendar.SECOND) == 0 &&
+        c.get(Calendar.MILLISECOND) == 0;
+  }
+
   private static boolean onTheHour(Calendar c) {
     return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
   }
@@ -146,5 +215,21 @@
     return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
   }
 
-  private static native String formatDateInterval(String skeleton, String localeName, String timeZoneName, long fromDate, long toDate);
+  private static boolean fallInSameYear(Calendar c1, Calendar c2) {
+    return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
+  }
+
+  private static boolean isThisYear(Calendar c) {
+    Calendar now = Calendar.getInstance(c.getTimeZone());
+    return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
+  }
+
+  private static int julianDay(Calendar c) {
+    long utcMs = c.get(Calendar.MILLISECOND) + c.get(Calendar.ZONE_OFFSET);
+    return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
+  }
+
+  private static native long createDateIntervalFormat(String skeleton, String localeName, String tzName);
+  private static native void destroyDateIntervalFormat(long address);
+  private static native String formatDateInterval(long address, long fromDate, long toDate);
 }
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 6a72d44..76d9c54 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -18,218 +18,228 @@
 
 import java.util.LinkedHashSet;
 import java.util.Locale;
+import libcore.util.BasicLruCache;
 
 /**
  * Makes ICU data accessible to Java.
  */
 public final class ICU {
-    /**
-     * Cache for ISO language names.
-     */
-    private static String[] isoLanguages;
+  private static final BasicLruCache<String, String> CACHED_PATTERNS =
+      new BasicLruCache<String, String>(8);
 
-    /**
-     * Cache for ISO country names.
-     */
-    private static String[] isoCountries;
+  private static Locale[] availableLocalesCache;
 
-    /**
-     * Returns an array of ISO language names (two-letter codes), fetched either
-     * from ICU's database or from our memory cache.
-     *
-     * @return The array.
-     */
-    public static String[] getISOLanguages() {
-        if (isoLanguages == null) {
-            isoLanguages = getISOLanguagesNative();
-        }
-        return isoLanguages.clone();
+  private static String[] isoCountries;
+
+  private static String[] isoLanguages;
+
+  /**
+   * Returns an array of two-letter ISO 639-1 language codes, either from ICU or our cache.
+   */
+  public static String[] getISOLanguages() {
+    if (isoLanguages == null) {
+      isoLanguages = getISOLanguagesNative();
     }
+    return isoLanguages.clone();
+  }
 
-    /**
-     * Returns an array of ISO country names (two-letter codes), fetched either
-     * from ICU's database or from our memory cache.
-     *
-     * @return The array.
-     */
-    public static String[] getISOCountries() {
-        if (isoCountries == null) {
-            isoCountries = getISOCountriesNative();
-        }
-        return isoCountries.clone();
+  /**
+   * Returns an array of two-letter ISO 3166 country codes, either from ICU or our cache.
+   */
+  public static String[] getISOCountries() {
+    if (isoCountries == null) {
+      isoCountries = getISOCountriesNative();
     }
+    return isoCountries.clone();
+  }
 
-    /**
-     * Returns the appropriate {@code Locale} given a {@code String} of the form returned
-     * by {@code toString}. This is very lenient, and doesn't care what's between the underscores:
-     * this method can parse strings that {@code Locale.toString} won't produce.
-     * Used to remove duplication.
-     */
-    public static Locale localeFromString(String localeName) {
-        int first = localeName.indexOf('_');
-        int second = localeName.indexOf('_', first + 1);
-        if (first == -1) {
-            // Language only ("ja").
-            return new Locale(localeName);
-        } else if (second == -1) {
-            // Language and country ("ja_JP").
-            return new Locale(localeName.substring(0, first), localeName.substring(first + 1));
-        } else {
-            // Language and country and variant ("ja_JP_TRADITIONAL").
-            return new Locale(localeName.substring(0, first), localeName.substring(first + 1, second), localeName.substring(second + 1));
-        }
+  /**
+   * Returns the appropriate {@code Locale} given a {@code String} of the form returned
+   * by {@code toString}. This is very lenient, and doesn't care what's between the underscores:
+   * this method can parse strings that {@code Locale.toString} won't produce.
+   * Used to remove duplication.
+   */
+  public static Locale localeFromString(String localeName) {
+    int first = localeName.indexOf('_');
+    int second = localeName.indexOf('_', first + 1);
+    if (first == -1) {
+      // Language only ("ja").
+      return new Locale(localeName);
+    } else if (second == -1) {
+      // Language and country ("ja_JP").
+      String language = localeName.substring(0, first);
+      String country = localeName.substring(first + 1);
+      return new Locale(language, country);
+    } else {
+      // Language and country and variant ("ja_JP_TRADITIONAL").
+      String language = localeName.substring(0, first);
+      String country = localeName.substring(first + 1, second);
+      String variant = localeName.substring(second + 1);
+      return new Locale(language, country, variant);
     }
+  }
 
-    public static Locale[] localesFromStrings(String[] localeNames) {
-        // We need to remove duplicates caused by the conversion of "he" to "iw", et cetera.
-        // Java needs the obsolete code, ICU needs the modern code, but we let ICU know about
-        // both so that we never need to convert back when talking to it.
-        LinkedHashSet<Locale> set = new LinkedHashSet<Locale>();
-        for (String localeName : localeNames) {
-            set.add(localeFromString(localeName));
-        }
-        return set.toArray(new Locale[set.size()]);
+  public static Locale[] localesFromStrings(String[] localeNames) {
+    // We need to remove duplicates caused by the conversion of "he" to "iw", et cetera.
+    // Java needs the obsolete code, ICU needs the modern code, but we let ICU know about
+    // both so that we never need to convert back when talking to it.
+    LinkedHashSet<Locale> set = new LinkedHashSet<Locale>();
+    for (String localeName : localeNames) {
+      set.add(localeFromString(localeName));
     }
+    return set.toArray(new Locale[set.size()]);
+  }
 
-    private static Locale[] availableLocalesCache;
-    public static Locale[] getAvailableLocales() {
-        if (availableLocalesCache == null) {
-            availableLocalesCache = localesFromStrings(getAvailableLocalesNative());
-        }
-        return availableLocalesCache.clone();
+  public static Locale[] getAvailableLocales() {
+    if (availableLocalesCache == null) {
+      availableLocalesCache = localesFromStrings(getAvailableLocalesNative());
     }
+    return availableLocalesCache.clone();
+  }
 
-    public static Locale[] getAvailableBreakIteratorLocales() {
-        return localesFromStrings(getAvailableBreakIteratorLocalesNative());
-    }
+  public static Locale[] getAvailableBreakIteratorLocales() {
+    return localesFromStrings(getAvailableBreakIteratorLocalesNative());
+  }
 
-    public static Locale[] getAvailableCalendarLocales() {
-        return localesFromStrings(getAvailableCalendarLocalesNative());
-    }
+  public static Locale[] getAvailableCalendarLocales() {
+    return localesFromStrings(getAvailableCalendarLocalesNative());
+  }
 
-    public static Locale[] getAvailableCollatorLocales() {
-        return localesFromStrings(getAvailableCollatorLocalesNative());
-    }
+  public static Locale[] getAvailableCollatorLocales() {
+    return localesFromStrings(getAvailableCollatorLocalesNative());
+  }
 
-    public static Locale[] getAvailableDateFormatLocales() {
-        return localesFromStrings(getAvailableDateFormatLocalesNative());
-    }
+  public static Locale[] getAvailableDateFormatLocales() {
+    return localesFromStrings(getAvailableDateFormatLocalesNative());
+  }
 
-    public static Locale[] getAvailableDateFormatSymbolsLocales() {
-        return getAvailableDateFormatLocales();
-    }
+  public static Locale[] getAvailableDateFormatSymbolsLocales() {
+    return getAvailableDateFormatLocales();
+  }
 
-    public static Locale[] getAvailableDecimalFormatSymbolsLocales() {
-        return getAvailableNumberFormatLocales();
-    }
+  public static Locale[] getAvailableDecimalFormatSymbolsLocales() {
+    return getAvailableNumberFormatLocales();
+  }
 
-    public static Locale[] getAvailableNumberFormatLocales() {
-        return localesFromStrings(getAvailableNumberFormatLocalesNative());
-    }
+  public static Locale[] getAvailableNumberFormatLocales() {
+    return localesFromStrings(getAvailableNumberFormatLocalesNative());
+  }
 
-    public static native String getBestDateTimePattern(String skeleton, String localeName);
-
-    public static char[] getDateFormatOrder(String pattern) {
-      char[] result = new char[3];
-      int resultIndex = 0;
-      boolean sawDay = false;
-      boolean sawMonth = false;
-      boolean sawYear = false;
-
-      for (int i = 0; i < pattern.length(); ++i) {
-        char ch = pattern.charAt(i);
-        if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') {
-          if (ch == 'd' && !sawDay) {
-            result[resultIndex++] = 'd';
-            sawDay = true;
-          } else if ((ch == 'L' || ch == 'M') && !sawMonth) {
-            result[resultIndex++] = 'M';
-            sawMonth = true;
-          } else if ((ch == 'y') && !sawYear) {
-            result[resultIndex++] = 'y';
-            sawYear = true;
-          }
-        } else if (ch == 'G') {
-          // Ignore the era specifier, if present.
-        } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
-          throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern);
-        } else if (ch == '\'') {
-          if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') {
-            ++i;
-          } else {
-            i = pattern.indexOf('\'', i + 1);
-            if (i == -1) {
-              throw new IllegalArgumentException("Bad quoting in " + pattern);
-            }
-            ++i;
-          }
-        } else {
-          // Ignore spaces and punctuation.
-        }
+  public static String getBestDateTimePattern(String skeleton, String localeName) {
+    String key = skeleton + "\t" + localeName;
+    synchronized (CACHED_PATTERNS) {
+      String pattern = CACHED_PATTERNS.get(key);
+      if (pattern == null) {
+        pattern = getBestDateTimePatternNative(skeleton, localeName);
+        CACHED_PATTERNS.put(key, pattern);
       }
-      return result;
+      return pattern;
     }
+  }
 
-    /**
-     * Returns the version of the CLDR data in use, such as "22.1.1".
-     */
-    public static native String getCldrVersion();
+  private static native String getBestDateTimePatternNative(String skeleton, String localeName);
 
-    /**
-     * Returns the icu4c version in use, such as "50.1.1".
-     */
-    public static native String getIcuVersion();
+  public static char[] getDateFormatOrder(String pattern) {
+    char[] result = new char[3];
+    int resultIndex = 0;
+    boolean sawDay = false;
+    boolean sawMonth = false;
+    boolean sawYear = false;
 
-    /**
-     * Returns the Unicode version our ICU supports, such as "6.2".
-     */
-    public static native String getUnicodeVersion();
-
-    // --- Case mapping.
-
-    public static native String toLowerCase(String s, String localeName);
-    public static native String toUpperCase(String s, String localeName);
-
-    // --- Errors.
-
-    // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU.
-    public static final int U_ZERO_ERROR = 0;
-    public static final int U_INVALID_CHAR_FOUND = 10;
-    public static final int U_TRUNCATED_CHAR_FOUND = 11;
-    public static final int U_ILLEGAL_CHAR_FOUND = 12;
-    public static final int U_BUFFER_OVERFLOW_ERROR = 15;
-
-    public static boolean U_FAILURE(int error) {
-      return error > U_ZERO_ERROR;
+    for (int i = 0; i < pattern.length(); ++i) {
+      char ch = pattern.charAt(i);
+      if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') {
+        if (ch == 'd' && !sawDay) {
+          result[resultIndex++] = 'd';
+          sawDay = true;
+        } else if ((ch == 'L' || ch == 'M') && !sawMonth) {
+          result[resultIndex++] = 'M';
+          sawMonth = true;
+        } else if ((ch == 'y') && !sawYear) {
+          result[resultIndex++] = 'y';
+          sawYear = true;
+        }
+      } else if (ch == 'G') {
+        // Ignore the era specifier, if present.
+      } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+        throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern);
+      } else if (ch == '\'') {
+        if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') {
+          ++i;
+        } else {
+          i = pattern.indexOf('\'', i + 1);
+          if (i == -1) {
+            throw new IllegalArgumentException("Bad quoting in " + pattern);
+          }
+          ++i;
+        }
+      } else {
+        // Ignore spaces and punctuation.
+      }
     }
+    return result;
+  }
 
-    // --- Native methods accessing ICU's database.
+  /**
+   * Returns the version of the CLDR data in use, such as "22.1.1".
+   */
+  public static native String getCldrVersion();
 
-    private static native String[] getAvailableBreakIteratorLocalesNative();
-    private static native String[] getAvailableCalendarLocalesNative();
-    private static native String[] getAvailableCollatorLocalesNative();
-    private static native String[] getAvailableDateFormatLocalesNative();
-    private static native String[] getAvailableLocalesNative();
-    private static native String[] getAvailableNumberFormatLocalesNative();
+  /**
+   * Returns the icu4c version in use, such as "50.1.1".
+   */
+  public static native String getIcuVersion();
 
-    public static native String[] getAvailableCurrencyCodes();
-    public static native String getCurrencyCode(String countryCode);
-    public static native String getCurrencyDisplayName(String locale, String currencyCode);
-    public static native int getCurrencyFractionDigits(String currencyCode);
-    public static native String getCurrencySymbol(String locale, String currencyCode);
+  /**
+   * Returns the Unicode version our ICU supports, such as "6.2".
+   */
+  public static native String getUnicodeVersion();
 
-    public static native String getDisplayCountryNative(String countryCode, String locale);
-    public static native String getDisplayLanguageNative(String languageCode, String locale);
-    public static native String getDisplayVariantNative(String variantCode, String locale);
+  // --- Case mapping.
 
-    public static native String getISO3CountryNative(String locale);
-    public static native String getISO3LanguageNative(String locale);
+  public static native String toLowerCase(String s, String localeName);
+  public static native String toUpperCase(String s, String localeName);
 
-    public static native String addLikelySubtags(String locale);
-    public static native String getScript(String locale);
+  // --- Errors.
 
-    private static native String[] getISOLanguagesNative();
-    private static native String[] getISOCountriesNative();
+  // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU.
+  public static final int U_ZERO_ERROR = 0;
+  public static final int U_INVALID_CHAR_FOUND = 10;
+  public static final int U_TRUNCATED_CHAR_FOUND = 11;
+  public static final int U_ILLEGAL_CHAR_FOUND = 12;
+  public static final int U_BUFFER_OVERFLOW_ERROR = 15;
 
-    static native boolean initLocaleDataImpl(String locale, LocaleData result);
+  public static boolean U_FAILURE(int error) {
+    return error > U_ZERO_ERROR;
+  }
+
+  // --- Native methods accessing ICU's database.
+
+  private static native String[] getAvailableBreakIteratorLocalesNative();
+  private static native String[] getAvailableCalendarLocalesNative();
+  private static native String[] getAvailableCollatorLocalesNative();
+  private static native String[] getAvailableDateFormatLocalesNative();
+  private static native String[] getAvailableLocalesNative();
+  private static native String[] getAvailableNumberFormatLocalesNative();
+
+  public static native String[] getAvailableCurrencyCodes();
+  public static native String getCurrencyCode(String countryCode);
+  public static native String getCurrencyDisplayName(String locale, String currencyCode);
+  public static native int getCurrencyFractionDigits(String currencyCode);
+  public static native String getCurrencySymbol(String locale, String currencyCode);
+
+  public static native String getDisplayCountryNative(String countryCode, String locale);
+  public static native String getDisplayLanguageNative(String languageCode, String locale);
+  public static native String getDisplayVariantNative(String variantCode, String locale);
+
+  public static native String getISO3CountryNative(String locale);
+  public static native String getISO3LanguageNative(String locale);
+
+  public static native String addLikelySubtags(String locale);
+  public static native String getScript(String locale);
+
+  private static native String[] getISOLanguagesNative();
+  private static native String[] getISOCountriesNative();
+
+  static native boolean initLocaleDataNative(String locale, LocaleData result);
 }
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index a6d700f..f00c30f 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -171,9 +171,15 @@
 
     private static LocaleData initLocaleData(Locale locale) {
         LocaleData localeData = new LocaleData();
-        if (!ICU.initLocaleDataImpl(locale.toString(), localeData)) {
+        if (!ICU.initLocaleDataNative(locale.toString(), localeData)) {
             throw new AssertionError("couldn't initialize LocaleData for locale " + locale);
         }
+
+        // Get the "h:mm a" and "HH:mm" 12- and 24-hour time format strings.
+        localeData.timeFormat12 = ICU.getBestDateTimePattern("hm", locale.toString());
+        localeData.timeFormat24 = ICU.getBestDateTimePattern("Hm", locale.toString());
+
+        // Fix up a couple of patterns.
         if (localeData.fullTimeFormat != null) {
             // There are some full time format patterns in ICU that use the pattern character 'v'.
             // Java doesn't accept this, so we replace it with 'z' which has about the same result
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index f7cc41f..10ef671 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -18,10 +18,11 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.io.RandomAccessFile;
 import java.net.Socket;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Random;
 import static libcore.io.OsConstants.*;
@@ -102,33 +103,15 @@
     /**
      * Returns the contents of 'path' as a byte array.
      */
-    public static byte[] readFileAsByteArray(String path) throws IOException {
-        return readFileAsBytes(path).toByteArray();
+    public static byte[] readFileAsByteArray(String absolutePath) throws IOException {
+        return new FileReader(absolutePath).readFully().toByteArray();
     }
 
     /**
      * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8.
      */
-    public static String readFileAsString(String path) throws IOException {
-        return readFileAsBytes(path).toString(StandardCharsets.UTF_8);
-    }
-
-    private static UnsafeByteSequence readFileAsBytes(String path) throws IOException {
-        RandomAccessFile f = null;
-        try {
-            f = new RandomAccessFile(path, "r");
-            UnsafeByteSequence bytes = new UnsafeByteSequence((int) f.length());
-            byte[] buffer = new byte[8192];
-            while (true) {
-                int byteCount = f.read(buffer);
-                if (byteCount == -1) {
-                    return bytes;
-                }
-                bytes.write(buffer, 0, byteCount);
-            }
-        } finally {
-            IoUtils.closeQuietly(f);
-        }
+    public static String readFileAsString(String absolutePath) throws IOException {
+        return new FileReader(absolutePath).readFully().toString(StandardCharsets.UTF_8);
     }
 
     /**
@@ -193,4 +176,100 @@
         // TODO: set InterruptedIOException.bytesTransferred
         throw new InterruptedIOException();
     }
+
+    /**
+     * A convenience class for reading the contents of a file into a {@code String}
+     * or a {@code byte[]}. This class attempts to minimize the number of allocations
+     * and copies required to read this data.
+     *
+     * For the case where we know the "true" length of a file (most ordinary files)
+     * we allocate exactly one byte[] and copy data into that. Calls to
+     * {@link #toByteArray} will then return the internal array and <b>not</b> a copy.
+     *
+     * <b>Note that an absolute path must be supplied. Expect your reads to fail
+     * if one isn't.</b>
+     */
+    private static class FileReader {
+        private FileDescriptor fd;
+        private boolean unknownLength;
+
+        private byte[] bytes;
+        private int count;
+
+        public FileReader(String absolutePath) throws IOException {
+            // We use IoBridge.open because callers might differentiate
+            // between a FileNotFoundException and a general IOException.
+            //
+            // NOTE: This costs us an additional call to fstat(2) to test whether
+            // "absolutePath" is a directory or not. We can eliminate it
+            // at the cost of copying some code from IoBridge.open.
+            try {
+                fd = IoBridge.open(absolutePath, O_RDONLY);
+            } catch (FileNotFoundException fnfe) {
+                throw fnfe;
+            }
+
+            int capacity;
+            try {
+                final StructStat stat = Libcore.os.fstat(fd);
+                // Like RAF & other APIs, we assume that the file size fits
+                // into a 32 bit integer.
+                capacity = (int) stat.st_size;
+                if (capacity == 0) {
+                    unknownLength = true;
+                    capacity = 8192;
+                }
+            } catch (ErrnoException exception) {
+                closeQuietly(fd);
+                throw exception.rethrowAsIOException();
+            }
+
+            bytes = new byte[capacity];
+        }
+
+        public FileReader readFully() throws IOException {
+            int read;
+            int capacity = bytes.length;
+            try {
+                while ((read = Libcore.os.read(fd, bytes, count, capacity - count)) != 0) {
+                    count += read;
+                    if (count == capacity) {
+                        if (unknownLength) {
+                            // If we don't know the length of this file, we need to continue
+                            // reading until we reach EOF. Double the capacity in preparation.
+                            final int newCapacity = capacity * 2;
+                            byte[] newBytes = new byte[newCapacity];
+                            System.arraycopy(bytes, 0, newBytes, 0, capacity);
+                            bytes = newBytes;
+                            capacity = newCapacity;
+                        } else {
+                            // We know the length of this file and we've read the right number
+                            // of bytes from it, return.
+                            break;
+                        }
+                    }
+                }
+
+                return this;
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            } finally {
+                closeQuietly(fd);
+            }
+        }
+
+        @FindBugsSuppressWarnings("EI_EXPOSE_REP")
+        public byte[] toByteArray() {
+            if (count == bytes.length) {
+                return bytes;
+            }
+            byte[] result = new byte[count];
+            System.arraycopy(bytes, 0, result, 0, count);
+            return result;
+        }
+
+        public String toString(Charset cs) {
+            return new String(bytes, 0, count, cs);
+        }
+    }
 }
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 6e8c168..aaa5670 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -61,6 +61,7 @@
         add("application/pgp-keys", "key");
         add("application/pgp-signature", "pgp");
         add("application/pics-rules", "prf");
+        add("application/pkix-cert", "cer");
         add("application/rar", "rar");
         add("application/rdf+xml", "rdf");
         add("application/rss+xml", "rss");
@@ -182,6 +183,7 @@
         add("application/x-nwc", "nwc");
         add("application/x-object", "o");
         add("application/x-oz-application", "oza");
+        add("application/x-pem-file", "pem");
         add("application/x-pkcs12", "p12");
         add("application/x-pkcs12", "pfx");
         add("application/x-pkcs7-certreqresp", "p7r");
@@ -205,6 +207,7 @@
         add("application/x-webarchive-xml", "webarchivexml");
         add("application/x-x509-ca-cert", "crt");
         add("application/x-x509-user-cert", "crt");
+        add("application/x-x509-server-cert", "crt");
         add("application/x-xcf", "xcf");
         add("application/x-xfig", "fig");
         add("application/xhtml+xml", "xhtml");
diff --git a/luni/src/main/java/libcore/reflect/AnnotationAccess.java b/luni/src/main/java/libcore/reflect/AnnotationAccess.java
index fe740de..064151e 100644
--- a/luni/src/main/java/libcore/reflect/AnnotationAccess.java
+++ b/luni/src/main/java/libcore/reflect/AnnotationAccess.java
@@ -187,11 +187,25 @@
         Dex dex = dexClass.getDex();
         int annotationTypeIndex = getTypeIndex(dex, annotationClass);
         if (annotationTypeIndex == -1) {
-            return null; // the dex file doesn't use this annotation
+            return null; // The dex file doesn't use this annotation.
         }
 
         int annotationSetOffset = getAnnotationSetOffset(element);
-        return getAnnotationFromAnnotationSet(dex, annotationSetOffset, annotationTypeIndex);
+        if (annotationSetOffset == 0) {
+            return null; // no annotation
+        }
+
+        Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
+        for (int i = 0, size = setIn.readInt(); i < size; i++) {
+            int annotationOffset = setIn.readInt();
+            Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item
+            com.android.dex.Annotation candidate = annotationIn.readAnnotation();
+            if (candidate.getTypeIndex() == annotationTypeIndex) {
+                return candidate;
+            }
+        }
+
+        return null; // This set doesn't contain the annotation.
     }
 
     /**
@@ -199,7 +213,7 @@
      */
     private static int getAnnotationSetOffset(AnnotatedElement element) {
         Class<?> dexClass = getDexClass(element);
-        int directoryOffset = getDirectoryOffset(dexClass);
+        int directoryOffset = dexClass.getDexAnnotationDirectoryOffset();
         if (directoryOffset == 0) {
             return 0; // nothing on this class has annotations
         }
@@ -255,38 +269,32 @@
         Dex dex = declaringClass.getDex();
         int declaringClassIndex = getTypeIndex(dex, declaringClass);
         int typeIndex = getTypeIndex(dex, type);
-        int nameIndex = getStringIndex(dex, name);
+        int nameIndex = dex.findStringIndex(name);
         FieldId fieldId = new FieldId(dex, declaringClassIndex, typeIndex, nameIndex);
-        return Collections.binarySearch(dex.fieldIds(), fieldId);
+        return dex.findFieldIndex(fieldId);
     }
 
     public static int getMethodIndex(Class<?> declaringClass, String name, int protoIndex) {
         Dex dex = declaringClass.getDex();
         int declaringClassIndex = getTypeIndex(dex, declaringClass);
-        int nameIndex = getStringIndex(dex, name);
+        int nameIndex = dex.findStringIndex(name);
         MethodId methodId = new MethodId(dex, declaringClassIndex, protoIndex, nameIndex);
-        return Collections.binarySearch(dex.methodIds(), methodId);
+        return dex.findMethodIndex(methodId);
     }
 
     /**
      * Returns the parameter annotations on {@code member}.
      */
-    public static Annotation[][] getParameterAnnotations(Member member) {
-        Class<?> declaringClass = member.getDeclaringClass();
+    public static Annotation[][] getParameterAnnotations(Class<?> declaringClass,
+                                                         int methodDexIndex) {
         Dex dex = declaringClass.getDex();
-        int methodDexIndex;
-        if (member instanceof Method) {
-            methodDexIndex = ((Method) member).getDexMethodIndex();
-        } else {
-            methodDexIndex = ((Constructor<?>) member).getDexMethodIndex();
-        }
         int protoIndex = dex.methodIds().get(methodDexIndex).getProtoIndex();
         ProtoId proto = dex.protoIds().get(protoIndex);
         TypeList parametersList = dex.readTypeList(proto.getParametersOffset());
         short[] types = parametersList.getTypes();
         int typesCount = types.length;
 
-        int directoryOffset = getDirectoryOffset(declaringClass);
+        int directoryOffset = declaringClass.getDexAnnotationDirectoryOffset();
         if (directoryOffset == 0) {
             return new Annotation[typesCount][0]; // nothing on this class has annotations
         }
@@ -358,7 +366,7 @@
             throw new AssertionError("annotation value type != annotation class");
         }
 
-        int methodNameIndex = Collections.binarySearch(dex.strings(), method.getName());
+        int methodNameIndex = dex.findStringIndex(method.getName());
         for (int i = 0; i < fieldCount; i++) {
             int candidateNameIndex = reader.readAnnotationName();
             if (candidateNameIndex == methodNameIndex) {
@@ -389,7 +397,7 @@
         if (reader == null) {
             return null;
         }
-        return indexToType(c, dex, reader.readType());
+        return c.getDexCacheType(dex, reader.readType());
     }
 
     public static AccessibleObject getEnclosingMethodOrConstructor(Class<?> c) {
@@ -529,47 +537,21 @@
      * was derived.
      */
 
+    /** Find dex's type index for the class c */
     private static int getTypeIndex(Dex dex, Class<?> c) {
-        return dex == c.getDex() ? c.getTypeIndex() : computeTypeIndex(dex, c);
-    }
-
-    public static int computeTypeIndex(Dex dex, Class<?> c) {
+        if (dex == c.getDex()) {
+            return  c.getDexTypeIndex();
+        }
         if (dex == null) {
             return -1;
         }
-        int typeIndex = Collections.binarySearch(dex.typeNames(), InternalNames.getInternalName(c));
+        int typeIndex = dex.findTypeIndex(InternalNames.getInternalName(c));
         if (typeIndex < 0) {
             typeIndex = -1;
         }
         return typeIndex;
     }
 
-    private static int getStringIndex(Dex dex, String string) {
-        return Collections.binarySearch(dex.strings(), string);
-    }
-
-    private static int getDirectoryOffset(Class<?> c) {
-        return c.getAnnotationDirectoryOffset();
-    }
-
-    private static com.android.dex.Annotation getAnnotationFromAnnotationSet(
-            Dex dex, int annotationSetOffset, int annotationType) {
-        if (annotationSetOffset == 0) {
-            return null; // no annotation
-        }
-
-        Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
-        for (int i = 0, size = setIn.readInt(); i < size; i++) {
-            int annotationOffset = setIn.readInt();
-            Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item
-            com.android.dex.Annotation candidate = annotationIn.readAnnotation();
-            if (candidate.getTypeIndex() == annotationType) {
-                return candidate;
-            }
-        }
-
-        return null; // this set doesn't carry the annotation
-    }
 
     private static EncodedValueReader getAnnotationReader(
             Dex dex, AnnotatedElement element, String annotationName, int expectedFieldCount) {
@@ -578,16 +560,28 @@
             return null; // no annotations on the class
         }
 
-        int annotationTypeIndex = Collections.binarySearch(dex.typeNames(), annotationName);
-        com.android.dex.Annotation annotation = getAnnotationFromAnnotationSet(
-                dex, annotationSetOffset, annotationTypeIndex);
+        Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
+        com.android.dex.Annotation annotation = null;
+        // TODO: is it better to compute the index of the annotation name in the dex file and check
+        //       indices below?
+        for (int i = 0, size = setIn.readInt(); i < size; i++) {
+            int annotationOffset = setIn.readInt();
+            Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item
+            com.android.dex.Annotation candidate = annotationIn.readAnnotation();
+            String candidateAnnotationName = dex.typeNames().get(candidate.getTypeIndex());
+            if (annotationName.equals(candidateAnnotationName)) {
+                annotation = candidate;
+                break;
+            }
+        }
         if (annotation == null) {
             return null; // no annotation
         }
 
         EncodedValueReader reader = annotation.getReader();
         int fieldCount = reader.readAnnotation();
-        if (reader.getAnnotationType() != annotationTypeIndex) {
+        String readerAnnotationName = dex.typeNames().get(reader.getAnnotationType());
+        if (!readerAnnotationName.equals(annotationName)) {
             throw new AssertionError();
         }
         if (fieldCount != expectedFieldCount) {
@@ -615,8 +609,8 @@
                                                                   int typeIndex) {
         try {
             @SuppressWarnings("unchecked") // we do a runtime check
-            Class<? extends Annotation> result = (Class<? extends Annotation>) indexToType(context,
-                    dex, typeIndex);
+            Class<? extends Annotation> result =
+                (Class<? extends Annotation>) context.getDexCacheType(dex, typeIndex);
             if (!result.isAnnotation()) {
                 throw new IncompatibleClassChangeError("Expected annotation: " + result.getName());
             }
@@ -626,65 +620,23 @@
         }
     }
 
-    private static Class<?> indexToType(Class<?> context, Dex dex, int typeIndex) {
-        String internalName = dex.typeNames().get(typeIndex);
-        return InternalNames.getClass(context.getClassLoader(), internalName);
-    }
-
     private static AccessibleObject indexToMethod(Class<?> context, Dex dex, int methodIndex) {
-        MethodId methodId = dex.methodIds().get(methodIndex);
-        Class<?> declaringClass = indexToType(context, dex, methodId.getDeclaringClassIndex());
-        String name = dex.strings().get(methodId.getNameIndex());
-        Class<?>[] parametersArray = protoIndexToParameters(context, dex, methodId.getProtoIndex());
-        try {
-            return name.equals("<init>")
-                    ? declaringClass.getDeclaredConstructor(parametersArray)
-                    : declaringClass.getDeclaredMethod(name, parametersArray);
-        } catch (NoSuchMethodException e) {
-            throw new IncompatibleClassChangeError("Couldn't find " + declaringClass.getName()
-                    + "." + name + Arrays.toString(parametersArray));
-        }
-    }
-
-    public static Class<?>[] protoIndexToParameters(Class<?> context, Dex dex, int protoIndex) {
-        ProtoId proto = dex.protoIds().get(protoIndex);
-        TypeList parametersList = dex.readTypeList(proto.getParametersOffset());
-        short[] types = parametersList.getTypes();
+        Class<?> declaringClass =
+            context.getDexCacheType(dex, dex.declaringClassIndexFromMethodIndex(methodIndex));
+        String name = context.getDexCacheString(dex, dex.nameIndexFromMethodIndex(methodIndex));
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(methodIndex);
         Class<?>[] parametersArray = new Class[types.length];
         for (int i = 0; i < types.length; i++) {
-            parametersArray[i] = indexToType(context, dex, types[i]);
+            parametersArray[i] = context.getDexCacheType(dex, types[i]);
         }
-        return parametersArray;
-    }
-
-    public static Class<?>[] typeIndexToInterfaces(Class<?> context, Dex dex, int typeIndex) {
-        ClassDef def = getClassDef(dex, typeIndex);
-        if (def == null) {
-          return EmptyArray.CLASS;
+        try {
+            return name.equals("<init>")
+                ? declaringClass.getDeclaredConstructor(parametersArray)
+                : declaringClass.getDeclaredMethod(name, parametersArray);
+        } catch (NoSuchMethodException e) {
+            throw new IncompatibleClassChangeError("Couldn't find " + declaringClass.getName()
+                                                   + "." + name + Arrays.toString(parametersArray));
         }
-        short[] interfaces = def.getInterfaces();
-        Class<?>[] result = new Class<?>[interfaces.length];
-        for (int i = 0; i < interfaces.length; i++) {
-            result[i] = indexToType(context, dex, interfaces[i]);
-        }
-        return result;
-    }
-
-    public static int typeIndexToAnnotationDirectoryOffset(Dex dex, int typeIndex) {
-        ClassDef def = getClassDef(dex, typeIndex);
-        return def == null ? 0 : def.getAnnotationsOffset();
-    }
-
-    private static ClassDef getClassDef(Dex dex, int typeIndex) {
-        if (typeIndex == -1) {
-            return null;
-        }
-        for (ClassDef def : dex.classDefs()) {
-            if (def.getTypeIndex() == typeIndex) {
-                return def;
-            }
-        }
-        throw new AssertionError();
     }
 
     private static List<Annotation> annotationSetToAnnotations(Class<?> context, int offset) {
@@ -722,7 +674,7 @@
     private static <A extends Annotation> A toAnnotationInstance(Class<?> context, Dex dex,
             Class<A> annotationClass, EncodedValueReader reader) {
         int fieldCount = reader.readAnnotation();
-        if (annotationClass != indexToType(context, dex, reader.getAnnotationType())) {
+        if (annotationClass != context.getDexCacheType(dex, reader.getAnnotationType())) {
             throw new AssertionError("annotation value type != return type");
         }
         AnnotationMember[] members = new AnnotationMember[fieldCount];
@@ -766,10 +718,10 @@
             return toAnnotationInstance(context, dex, annotationClass, reader);
         } else if (type == String.class) {
             int index = reader.readString();
-            return dex.strings().get(index);
+            return context.getDexCacheString(dex, index);
         } else if (type == Class.class) {
             int index = reader.readType();
-            return indexToType(context, dex, index);
+            return context.getDexCacheType(dex, index);
         } else if (type == byte.class) {
             return reader.readByte();
         } else if (type == short.class) {
diff --git a/luni/src/main/java/libcore/reflect/GenericSignatureParser.java b/luni/src/main/java/libcore/reflect/GenericSignatureParser.java
index 0c94eba..5e86c49 100644
--- a/luni/src/main/java/libcore/reflect/GenericSignatureParser.java
+++ b/luni/src/main/java/libcore/reflect/GenericSignatureParser.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
+import libcore.util.EmptyArray;
 
 /**
  * Implements a parser for the generics signature attribute.
@@ -119,19 +120,23 @@
      * @param genericDecl the GenericDeclaration calling this method
      * @param signature the generic signature of the class
      */
-    public void parseForClass(GenericDeclaration genericDecl,
-            String signature) {
+    public void parseForClass(GenericDeclaration genericDecl, String signature) {
         setInput(genericDecl, signature);
         if (!eof) {
             parseClassSignature();
         } else {
             if(genericDecl instanceof Class) {
                 Class c = (Class) genericDecl;
-                this.formalTypeParameters = ListOfVariables.EMPTY;
+                this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
                 this.superclassType = c.getSuperclass();
-                this.interfaceTypes = new ListOfTypes(c.getInterfaces());
+                Class<?>[] interfaces = c.getInterfaces();
+                if (interfaces.length == 0) {
+                    this.interfaceTypes = ListOfTypes.EMPTY;
+                } else {
+                    this.interfaceTypes = new ListOfTypes(interfaces);
+                }
             } else {
-                this.formalTypeParameters = ListOfVariables.EMPTY;
+                this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
                 this.superclassType = Object.class;
                 this.interfaceTypes = ListOfTypes.EMPTY;
             }
@@ -152,9 +157,19 @@
             parseMethodTypeSignature(rawExceptionTypes);
         } else {
             Method m = (Method) genericDecl;
-            this.formalTypeParameters = ListOfVariables.EMPTY;
-            this.parameterTypes = new ListOfTypes(m.getParameterTypes());
-            this.exceptionTypes = new ListOfTypes(m.getExceptionTypes());
+            this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
+            Class<?>[] parameterTypes = m.getParameterTypes();
+            if (parameterTypes.length == 0) {
+                this.parameterTypes = ListOfTypes.EMPTY;
+            } else {
+                this.parameterTypes = new ListOfTypes(parameterTypes);
+            }
+            Class<?>[] exceptionTypes = m.getExceptionTypes();
+            if (exceptionTypes.length == 0) {
+                this.exceptionTypes = ListOfTypes.EMPTY;
+            } else {
+                this.exceptionTypes = new ListOfTypes(exceptionTypes);
+            }
             this.returnType = m.getReturnType();
         }
     }
@@ -173,9 +188,19 @@
             parseMethodTypeSignature(rawExceptionTypes);
         } else {
             Constructor c = (Constructor) genericDecl;
-            this.formalTypeParameters = ListOfVariables.EMPTY;
-            this.parameterTypes = new ListOfTypes(c.getParameterTypes());
-            this.exceptionTypes = new ListOfTypes(c.getExceptionTypes());
+            this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
+            Class<?>[] parameterTypes = c.getParameterTypes();
+            if (parameterTypes.length == 0) {
+                this.parameterTypes = ListOfTypes.EMPTY;
+            } else {
+                this.parameterTypes = new ListOfTypes(parameterTypes);
+            }
+            Class<?>[] exceptionTypes = c.getExceptionTypes();
+            if (exceptionTypes.length == 0) {
+                this.exceptionTypes = ListOfTypes.EMPTY;
+            } else {
+                this.exceptionTypes = new ListOfTypes(exceptionTypes);
+            }
         }
     }
 
@@ -449,7 +474,7 @@
         }
     }
 
-    boolean isStopSymbol(char ch) {
+    static boolean isStopSymbol(char ch) {
         switch (ch) {
         case ':':
         case '/':
@@ -472,7 +497,7 @@
                     char ch = buffer[pos];
                     if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z')
                             || !isStopSymbol(ch)) {
-                        identBuf.append(buffer[pos]);
+                        identBuf.append(ch);
                         pos++;
                     } else {
                         identifier = identBuf.toString();
diff --git a/luni/src/main/java/libcore/reflect/ListOfTypes.java b/luni/src/main/java/libcore/reflect/ListOfTypes.java
index c5d90e2..12ad84d 100644
--- a/luni/src/main/java/libcore/reflect/ListOfTypes.java
+++ b/luni/src/main/java/libcore/reflect/ListOfTypes.java
@@ -19,6 +19,7 @@
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
+import libcore.util.EmptyArray;
 
 public final class ListOfTypes {
     public static final ListOfTypes EMPTY = new ListOfTypes(0);
@@ -50,12 +51,20 @@
 
     public Type[] getResolvedTypes() {
         Type[] result = resolvedTypes;
-        return result != null ? result : (resolvedTypes = resolveTypes(types));
+        if (result == null) {
+            result = resolveTypes(types);
+            resolvedTypes = result;
+        }
+        return result;
     }
 
     private Type[] resolveTypes(List<Type> unresolved) {
-        Type[] result = new Type[unresolved.size()];
-        for (int i = 0; i < unresolved.size(); i++) {
+        int size = unresolved.size();
+        if (size == 0) {
+            return EmptyArray.TYPE;
+        }
+        Type[] result = new Type[size];
+        for (int i = 0; i < size; i++) {
             Type type = unresolved.get(i);
             try {
                 result[i] = ((ParameterizedTypeImpl) type).getResolvedType();
diff --git a/luni/src/main/java/libcore/reflect/ListOfVariables.java b/luni/src/main/java/libcore/reflect/ListOfVariables.java
index 5d96817..43f2969 100644
--- a/luni/src/main/java/libcore/reflect/ListOfVariables.java
+++ b/luni/src/main/java/libcore/reflect/ListOfVariables.java
@@ -20,8 +20,6 @@
 import java.util.ArrayList;
 
 final class ListOfVariables {
-    public static final TypeVariable[] EMPTY = new TypeVariableImpl[0];
-
     final ArrayList<TypeVariable<?>> array = new ArrayList<TypeVariable<?>>();
 
     void add (TypeVariable<?> elem) {
diff --git a/luni/src/main/java/libcore/reflect/Types.java b/luni/src/main/java/libcore/reflect/Types.java
index 2132b11..3181ca1 100644
--- a/luni/src/main/java/libcore/reflect/Types.java
+++ b/luni/src/main/java/libcore/reflect/Types.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.TypeVariable;
 import java.util.HashMap;
 import java.util.Map;
+import libcore.util.EmptyArray;
 
 public final class Types {
     private Types() {
@@ -42,16 +43,19 @@
         PRIMITIVE_TO_SIGNATURE.put(boolean.class, "Z");
     }
 
-    public static Type[] getClonedTypeArray(ListOfTypes types) {
-        return types.getResolvedTypes().clone();
+    public static Type[] getTypeArray(ListOfTypes types, boolean clone) {
+        if (types.length() == 0) {
+            return EmptyArray.TYPE;
+        }
+        Type[] result = types.getResolvedTypes();
+        return clone ? result.clone() : result;
     }
 
     public static Type getType(Type type) {
         if (type instanceof ParameterizedTypeImpl) {
             return ((ParameterizedTypeImpl)type).getResolvedType();
-        } else {
-            return type;
         }
+        return type;
     }
 
     /**
diff --git a/luni/src/main/java/libcore/util/EmptyArray.java b/luni/src/main/java/libcore/util/EmptyArray.java
index 6c99878..1713bfc 100644
--- a/luni/src/main/java/libcore/util/EmptyArray.java
+++ b/luni/src/main/java/libcore/util/EmptyArray.java
@@ -30,4 +30,7 @@
     public static final String[] STRING = new String[0];
     public static final Throwable[] THROWABLE = new Throwable[0];
     public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
+    public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0];
+    public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE =
+        new java.lang.reflect.TypeVariable[0];
 }
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java b/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java
index 9d01f8d..c1d73df 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java
@@ -30,6 +30,7 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import org.apache.harmony.security.asn1.ObjectIdentifier;
+import org.apache.harmony.security.fortress.Services;
 
 /**
  * Provides Algorithm Name to OID and OID to Algorithm Name mappings. Some known
@@ -37,6 +38,9 @@
  * providers during initialization.
  */
 public class AlgNameMapper {
+    private static AlgNameMapperSource source = null;
+
+    private static volatile int cacheVersion = -1;
 
     // Will search OID mappings for these services
     private static final String[] serviceName = {
@@ -89,22 +93,29 @@
             // map upper case alg name to its original name
             algAliasesMap.put(algUC, element[1]);
         }
-        //
-        // Now search providers for mappings like
-        // Alg.Alias.<service>.<OID-INTS-DOT-SEPARATED>=<alg-name>
-        //  or
-        // Alg.Alias.<service>.OID.<OID-INTS-DOT-SEPARATED>=<alg-name>
-        //
-        Provider[] pl = Security.getProviders();
-        for (Provider element : pl) {
-            selectEntries(element);
-        }
     }
 
     // No instances
     private AlgNameMapper() {
     }
 
+    private static synchronized void checkCacheVersion() {
+        int newCacheVersion = Services.getCacheVersion();
+        if (newCacheVersion != cacheVersion) {
+            //
+            // Now search providers for mappings like
+            // Alg.Alias.<service>.<OID-INTS-DOT-SEPARATED>=<alg-name>
+            //  or
+            // Alg.Alias.<service>.OID.<OID-INTS-DOT-SEPARATED>=<alg-name>
+            //
+            Provider[] pl = Security.getProviders();
+            for (Provider element : pl) {
+                selectEntries(element);
+            }
+            cacheVersion = newCacheVersion;
+        }
+    }
+
     /**
      * Returns OID for algName
      *
@@ -112,8 +123,21 @@
      * @return OID as String
      */
     public static String map2OID(String algName) {
+        checkCacheVersion();
+
         // alg2OidMap map contains upper case keys
-        return alg2OidMap.get(algName.toUpperCase(Locale.US));
+        String result = alg2OidMap.get(algName.toUpperCase(Locale.US));
+        if (result != null) {
+            return result;
+        }
+
+        // Check our external source.
+        AlgNameMapperSource s = source;
+        if (s != null) {
+            return s.mapNameToOid(algName);
+        }
+
+        return null;
     }
 
     /**
@@ -123,12 +147,21 @@
      * @return algorithm name
      */
     public static String map2AlgName(String oid) {
+        checkCacheVersion();
+
         // oid2AlgMap map contains upper case values
-        final String algUC = oid2AlgMap.get(oid);
+        String algUC = oid2AlgMap.get(oid);
         // if not null there is always map UC->Orig
         if (algUC != null) {
             return algAliasesMap.get(algUC);
         }
+
+        // Check our external source.
+        AlgNameMapperSource s = source;
+        if (s != null) {
+            return s.mapOidToName(oid);
+        }
+
         return null;
     }
 
@@ -204,4 +237,8 @@
             ? oid.substring(4)
             : oid;
     }
+
+    public static void setSource(AlgNameMapperSource source) {
+        AlgNameMapper.source = source;
+    }
 }
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapperSource.java b/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapperSource.java
new file mode 100644
index 0000000..abca36c
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapperSource.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.security.utils;
+
+/**
+ * Provides a mapping source that the {@link AlgNameMapper} can query for
+ * mapping between algorithm names and OIDs.
+ */
+public interface AlgNameMapperSource {
+    public String mapNameToOid(String algName);
+
+    public String mapOidToName(String oid);
+}
diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp
index ef9909b..c1bdd0f 100644
--- a/luni/src/main/native/IcuUtilities.cpp
+++ b/luni/src/main/native/IcuUtilities.cpp
@@ -32,13 +32,11 @@
   return Locale::createFromName(ScopedUtfChars(env, localeName).c_str());
 }
 
-jobjectArray fromStringEnumeration(JNIEnv* env, StringEnumeration* se) {
-  UniquePtr<StringEnumeration> deleter(se);
-  if (se == NULL) {
+jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, StringEnumeration* se) {
+  if (maybeThrowIcuException(env, provider, status)) {
     return NULL;
   }
 
-  UErrorCode status = U_ZERO_ERROR;
   int32_t count = se->count(status);
   if (maybeThrowIcuException(env, "StringEnumeration::count", status)) {
     return NULL;
diff --git a/luni/src/main/native/IcuUtilities.h b/luni/src/main/native/IcuUtilities.h
index d93282c..cff1e0d 100644
--- a/luni/src/main/native/IcuUtilities.h
+++ b/luni/src/main/native/IcuUtilities.h
@@ -20,11 +20,12 @@
 #define U_HAVE_STD_STRING 1 // For UnicodeString::toUTF8String(std::string&).
 
 #include "jni.h"
+#include "ustrenum.h" // For UStringEnumeration.
 #include "unicode/utypes.h" // For UErrorCode.
 #include "unicode/locid.h" // For Locale.
 
 extern Locale getLocale(JNIEnv* env, jstring localeName);
-extern jobjectArray fromStringEnumeration(JNIEnv* env, StringEnumeration*);
+extern jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, StringEnumeration*);
 bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error);
 
 #endif  // ICU_UTILITIES_H_included
diff --git a/luni/src/main/native/JniException.cpp b/luni/src/main/native/JniException.cpp
index 8f97891..9744777 100644
--- a/luni/src/main/native/JniException.cpp
+++ b/luni/src/main/native/JniException.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <stdio.h>  // For BUFSIZ
+
 #include "JniException.h"
 #include "JNIHelp.h"
 
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 54aecef..6b24a36 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "libcore" // We'll be next to "dalvikvm" in the log; make the distinction clear.
 
+#include "cutils/log.h"
 #include "JniConstants.h"
 #include "ScopedLocalFrame.h"
 
@@ -73,7 +74,6 @@
     REGISTER(register_libcore_net_RawSocket);
     REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
     REGISTER(register_org_apache_harmony_xml_ExpatParser);
-    REGISTER(register_org_conscrypt_NativeCrypto);
     REGISTER(register_sun_misc_Unsafe);
 #undef REGISTER
     return JNI_VERSION_1_6;
diff --git a/luni/src/main/native/ZipUtilities.cpp b/luni/src/main/native/ZipUtilities.cpp
new file mode 100644
index 0000000..745b3b1
--- /dev/null
+++ b/luni/src/main/native/ZipUtilities.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#include "JniConstants.h"
+#include "JniException.h"
+#include "UniquePtr.h"
+#include "ZipUtilities.h"
+
+void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int error,
+    NativeZipStream* stream) {
+  if (error == Z_MEM_ERROR) {
+    jniThrowOutOfMemoryError(env, NULL);
+  } else if (stream != NULL && stream->stream.msg != NULL) {
+    jniThrowException(env, exceptionClassName, stream->stream.msg);
+  } else {
+    jniThrowException(env, exceptionClassName, zError(error));
+  }
+}
+
+NativeZipStream::NativeZipStream() : input(NULL), inCap(0), mDict(NULL) {
+  // Let zlib use its default allocator.
+  stream.opaque = Z_NULL;
+  stream.zalloc = Z_NULL;
+  stream.zfree = Z_NULL;
+}
+
+NativeZipStream::~NativeZipStream() {
+}
+
+void NativeZipStream::setDictionary(JNIEnv* env, jbyteArray javaDictionary, int off, int len,
+    bool inflate) {
+  UniquePtr<jbyte[]> dictionaryBytes(new jbyte[len]);
+  if (dictionaryBytes.get() == NULL) {
+    jniThrowOutOfMemoryError(env, NULL);
+    return;
+  }
+  env->GetByteArrayRegion(javaDictionary, off, len, &dictionaryBytes[0]);
+  const Bytef* dictionary = reinterpret_cast<const Bytef*>(&dictionaryBytes[0]);
+  int err;
+  if (inflate) {
+    err = inflateSetDictionary(&stream, dictionary, len);
+  } else {
+    err = deflateSetDictionary(&stream, dictionary, len);
+  }
+  if (err != Z_OK) {
+    throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, NULL);
+    return;
+  }
+  mDict.reset(dictionaryBytes.release());
+}
+
+void NativeZipStream::setInput(JNIEnv* env, jbyteArray buf, jint off, jint len) {
+  input.reset(new jbyte[len]);
+  if (input.get() == NULL) {
+    inCap = 0;
+    jniThrowOutOfMemoryError(env, NULL);
+    return;
+  }
+  inCap = len;
+  if (buf != NULL) {
+    env->GetByteArrayRegion(buf, off, len, &input[0]);
+  }
+  stream.next_in = reinterpret_cast<Bytef*>(&input[0]);
+  stream.avail_in = len;
+}
+
+NativeZipStream* toNativeZipStream(jlong address) {
+  return reinterpret_cast<NativeZipStream*>(static_cast<uintptr_t>(address));
+}
diff --git a/luni/src/main/native/ZipUtilities.h b/luni/src/main/native/ZipUtilities.h
index 4c86f32..fe0f977 100644
--- a/luni/src/main/native/ZipUtilities.h
+++ b/luni/src/main/native/ZipUtilities.h
@@ -18,20 +18,9 @@
 #ifndef ZIP_UTILITIES_H_included
 #define ZIP_UTILITIES_H_included
 
-#include "JNIHelp.h"
-#include "JniException.h"
 #include "UniquePtr.h"
 #include "jni.h"
 #include "zlib.h"
-#include "zutil.h" // For DEF_WBITS and DEF_MEM_LEVEL.
-
-static void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int error) {
-    if (error == Z_MEM_ERROR) {
-        jniThrowOutOfMemoryError(env, NULL);
-    } else {
-        jniThrowException(env, exceptionClassName, zError(error));
-    }
-}
 
 class NativeZipStream {
 public:
@@ -39,51 +28,10 @@
     int inCap;
     z_stream stream;
 
-    NativeZipStream() : input(NULL), inCap(0), mDict(NULL) {
-        // Let zlib use its default allocator.
-        stream.opaque = Z_NULL;
-        stream.zalloc = Z_NULL;
-        stream.zfree = Z_NULL;
-    }
-
-    ~NativeZipStream() {
-    }
-
-    void setDictionary(JNIEnv* env, jbyteArray javaDictionary, int off, int len, bool inflate) {
-        UniquePtr<jbyte[]> dictionaryBytes(new jbyte[len]);
-        if (dictionaryBytes.get() == NULL) {
-            jniThrowOutOfMemoryError(env, NULL);
-            return;
-        }
-        env->GetByteArrayRegion(javaDictionary, off, len, &dictionaryBytes[0]);
-        const Bytef* dictionary = reinterpret_cast<const Bytef*>(&dictionaryBytes[0]);
-        int err;
-        if (inflate) {
-            err = inflateSetDictionary(&stream, dictionary, len);
-        } else {
-            err = deflateSetDictionary(&stream, dictionary, len);
-        }
-        if (err != Z_OK) {
-            throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
-            return;
-        }
-        mDict.reset(dictionaryBytes.release());
-    }
-
-    void setInput(JNIEnv* env, jbyteArray buf, jint off, jint len) {
-        input.reset(new jbyte[len]);
-        if (input.get() == NULL) {
-            inCap = 0;
-            jniThrowOutOfMemoryError(env, NULL);
-            return;
-        }
-        inCap = len;
-        if (buf != NULL) {
-            env->GetByteArrayRegion(buf, off, len, &input[0]);
-        }
-        stream.next_in = reinterpret_cast<Bytef*>(&input[0]);
-        stream.avail_in = len;
-    }
+    NativeZipStream();
+    ~NativeZipStream();
+    void setDictionary(JNIEnv* env, jbyteArray javaDictionary, int off, int len, bool inflate);
+    void setInput(JNIEnv* env, jbyteArray buf, jint off, jint len);
 
 private:
     UniquePtr<jbyte[]> mDict;
@@ -93,8 +41,9 @@
     void operator=(const NativeZipStream&);
 };
 
-static NativeZipStream* toNativeZipStream(jlong address) {
-    return reinterpret_cast<NativeZipStream*>(static_cast<uintptr_t>(address));
-}
+NativeZipStream* toNativeZipStream(jlong address);
+
+void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int error,
+        NativeZipStream* stream);
 
 #endif  // ZIP_UTILITIES_H_included
diff --git a/luni/src/main/native/java_lang_Character.cpp b/luni/src/main/native/java_lang_Character.cpp
index de1800e..14eef64 100644
--- a/luni/src/main/native/java_lang_Character.cpp
+++ b/luni/src/main/native/java_lang_Character.cpp
@@ -21,6 +21,7 @@
 #include "ScopedUtfChars.h"
 #include "unicode/uchar.h"
 #include <math.h>
+#include <stdio.h> // For BUFSIZ
 #include <stdlib.h>
 
 static jint Character_digitImpl(JNIEnv*, jclass, jint codePoint, jint radix) {
diff --git a/luni/src/main/native/java_util_zip_Deflater.cpp b/luni/src/main/native/java_util_zip_Deflater.cpp
index ed7d754..1afd36e 100644
--- a/luni/src/main/native/java_util_zip_Deflater.cpp
+++ b/luni/src/main/native/java_util_zip_Deflater.cpp
@@ -18,8 +18,10 @@
 #define LOG_TAG "Deflater"
 
 #include "JniConstants.h"
+#include "JniException.h"
 #include "ScopedPrimitiveArray.h"
 #include "ZipUtilities.h"
+#include "zutil.h" // For DEF_WBITS and DEF_MEM_LEVEL.
 
 static void Deflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
     toNativeZipStream(handle)->setDictionary(env, dict, off, len, false);
@@ -56,7 +58,7 @@
     int memLevel = DEF_MEM_LEVEL;
     int err = deflateInit2(&jstream->stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
     if (err != Z_OK) {
-        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
+        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, jstream.get());
         return -1;
     }
     return reinterpret_cast<uintptr_t>(jstream.release());
@@ -92,7 +94,7 @@
         // input and more output space to continue compressing".
         break;
     default:
-        throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err);
+        throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err, stream);
         return -1;
     }
 
@@ -116,7 +118,7 @@
     NativeZipStream* stream = toNativeZipStream(handle);
     int err = deflateReset(&stream->stream);
     if (err != Z_OK) {
-        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
+        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
     }
 }
 
@@ -130,7 +132,7 @@
     stream->stream.avail_out = 0;
     int err = deflateParams(&stream->stream, level, strategy);
     if (err != Z_OK) {
-        throwExceptionForZlibError(env, "java/lang/IllegalStateException", err);
+        throwExceptionForZlibError(env, "java/lang/IllegalStateException", err, stream);
     }
 }
 
diff --git a/luni/src/main/native/java_util_zip_Inflater.cpp b/luni/src/main/native/java_util_zip_Inflater.cpp
index 6aa3e24..ca3ee09 100644
--- a/luni/src/main/native/java_util_zip_Inflater.cpp
+++ b/luni/src/main/native/java_util_zip_Inflater.cpp
@@ -18,8 +18,10 @@
 #define LOG_TAG "Inflater"
 
 #include "JniConstants.h"
+#include "JniException.h"
 #include "ScopedPrimitiveArray.h"
 #include "ZipUtilities.h"
+#include "zutil.h" // For DEF_WBITS and DEF_MEM_LEVEL.
 #include <errno.h>
 
 static jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
@@ -40,7 +42,7 @@
      */
     int err = inflateInit2(&jstream->stream, noHeader ? -DEF_WBITS : DEF_WBITS);
     if (err != Z_OK) {
-        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
+        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, jstream.get());
         return -1;
     }
     return reinterpret_cast<uintptr_t>(jstream.release());
@@ -113,7 +115,7 @@
     case Z_STREAM_ERROR:
         return 0;
     default:
-        throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err);
+        throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err, stream);
         return -1;
     }
 
@@ -142,9 +144,10 @@
 }
 
 static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
-    int err = inflateReset(&toNativeZipStream(handle)->stream);
+    NativeZipStream* stream = toNativeZipStream(handle);
+    int err = inflateReset(&stream->stream);
     if (err != Z_OK) {
-        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
+        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
     }
 }
 
diff --git a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
index 65ace4e..72bc631 100644
--- a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
@@ -23,41 +23,52 @@
 #include "cutils/log.h"
 #include "unicode/dtitvfmt.h"
 
-static jstring DateIntervalFormat_formatDateInterval(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName, jlong fromDate, jlong toDate) {
+static jlong DateIntervalFormat_createDateIntervalFormat(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName) {
   Locale locale = getLocale(env, javaLocaleName);
 
   ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton);
   if (!skeletonHolder.valid()) {
-    return NULL;
+    return 0;
+  }
+
+  UErrorCode status = U_ZERO_ERROR;
+  DateIntervalFormat* formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), locale, status));
+  if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
+    return 0;
   }
 
   ScopedJavaUnicodeString tzNameHolder(env, javaTzName);
   if (!tzNameHolder.valid()) {
-    return NULL;
+    return 0;
   }
-
-  UErrorCode status = U_ZERO_ERROR;
-  UniquePtr<DateIntervalFormat> formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), locale, status));
-  if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
-    return NULL;
-  }
-
   formatter->adoptTimeZone(TimeZone::createTimeZone(tzNameHolder.unicodeString()));
 
+  return reinterpret_cast<uintptr_t>(formatter);
+}
+
+static void DateIntervalFormat_destroyDateIntervalFormat(JNIEnv*, jclass, jlong address) {
+  delete reinterpret_cast<DateIntervalFormat*>(address);
+}
+
+static jstring DateIntervalFormat_formatDateInterval(JNIEnv* env, jclass, jlong address, jlong fromDate, jlong toDate) {
+  DateIntervalFormat* formatter(reinterpret_cast<DateIntervalFormat*>(address));
   DateInterval date_interval(fromDate, toDate);
 
-  UnicodeString result;
+  UnicodeString s;
   FieldPosition pos = 0;
-  formatter->format(&date_interval, result, pos, status);
+  UErrorCode status = U_ZERO_ERROR;
+  formatter->format(&date_interval, s, pos, status);
   if (maybeThrowIcuException(env, "DateIntervalFormat::format", status)) {
-      return NULL;
+    return NULL;
   }
 
-  return env->NewString(result.getBuffer(), result.length());
+  return env->NewString(s.getBuffer(), s.length());
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(DateIntervalFormat, formatDateInterval, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJ)Ljava/lang/String;"),
+  NATIVE_METHOD(DateIntervalFormat, createDateIntervalFormat, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J"),
+  NATIVE_METHOD(DateIntervalFormat, destroyDateIntervalFormat, "(J)V"),
+  NATIVE_METHOD(DateIntervalFormat, formatDateInterval, "(JJJ)Ljava/lang/String;"),
 };
 void register_libcore_icu_DateIntervalFormat(JNIEnv* env) {
   jniRegisterNativeMethods(env, "libcore/icu/DateIntervalFormat", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 40d6e04..5bbb506 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -439,28 +439,6 @@
   return true;
 }
 
-static bool getTimeFormats12And24(JNIEnv* env, jobject localeData, Locale& locale) {
-  UErrorCode status = U_ZERO_ERROR;
-  DateTimePatternGenerator* generator = DateTimePatternGenerator::createInstance(locale, status);
-  if (U_FAILURE(status)) {
-    return false;
-  }
-
-  UnicodeString pattern_Hm(generator->getBestPattern(UnicodeString("Hm", 2, US_INV), status));
-  if (U_FAILURE(status)) {
-    return false;
-  }
-
-  UnicodeString pattern_hm(generator->getBestPattern(UnicodeString("hm", 2, US_INV), status));
-  if (U_FAILURE(status)) {
-    return false;
-  }
-
-  setStringField(env, localeData, "timeFormat12", pattern_hm);
-  setStringField(env, localeData, "timeFormat24", pattern_Hm);
-  return true;
-}
-
 static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const char* locale_name) {
   UErrorCode status = U_ZERO_ERROR;
   ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
@@ -489,7 +467,7 @@
   return false;
 }
 
-static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) {
+static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) {
     ScopedUtfChars localeName(env, javaLocaleName);
     if (localeName.c_str() == NULL) {
         return JNI_FALSE;
@@ -512,13 +490,6 @@
         return JNI_FALSE;
     }
 
-    // Get the "h:mm a" and "HH:mm" 12- and 24-hour time format strings.
-    Locale locale = getLocale(env, javaLocaleName);
-    if (!getTimeFormats12And24(env, localeData, locale)) {
-        ALOGE("Couldn't find ICU 12- and 24-hour time formats for %s", localeName.c_str());
-        return JNI_FALSE;
-    }
-
     // Get the "Yesterday", "Today", and "Tomorrow" strings.
     bool foundYesterdayTodayAndTomorrow = false;
     for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
@@ -533,6 +504,7 @@
     }
 
     status = U_ZERO_ERROR;
+    Locale locale = getLocale(env, javaLocaleName);
     UniquePtr<Calendar> cal(Calendar::createInstance(locale, status));
     if (U_FAILURE(status)) {
         return JNI_FALSE;
@@ -667,35 +639,16 @@
     return versionString(env, unicodeVersion);
 }
 
-struct EnumerationCounter {
-    const size_t count;
-    EnumerationCounter(size_t count) : count(count) {}
-    size_t operator()() { return count; }
-};
-struct EnumerationGetter {
-    UEnumeration* e;
-    UErrorCode* status;
-    EnumerationGetter(UEnumeration* e, UErrorCode* status) : e(e), status(status) {}
-    const UChar* operator()(int32_t* charCount) { return uenum_unext(e, charCount, status); }
-};
 static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) {
-    UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
-    EnumerationCounter counter(uenum_count(e, &status));
-    if (maybeThrowIcuException(env, "uenum_count", status)) {
-        return NULL;
-    }
-    EnumerationGetter getter(e, &status);
-    jobject result = toStringArray16(env, &counter, &getter);
-    maybeThrowIcuException(env, "uenum_unext", status);
-    uenum_close(e);
-    return result;
+  UErrorCode status = U_ZERO_ERROR;
+  UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
+  return fromStringEnumeration(env, status, "ucurr_openISOCurrencies", &e);
 }
 
-static jstring ICU_getBestDateTimePattern(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName) {
+static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName) {
   Locale locale = getLocale(env, javaLocaleName);
   UErrorCode status = U_ZERO_ERROR;
-  DateTimePatternGenerator* generator = DateTimePatternGenerator::createInstance(locale, status);
+  UniquePtr<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(locale, status));
   if (maybeThrowIcuException(env, "DateTimePatternGenerator::createInstance", status)) {
     return NULL;
   }
@@ -721,7 +674,7 @@
     NATIVE_METHOD(ICU, getAvailableDateFormatLocalesNative, "()[Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getAvailableNumberFormatLocalesNative, "()[Ljava/lang/String;"),
-    NATIVE_METHOD(ICU, getBestDateTimePattern, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
+    NATIVE_METHOD(ICU, getBestDateTimePatternNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getCldrVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getCurrencyDisplayName, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
@@ -737,7 +690,7 @@
     NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
-    NATIVE_METHOD(ICU, initLocaleDataImpl, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
+    NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
     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;"),
 };
diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index fae56b7..137e172 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -57,17 +57,105 @@
     UConverterFromUCallback onMalformedInput;
 };
 
-struct UConverterDeleter {
-    void operator()(UConverter* p) const {
-        ucnv_close(p);
-    }
-};
-typedef UniquePtr<UConverter, UConverterDeleter> UniqueUConverter;
-
 static UConverter* toUConverter(jlong address) {
     return reinterpret_cast<UConverter*>(static_cast<uintptr_t>(address));
 }
 
+static bool collectStandardNames(JNIEnv* env, const char* canonicalName, const char* standard,
+                                 std::vector<std::string>& result) {
+  UErrorCode status = U_ZERO_ERROR;
+  UStringEnumeration e(ucnv_openStandardNames(canonicalName, standard, &status));
+  if (maybeThrowIcuException(env, "ucnv_openStandardNames", status)) {
+    return false;
+  }
+
+  int32_t count = e.count(status);
+  if (maybeThrowIcuException(env, "StringEnumeration::count", status)) {
+    return false;
+  }
+
+  for (int32_t i = 0; i < count; ++i) {
+    const UnicodeString* string = e.snext(status);
+    if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
+      return false;
+    }
+    std::string s;
+    string->toUTF8String(s);
+    if (s.find_first_of("+,") == std::string::npos) {
+      result.push_back(s);
+    }
+  }
+
+  return true;
+}
+
+static const char* getICUCanonicalName(const char* name) {
+  UErrorCode error = U_ZERO_ERROR;
+  const char* canonicalName = NULL;
+  if ((canonicalName = ucnv_getCanonicalName(name, "MIME", &error)) != NULL) {
+    return canonicalName;
+  } else if ((canonicalName = ucnv_getCanonicalName(name, "IANA", &error)) != NULL) {
+    return canonicalName;
+  } else if ((canonicalName = ucnv_getCanonicalName(name, "", &error)) != NULL) {
+    return canonicalName;
+  } else if ((canonicalName = ucnv_getAlias(name, 0, &error)) != NULL) {
+    // We have some aliases in the form x-blah .. match those first.
+    return canonicalName;
+  } else if (strstr(name, "x-") == name) {
+    // Check if the converter can be opened with the name given.
+    error = U_ZERO_ERROR;
+    LocalUConverterPointer cnv(ucnv_open(name + 2, &error));
+    if (U_SUCCESS(error)) {
+      return name + 2;
+    }
+  }
+  return NULL;
+}
+
+// If a charset listed in the IANA Charset Registry is supported by an implementation
+// of the Java platform then its canonical name must be the name listed in the registry.
+// Many charsets are given more than one name in the registry, in which case the registry
+// identifies one of the names as MIME-preferred. If a charset has more than one registry
+// name then its canonical name must be the MIME-preferred name and the other names in
+// the registry must be valid aliases. If a supported charset is not listed in the IANA
+// registry then its canonical name must begin with one of the strings "X-" or "x-".
+static jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
+  UErrorCode status = U_ZERO_ERROR;
+
+  // Check to see if this is a well-known MIME or IANA name.
+  const char* cName = NULL;
+  if ((cName = ucnv_getStandardName(icuCanonicalName, "MIME", &status)) != NULL) {
+    return env->NewStringUTF(cName);
+  } else if ((cName = ucnv_getStandardName(icuCanonicalName, "IANA", &status)) != NULL) {
+    return env->NewStringUTF(cName);
+  }
+
+  // Check to see if an alias already exists with "x-" prefix, if yes then
+  // make that the canonical name.
+  int32_t aliasCount = ucnv_countAliases(icuCanonicalName, &status);
+  for (int i = 0; i < aliasCount; ++i) {
+    const char* name = ucnv_getAlias(icuCanonicalName, i, &status);
+    if (name != NULL && name[0] == 'x' && name[1] == '-') {
+      return env->NewStringUTF(name);
+    }
+  }
+
+  // As a last resort, prepend "x-" to any alias and make that the canonical name.
+  status = U_ZERO_ERROR;
+  const char* name = ucnv_getStandardName(icuCanonicalName, "UTR22", &status);
+  if (name == NULL && strchr(icuCanonicalName, ',') != NULL) {
+    name = ucnv_getAlias(icuCanonicalName, 1, &status);
+  }
+  // If there is no UTR22 canonical name then just return the original name.
+  if (name == NULL) {
+    name = icuCanonicalName;
+  }
+  UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
+  strcpy(&result[0], "x-");
+  strcat(&result[0], name);
+  return env->NewStringUTF(&result[0]);
+}
+
 static jlong NativeConverter_openConverter(JNIEnv* env, jclass, jstring converterName) {
     ScopedUtfChars converterNameChars(env, converterName);
     if (converterNameChars.c_str() == NULL) {
@@ -230,52 +318,6 @@
     return (cnv != NULL) ? ((ucnv_getMaxCharSize(cnv) + ucnv_getMinCharSize(cnv)) / 2.0) : -1;
 }
 
-/*
- * If a charset listed in the IANA Charset Registry is supported by an implementation
- * of the Java platform then its canonical name must be the name listed in the registry.
- * Many charsets are given more than one name in the registry, in which case the registry
- * identifies one of the names as MIME-preferred. If a charset has more than one registry
- * name then its canonical name must be the MIME-preferred name and the other names in
- * the registry must be valid aliases. If a supported charset is not listed in the IANA
- * registry then its canonical name must begin with one of the strings "X-" or "x-".
- */
-static jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
-    UErrorCode status = U_ZERO_ERROR;
-
-    // Check to see if this is a well-known MIME or IANA name.
-    const char* cName = NULL;
-    if ((cName = ucnv_getStandardName(icuCanonicalName, "MIME", &status)) != NULL) {
-        return env->NewStringUTF(cName);
-    } else if ((cName = ucnv_getStandardName(icuCanonicalName, "IANA", &status)) != NULL) {
-        return env->NewStringUTF(cName);
-    }
-
-    // Check to see if an alias already exists with "x-" prefix, if yes then
-    // make that the canonical name.
-    int32_t aliasCount = ucnv_countAliases(icuCanonicalName, &status);
-    for (int i = 0; i < aliasCount; ++i) {
-        const char* name = ucnv_getAlias(icuCanonicalName, i, &status);
-        if (name != NULL && name[0] == 'x' && name[1] == '-') {
-            return env->NewStringUTF(name);
-        }
-    }
-
-    // As a last resort, prepend "x-" to any alias and make that the canonical name.
-    status = U_ZERO_ERROR;
-    const char* name = ucnv_getStandardName(icuCanonicalName, "UTR22", &status);
-    if (name == NULL && strchr(icuCanonicalName, ',') != NULL) {
-        name = ucnv_getAlias(icuCanonicalName, 1, &status);
-    }
-    // If there is no UTR22 canonical name then just return the original name.
-    if (name == NULL) {
-        name = icuCanonicalName;
-    }
-    UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
-    strcpy(&result[0], "x-");
-    strcat(&result[0], name);
-    return env->NewStringUTF(&result[0]);
-}
-
 static jobjectArray NativeConverter_getAvailableCharsetNames(JNIEnv* env, jclass) {
     int32_t num = ucnv_countAvailable();
     jobjectArray result = env->NewObjectArray(num, JniConstants::stringClass, NULL);
@@ -296,57 +338,6 @@
     return result;
 }
 
-static jobjectArray getAliases(JNIEnv* env, const char* icuCanonicalName) {
-    // Get an upper bound on the number of aliases...
-    const char* myEncName = icuCanonicalName;
-    UErrorCode error = U_ZERO_ERROR;
-    size_t aliasCount = ucnv_countAliases(myEncName, &error);
-    if (aliasCount == 0 && myEncName[0] == 'x' && myEncName[1] == '-') {
-        myEncName = myEncName + 2;
-        aliasCount = ucnv_countAliases(myEncName, &error);
-    }
-    if (!U_SUCCESS(error)) {
-        return NULL;
-    }
-
-    // Collect the aliases we want...
-    std::vector<std::string> aliases;
-    for (size_t i = 0; i < aliasCount; ++i) {
-        const char* name = ucnv_getAlias(myEncName, i, &error);
-        if (!U_SUCCESS(error)) {
-            return NULL;
-        }
-        // TODO: why do we ignore these ones?
-        if (strchr(name, '+') == 0 && strchr(name, ',') == 0) {
-            aliases.push_back(name);
-        }
-    }
-    return toStringArray(env, aliases);
-}
-
-static const char* getICUCanonicalName(const char* name) {
-    UErrorCode error = U_ZERO_ERROR;
-    const char* canonicalName = NULL;
-    if ((canonicalName = ucnv_getCanonicalName(name, "MIME", &error)) != NULL) {
-        return canonicalName;
-    } else if((canonicalName = ucnv_getCanonicalName(name, "IANA", &error)) != NULL) {
-        return canonicalName;
-    } else if((canonicalName = ucnv_getCanonicalName(name, "", &error)) != NULL) {
-        return canonicalName;
-    } else if((canonicalName =  ucnv_getAlias(name, 0, &error)) != NULL) {
-        /* we have some aliases in the form x-blah .. match those first */
-        return canonicalName;
-    } else if (strstr(name, "x-") == name) {
-        /* check if the converter can be opened with the name given */
-        error = U_ZERO_ERROR;
-        UniqueUConverter cnv(ucnv_open(name + 2, &error));
-        if (cnv.get() != NULL) {
-            return name + 2;
-        }
-    }
-    return NULL;
-}
-
 static void CHARSET_ENCODER_CALLBACK(const void* rawContext, UConverterFromUnicodeArgs* args,
         const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason,
         UErrorCode* status) {
@@ -544,13 +535,13 @@
     }
 
     UErrorCode errorCode = U_ZERO_ERROR;
-    UniqueUConverter converter1(ucnv_open(name1Chars.c_str(), &errorCode));
+    LocalUConverterPointer converter1(ucnv_open(name1Chars.c_str(), &errorCode));
     UnicodeSet set1;
-    ucnv_getUnicodeSet(converter1.get(), set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
+    ucnv_getUnicodeSet(&*converter1, set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
 
-    UniqueUConverter converter2(ucnv_open(name2Chars.c_str(), &errorCode));
+    LocalUConverterPointer converter2(ucnv_open(name2Chars.c_str(), &errorCode));
     UnicodeSet set2;
-    ucnv_getUnicodeSet(converter2.get(), set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
+    ucnv_getUnicodeSet(&*converter2, set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
 
     return U_SUCCESS(errorCode) && set1.containsAll(set2);
 }
@@ -560,11 +551,13 @@
     if (charsetNameChars.c_str() == NULL) {
         return NULL;
     }
+
     // Get ICU's canonical name for this charset.
     const char* icuCanonicalName = getICUCanonicalName(charsetNameChars.c_str());
     if (icuCanonicalName == NULL) {
         return NULL;
     }
+
     // Get Java's canonical name for this charset.
     jstring javaCanonicalName = getJavaCanonicalName(env, icuCanonicalName);
     if (env->ExceptionCheck()) {
@@ -572,17 +565,30 @@
     }
 
     // Check that this charset is supported.
-    // ICU doesn't offer any "isSupported", so we just open and immediately close.
-    // We ignore the UErrorCode because ucnv_open returning NULL is all the information we need.
-    UErrorCode dummy = U_ZERO_ERROR;
-    UniqueUConverter cnv(ucnv_open(icuCanonicalName, &dummy));
-    if (cnv.get() == NULL) {
-        return NULL;
+    {
+        // ICU doesn't offer any "isSupported", so we just open and immediately close.
+        UErrorCode error = U_ZERO_ERROR;
+        LocalUConverterPointer cnv(ucnv_open(icuCanonicalName, &error));
+        if (!U_SUCCESS(error)) {
+            return NULL;
+        }
     }
-    cnv.reset();
 
     // Get the aliases for this charset.
-    jobjectArray aliases = getAliases(env, icuCanonicalName);
+    std::vector<std::string> aliases;
+    if (!collectStandardNames(env, icuCanonicalName, "IANA", aliases)) {
+        return NULL;
+    }
+    if (!collectStandardNames(env, icuCanonicalName, "MIME", aliases)) {
+        return NULL;
+    }
+    if (!collectStandardNames(env, icuCanonicalName, "JAVA", aliases)) {
+        return NULL;
+    }
+    if (!collectStandardNames(env, icuCanonicalName, "WINDOWS", aliases)) {
+        return NULL;
+    }
+    jobjectArray javaAliases = toStringArray(env, aliases);
     if (env->ExceptionCheck()) {
         return NULL;
     }
@@ -594,7 +600,7 @@
         return NULL;
     }
     return env->NewObject(JniConstants::charsetICUClass, charsetConstructor,
-            javaCanonicalName, env->NewStringUTF(icuCanonicalName), aliases);
+            javaCanonicalName, env->NewStringUTF(icuCanonicalName), javaAliases);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/luni/src/main/native/libcore_icu_Transliterator.cpp b/luni/src/main/native/libcore_icu_Transliterator.cpp
index f2a54cb..0c52053 100644
--- a/luni/src/main/native/libcore_icu_Transliterator.cpp
+++ b/luni/src/main/native/libcore_icu_Transliterator.cpp
@@ -46,7 +46,8 @@
 
 static jobjectArray Transliterator_getAvailableIDs(JNIEnv* env, jclass) {
   UErrorCode status = U_ZERO_ERROR;
-  return fromStringEnumeration(env, Transliterator::getAvailableIDs(status));
+  StringEnumeration* e = Transliterator::getAvailableIDs(status);
+  return fromStringEnumeration(env, status, "Transliterator::getAvailableIDs", e);
 }
 
 static jstring Transliterator_transliterate(JNIEnv* env, jclass, jlong peer, jstring javaString) {
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 7093a68..cf1b714 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "Posix"
 
 #include "AsynchronousSocketCloseMonitor.h"
+#include "cutils/log.h"
 #include "ExecStrings.h"
 #include "JNIHelp.h"
 #include "JniConstants.h"
diff --git a/luni/src/main/native/libcore_net_RawSocket.cpp b/luni/src/main/native/libcore_net_RawSocket.cpp
index 2e493ac..f8a8506 100644
--- a/luni/src/main/native/libcore_net_RawSocket.cpp
+++ b/luni/src/main/native/libcore_net_RawSocket.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "RawSocket"
+
 #include "AsynchronousSocketCloseMonitor.h"
+#include "cutils/log.h"
 #include "JNIHelp.h"
 #include "JniException.h"
 #include "JniConstants.h"
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index e576d1a..8a1d7c5 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -10,6 +10,7 @@
 	JniException.cpp \
 	NetworkUtilities.cpp \
 	Register.cpp \
+	ZipUtilities.cpp \
 	cbigint.cpp \
 	java_io_Console.cpp \
 	java_io_File.cpp \
diff --git a/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java b/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java
index 899bd46..801db4b 100644
--- a/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java
+++ b/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java
@@ -51,6 +51,8 @@
 
     // Kanji (sorts to inflow section)
     assertHasLabel(ja, "\u65e5", "");
+    // http://bugs.icu-project.org/trac/ticket/10423 / http://b/10809397
+    assertHasLabel(ja, "\u95c7", "");
 
     // English
     assertHasLabel(ja, "Smith", "S");
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index 8de3407..93be0ce 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -26,39 +26,45 @@
 import static libcore.icu.DateIntervalFormat.*;
 
 public class DateIntervalFormatTest extends junit.framework.TestCase {
-  private static final long MINUTE_DURATION = 60 * 1000;
-  private static final long HOUR_DURATION = 60 * MINUTE_DURATION;
-  private static final long DAY_DURATION = 24 * HOUR_DURATION;
-  private static final long MONTH_DURATION = 31 * DAY_DURATION;
-  private static final long YEAR_DURATION = 12 * MONTH_DURATION;
+  private static final long MINUTE = 60 * 1000;
+  private static final long HOUR = 60 * MINUTE;
+  private static final long DAY = 24 * HOUR;
+  private static final long MONTH = 31 * DAY;
+  private static final long YEAR = 12 * MONTH;
 
+  // These are the old CTS tests for DateIntervalFormat.formatDateRange.
   public void test_formatDateInterval() throws Exception {
-    Date date = new Date(109, 0, 19, 3, 30, 15);
-    long fixedTime = date.getTime();
+    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
 
-    int currentYear = Calendar.getInstance().get(Calendar.YEAR);
-    Date dateWithCurrentYear = new Date(currentYear - 1900, 0, 19, 3, 30, 15);
-    long timeWithCurrentYear = dateWithCurrentYear.getTime();
+    Calendar c = Calendar.getInstance(tz);
+    c.set(Calendar.MONTH, Calendar.JANUARY);
+    c.set(Calendar.DAY_OF_MONTH, 19);
+    c.set(Calendar.HOUR_OF_DAY, 3);
+    c.set(Calendar.MINUTE, 30);
+    c.set(Calendar.SECOND, 15);
+    long timeWithCurrentYear = c.getTimeInMillis();
+
+    c.set(Calendar.YEAR, 2009);
+    long fixedTime = c.getTimeInMillis();
+
+    c.set(Calendar.MINUTE, 0);
+    c.set(Calendar.SECOND, 0);
+    long onTheHour = c.getTimeInMillis();
 
     long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000;
     long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000;
-    long integralDuration = 30 * 60 * 1000 + 15 * 1000;
-
-    // These are the old CTS tests for DateIntervalFormat.formatDateRange.
 
     Locale de_DE = new Locale("de", "DE");
     Locale en_US = new Locale("en", "US");
     Locale es_ES = new Locale("es", "ES");
     Locale es_US = new Locale("es", "US");
 
-    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-
-    assertEquals("Monday", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR_DURATION, FORMAT_SHOW_WEEKDAY));
-    assertEquals("January 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR_DURATION, FORMAT_SHOW_DATE));
+    assertEquals("Monday", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY));
+    assertEquals("January 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, FORMAT_SHOW_DATE));
     assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME));
-    assertEquals("January 19, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR_DURATION, FORMAT_SHOW_YEAR));
-    assertEquals("January 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR_DURATION, 0 /*FORMAT_NO_YEAR*/));
-    assertEquals("January", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR_DURATION, FORMAT_NO_MONTH_DAY));
+    assertEquals("January 19, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR));
+    assertEquals("January 19", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_NO_YEAR));
+    assertEquals("January", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, FORMAT_NO_MONTH_DAY));
     assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_12HOUR | FORMAT_SHOW_TIME));
     assertEquals("03:30", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_24HOUR | FORMAT_SHOW_TIME));
     assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_12HOUR /*| FORMAT_CAP_AMPM*/ | FORMAT_SHOW_TIME));
@@ -67,113 +73,254 @@
     assertEquals("12:00 PM", formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, FORMAT_12HOUR /*| FORMAT_NO_NOON*/ | FORMAT_SHOW_TIME));
     assertEquals("12:00 AM", formatDateRange(en_US, tz, fixedTime - midnightDuration, fixedTime - midnightDuration, FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_NO_MIDNIGHT*/));
     assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME | FORMAT_UTC));
-    assertEquals("3 AM", formatDateRange(en_US, tz, fixedTime - integralDuration, fixedTime - integralDuration, FORMAT_SHOW_TIME | FORMAT_ABBREV_TIME));
-    assertEquals("Mon", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY));
-    assertEquals("Jan 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH));
-    assertEquals("Jan 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("3 AM", formatDateRange(en_US, tz, onTheHour, onTheHour, FORMAT_SHOW_TIME | FORMAT_ABBREV_TIME));
+    assertEquals("Mon", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY));
+    assertEquals("Jan 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH));
+    assertEquals("Jan 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
 
-    assertEquals("1/19/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * HOUR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("1/19/2009 – 1/22/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("1/19/2009 – 4/22/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("1/19/2009 – 2/9/2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("1/19/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("1/19/2009 – 1/22/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("1/19/2009 – 4/22/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("1/19/2009 – 2/9/2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
 
-    assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19.01.2009 - 22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19.01.2009 - 22.04.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19.01.2009 - 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19.01.2009 - 22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19.01.2009 - 22.04.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19.01.2009 - 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
 
-    assertEquals("1/19/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19/1/2009 – 22/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19/1/2009 – 22/4/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19/1/2009 – 9/2/2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("1/19/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009 – 22/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009 – 22/4/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009 – 9/2/2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
 
-    assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19/1/2009 – 22/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19/1/2009 – 22/4/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
-    assertEquals("19/1/2009 – 9/2/2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009 – 22/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009 – 22/4/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/2009 – 9/2/2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
 
     // These are some random other test cases I came up with.
 
-    assertEquals("January 19–22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, 0));
-    assertEquals("Jan 19–22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("Mon, Jan 19 – Thu, Jan 22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("Monday, January 19 – Thursday, January 22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("January 19–22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+    assertEquals("Jan 19–22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("Mon, Jan 19 – Thu, Jan 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("Monday, January 19 – Thursday, January 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
-    assertEquals("January 19 – April 22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, 0));
-    assertEquals("Jan 19 – Apr 22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("Mon, Jan 19 – Wed, Apr 22", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("January–April", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_NO_MONTH_DAY));
+    assertEquals("January 19 – April 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+    assertEquals("Jan 19 – Apr 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("Mon, Jan 19 – Wed, Apr 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("January–April 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("Jan 19, 2009 – Feb 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("Jan 2009 – Feb 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
-    assertEquals("January 19, 2009 – February 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, 0));
-    assertEquals("Monday, January 19, 2009 – Thursday, February 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("Jan 19, 2009 – Feb 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("Jan 2009 – Feb 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+    assertEquals("January 19, 2009 – February 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+    assertEquals("Monday, January 19, 2009 – Thursday, February 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
 
     // The same tests but for de_DE.
 
-    assertEquals("19.-22. Januar", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY_DURATION, 0));
-    assertEquals("19.-22. Jan.", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("Mo., 19. - Do., 22. Jan.", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("Montag, 19. - Donnerstag, 22. Januar", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19.-22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
+    assertEquals("19.-22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("Mo., 19. - Do., 22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("Montag, 19. - Donnerstag, 22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
-    assertEquals("19. Januar - 22. April", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, 0));
-    assertEquals("19. Jan. - 22. Apr.", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("Mo., 19. Jan. - Mi., 22. Apr.", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("Januar-April", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_NO_MONTH_DAY));
+    assertEquals("19. Januar - 22. April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+    assertEquals("19. Jan. - 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("Mo., 19. Jan. - Mi., 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("Januar-April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("19. Jan. 2009 - 9. Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("Jan. 2009 - Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
-    assertEquals("19. Januar 2009 - 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, 0));
-    assertEquals("Montag, 19. Januar 2009 - Donnerstag, 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19. Jan. 2009 - 9. Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("Jan. 2009 - Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+    assertEquals("19. Januar 2009 - 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+    assertEquals("Montag, 19. Januar 2009 - Donnerstag, 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
 
     // The same tests but for es_US.
 
-    assertEquals("19–22 enero", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, 0));
-    assertEquals("19–22 ene", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun 19 ene – jue 22 ene", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("lunes 19 enero – jueves 22 enero", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19–22 enero 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+    assertEquals("19–22 ene 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun 19 ene – jue 22 ene 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("lunes 19 enero – jueves 22 enero 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
-    assertEquals("19 enero – 22 abril", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, 0));
-    assertEquals("19 ene – 22 abr", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun 19 ene – mié 22 abr", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("enero–abril", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_NO_MONTH_DAY));
+    assertEquals("19 enero – 22 abril 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+    assertEquals("19 ene – 22 abr 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun 19 ene – mié 22 abr 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("enero–abril 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("ene 2009 – feb 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
-    assertEquals("19 enero 2009 – 9 febrero 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, 0));
-    assertEquals("lunes 19 enero 2009 – jueves 9 febrero 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("ene 2009 – feb 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+    assertEquals("19 enero 2009 – 9 febrero 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+    assertEquals("lunes 19 enero 2009 – jueves 9 febrero 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
 
     // The same tests but for es_ES.
 
-    assertEquals("19–22 enero", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY_DURATION, 0));
-    assertEquals("19–22 ene", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun 19 ene – jue 22 ene", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("lunes 19 enero – jueves 22 enero", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19–22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
+    assertEquals("19–22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun 19 ene – jue 22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("lunes 19 enero – jueves 22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
-    assertEquals("19 enero – 22 abril", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, 0));
-    assertEquals("19 ene – 22 abr", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun 19 ene – mié 22 abr", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("enero–abril", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH_DURATION, FORMAT_NO_MONTH_DAY));
+    assertEquals("19 enero – 22 abril 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+    assertEquals("19 ene – 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun 19 ene – mié 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("enero–abril 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("ene 2009 – feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
-    assertEquals("19 enero 2009 – 9 febrero 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, 0));
-    assertEquals("lunes 19 enero 2009 – jueves 9 febrero 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR_DURATION, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("ene 2009 – feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+    assertEquals("19 enero 2009 – 9 febrero 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+    assertEquals("lunes 19 enero 2009 – jueves 9 febrero 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
   }
 
+  // http://b/8862241 - we should be able to format dates past 2038.
+  // See also http://code.google.com/p/android/issues/detail?id=13050.
   public void test8862241() throws Exception {
-    // Test the post-2038 future (http://b/8862241).
+    Locale l = Locale.US;
     TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-    long jan_19_2042 = new Date(2042 - 1900, 0, 19, 3, 30, 15).getTime();
-    long oct_4_2046 = new Date(2046 - 1900, 9, 4, 3, 30, 15).getTime();
-    assertEquals("Jan 19, 2042 – Oct 4, 2046", formatDateRange(Locale.US, tz, jan_19_2042, oct_4_2046, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    Calendar c = Calendar.getInstance(tz);
+    c.set(2042, Calendar.JANUARY, 19, 3, 30);
+    long jan_19_2042 = c.getTimeInMillis();
+    c.set(2046, Calendar.OCTOBER, 4, 3, 30);
+    long oct_4_2046 = c.getTimeInMillis();
+    int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL;
+    assertEquals("Jan 19, 2042 – Oct 4, 2046", formatDateRange(l, tz, jan_19_2042, oct_4_2046, flags));
   }
 
+  // http://b/10089890 - we should take the given time zone into account.
   public void test10089890() throws Exception {
-    // Test that we actually take the time zone into account.
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
+    int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
     // The Unix epoch is UTC, so 0 is 1970-01-01T00:00Z...
-    assertEquals("Jan 1–2", formatDateRange(0, 24*60*60 * 1000L, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL, "Europe/London"));
+    assertEquals("Jan 1, 1970, 00:00 – Jan 2, 1970, 00:00", formatDateRange(l, utc, 0, DAY + 1, flags));
     // But MTV is hours behind, so 0 was still the afternoon of the previous day...
-    assertEquals("Dec 31, 1969 – Jan 1, 1970", formatDateRange(0, 24*60*60 * 1000L, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL, "America/Los_Angeles"));
+    assertEquals("Dec 31, 1969, 16:00 – Jan 1, 1970, 16:00", formatDateRange(l, pacific, 0, DAY, flags));
+  }
+
+  // http://b/10318326 - we can drop the minutes in a 12-hour time if they're zero,
+  // but not if we're using the 24-hour clock. That is: "4 PM" is reasonable, "16" is not.
+  public void test10318326() throws Exception {
+    long midnight = 0;
+    long teaTime = 16 * HOUR;
+
+    int time12 = FORMAT_12HOUR | FORMAT_SHOW_TIME;
+    int time24 = FORMAT_24HOUR | FORMAT_SHOW_TIME;
+    int abbr12 = time12 | FORMAT_ABBREV_ALL;
+    int abbr24 = time24 | FORMAT_ABBREV_ALL;
+
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+
+    // Full length on-the-hour times.
+    assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, time24));
+    assertEquals("12:00 AM", formatDateRange(l, utc, midnight, midnight, time12));
+    assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, time24));
+    assertEquals("4:00 PM", formatDateRange(l, utc, teaTime, teaTime, time12));
+
+    // Abbreviated on-the-hour times.
+    assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, abbr24));
+    assertEquals("12 AM", formatDateRange(l, utc, midnight, midnight, abbr12));
+    assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, abbr24));
+    assertEquals("4 PM", formatDateRange(l, utc, teaTime, teaTime, abbr12));
+
+    // Abbreviated on-the-hour ranges.
+    assertEquals("00:00–16:00", formatDateRange(l, utc, midnight, teaTime, abbr24));
+    assertEquals("12 AM – 4 PM", formatDateRange(l, utc, midnight, teaTime, abbr12));
+
+    // Abbreviated mixed ranges.
+    assertEquals("00:00–16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24));
+    assertEquals("12:00 AM – 4:01 PM", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr12));
+  }
+
+  // http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is
+  // considered to belong to the previous day.
+  public void test10560853_when_time_not_displayed() throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+
+    long midnight = 0;
+    long midnightNext = 1 * DAY;
+
+    int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY;
+
+    // An all-day event runs until 0 milliseconds into the next day, but is formatted as if it's
+    // just the first day.
+    assertEquals("Thursday, January 1, 1970", formatDateRange(l, utc, midnight, midnightNext, flags));
+
+    // Run one millisecond over, though, and you're into the next day.
+    long nextMorning = 1 * DAY + 1;
+    assertEquals("Thursday, January 1 – Friday, January 2, 1970", formatDateRange(l, utc, midnight, nextMorning, flags));
+
+    // But the same reasoning applies for that day.
+    long nextMidnight = 2 * DAY;
+    assertEquals("Thursday, January 1 – Friday, January 2, 1970", formatDateRange(l, utc, midnight, nextMidnight, flags));
+  }
+
+  // http://b/10560853 - when the start and end times are otherwise on the same day,
+  // an end time 0 ms into the next day is considered to belong to the previous day.
+  public void test10560853_for_single_day_events() throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+
+    int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+    assertEquals("January 1, 1970, 22:00–00:00", formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags));
+    assertEquals("January 1, 1970, 22:00 – January 2, 1970, 00:30", formatDateRange(l, utc, 22 * HOUR, 24 * HOUR + 30 * MINUTE, flags));
+  }
+
+  // http://b/10209343 - even if the caller didn't explicitly ask us to include the year,
+  // we should do so for years other than the current year.
+  public void test10209343_when_not_this_year() {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+
+    int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+    assertEquals("Thursday, January 1, 1970, 00:00", formatDateRange(l, utc, 0L, 0L, flags));
+
+    long t1833 = ((long) Integer.MIN_VALUE + Integer.MIN_VALUE) * 1000L;
+    assertEquals("Sunday, November 24, 1833, 17:31", formatDateRange(l, utc, t1833, t1833, flags));
+
+    long t1901 = Integer.MIN_VALUE * 1000L;
+    assertEquals("Friday, December 13, 1901, 20:45", formatDateRange(l, utc, t1901, t1901, flags));
+
+    long t2038 = Integer.MAX_VALUE * 1000L;
+    assertEquals("Tuesday, January 19, 2038, 03:14", formatDateRange(l, utc, t2038, t2038, flags));
+
+    long t2106 = (2L + Integer.MAX_VALUE + Integer.MAX_VALUE) * 1000L;
+    assertEquals("Sunday, February 7, 2106, 06:28", formatDateRange(l, utc, t2106, t2106, flags));
+  }
+
+  // http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags.
+  public void test10209343_when_this_year() {
+    // Construct a date in the current year (whenever the test happens to be run).
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    Calendar c = Calendar.getInstance(utc);
+    c.set(Calendar.MONTH, Calendar.FEBRUARY);
+    c.set(Calendar.DAY_OF_MONTH, 10);
+    c.set(Calendar.HOUR_OF_DAY, 0);
+    long thisYear = c.getTimeInMillis();
+
+    // You don't get the year if it's this year...
+    assertEquals("February 10", formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE));
+
+    // ...unless you explicitly ask for it.
+    assertEquals(String.format("February 10, %d", c.get(Calendar.YEAR)),
+                 formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR));
+
+    // ...or it's not actually this year...
+    Calendar c2 = (Calendar) c.clone();
+    c2.set(Calendar.YEAR, 1980);
+    long oldYear = c2.getTimeInMillis();
+    assertEquals("February 10, 1980", formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE));
+
+    // (But you can disable that!)
+    assertEquals("February 10", formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR));
+
+    // ...or the start and end years aren't the same...
+    assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)),
+                 formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE));
+
+    // (And you can't avoid that --- icu4c steps in and overrides you.)
+    assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)),
+                 formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR));
   }
 }
diff --git a/luni/src/test/java/libcore/java/lang/StringTest.java b/luni/src/test/java/libcore/java/lang/StringTest.java
index 605fd63..e16496b 100644
--- a/luni/src/test/java/libcore/java/lang/StringTest.java
+++ b/luni/src/test/java/libcore/java/lang/StringTest.java
@@ -348,4 +348,13 @@
         assertEquals("-w-o-r-l-d-", "hello world".substring(6).replace("", "-"));
         assertEquals("-*-w-*-o-*-r-*-l-*-d-*-", "hello world".substring(6).replace("", "-*-"));
     }
+
+    // http://b/11571917
+    public void test_String_getBytes() throws Exception {
+        assertEquals("[-126, -96]", Arrays.toString("あ".getBytes("Shift_JIS")));
+        assertEquals("[-126, -87]", Arrays.toString("か".getBytes("Shift_JIS")));
+        assertEquals("[-105, 67]", Arrays.toString("佑".getBytes("Shift_JIS")));
+        assertEquals("[36]", Arrays.toString("$".getBytes("Shift_JIS")));
+        assertEquals("[-29, -127, -117]", Arrays.toString("か".getBytes("UTF-8")));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java b/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java
index bdde645..b7c4bc6 100644
--- a/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java
+++ b/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java
@@ -16,12 +16,17 @@
 
 package libcore.java.net;
 
-import java.net.*;
-import java.util.Arrays;
+import junit.framework.TestCase;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
-import junit.framework.TestCase;
 
 public class NetworkInterfaceTest extends TestCase {
     // http://code.google.com/p/android/issues/detail?id=13784
@@ -36,6 +41,35 @@
         assertEquals(expected, actual);
     }
 
+    // http://code.google.com/p/android/issues/detail?id=34022
+    public void test_collectIpv6Addresses_3digitInterfaceIndex() throws Exception {
+        String lines[] = new String[] {
+                "fe800000000000000000000000000000 407 40 20 80    wlan0" };
+        List<InetAddress> addresses = new ArrayList<InetAddress>(1);
+        List<InterfaceAddress> ifAddresses = new ArrayList<InterfaceAddress>(1);
+
+        NetworkInterface.collectIpv6Addresses("wlan0", 1, addresses,
+                ifAddresses, lines);
+        assertEquals(1, addresses.size());
+        assertEquals(1, ifAddresses.size());
+        // Make sure the prefix length (field #3) is parsed correctly
+        assertEquals(4*16 + 0, ifAddresses.get(0).getNetworkPrefixLength());
+    }
+
+    public void test_collectIpv6Addresses_skipsUnmatchedLines() throws Exception {
+        String[] lines = new String[] {
+                "fe800000000000000000000000000000 40 40 20 80    wlan0",
+                "fe100000000000000000000000000000 41 40 20 80    wlan1",
+                "feb00000000000000000000000000000 42 40 20 80    wlan2" };
+        List<InetAddress> addresses = new ArrayList<InetAddress>(1);
+        List<InterfaceAddress> ifAddresses = new ArrayList<InterfaceAddress>(1);
+
+        NetworkInterface.collectIpv6Addresses("wlan0", 1, addresses,
+                ifAddresses, lines);
+        assertEquals(1, addresses.size());
+        assertEquals(1, ifAddresses.size());
+    }
+
     public void testLoopback() throws Exception {
         // We know lo shouldn't have a hardware address or an IPv4 broadcast address.
         NetworkInterface lo = NetworkInterface.getByName("lo");
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index d39e39f..7ecdc59 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -74,6 +74,7 @@
 import libcore.javax.net.ssl.TestSSLContext;
 import tests.net.StuckServer;
 
+import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AFTER_READING_REQUEST;
 import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
 import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
 import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
@@ -335,26 +336,36 @@
     }
 
     public void testRetryableRequestBodyAfterBrokenConnection() throws Exception {
-        server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END));
-        server.enqueue(new MockResponse().setBody("def"));
+        // Use SSL to make an alternate route available.
+        TestSSLContext testSSLContext = TestSSLContext.create();
+        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+
+        server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(
+            DISCONNECT_AFTER_READING_REQUEST));
+        server.enqueue(new MockResponse().setBody("abc"));
         server.play();
 
-        assertContent("abc", server.getUrl("/a").openConnection());
-        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection();
+        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("").openConnection();
+        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
         connection.setDoOutput(true);
         OutputStream out = connection.getOutputStream();
         out.write(new byte[] {1, 2, 3});
         out.close();
-        assertContent("def", connection);
+        assertContent("abc", connection);
+
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+        assertEquals(0, server.takeRequest().getSequenceNumber());
     }
 
     public void testNonRetryableRequestBodyAfterBrokenConnection() throws Exception {
-        server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END));
-        server.enqueue(new MockResponse().setBody("def"));
+        TestSSLContext testSSLContext = TestSSLContext.create();
+        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+        server.enqueue(new MockResponse().setBody("abc")
+            .setSocketPolicy(DISCONNECT_AFTER_READING_REQUEST));
         server.play();
 
-        assertContent("abc", server.getUrl("/a").openConnection());
-        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection();
+        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/a").openConnection();
+        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
         connection.setDoOutput(true);
         connection.setFixedLengthStreamingMode(3);
         OutputStream out = connection.getOutputStream();
@@ -520,6 +531,11 @@
 
         assertContent("this response comes via SSL", connection);
 
+        // The first request will be an incomplete (bookkeeping) request
+        // that the server disconnected from at start.
+        server.takeRequest();
+
+        // The request will be retried.
         RecordedRequest request = server.takeRequest();
         assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
     }
@@ -688,10 +704,17 @@
         initResponseCache();
 
         server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
-        server.enqueue(new MockResponse()
+        MockResponse badProxyResponse = new MockResponse()
                 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
                 .clearHeaders()
-                .setBody("bogus proxy connect response content")); // Key to reproducing b/6754912
+                .setBody("bogus proxy connect response content"); // Key to reproducing b/6754912
+
+        // We enqueue the bad response twice because the connection will
+        // be retried with TLS_MODE_COMPATIBLE after the first connection
+        // fails.
+        server.enqueue(badProxyResponse);
+        server.enqueue(badProxyResponse);
+
         server.play();
 
         URL url = new URL("https://android.com/foo");
@@ -701,7 +724,7 @@
         try {
             connection.connect();
             fail();
-        } catch (IOException expected) {
+        } catch (SSLHandshakeException 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.
@@ -1381,11 +1404,37 @@
         }
     }
 
+    // bug 11473660
+    public void testAuthenticateWithLowerCaseHeadersAndScheme() throws Exception {
+        MockResponse pleaseAuthenticate = new MockResponse()
+                .setResponseCode(401)
+                .addHeader("www-authenticate: basic realm=\"protected area\"")
+                .setBody("Please authenticate.");
+        // fail auth three times...
+        server.enqueue(pleaseAuthenticate);
+        server.enqueue(pleaseAuthenticate);
+        server.enqueue(pleaseAuthenticate);
+        // ...then succeed the fourth time
+        server.enqueue(new MockResponse().setBody("Successful auth!"));
+        server.play();
+
+        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);
+    }
+
     // 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\", "
+                .addHeader("WWW-Authenticate: Scheme1 realm=\"a\", Basic realm=\"b\", "
                         + "Scheme3 realm=\"c\"")
                 .setBody("Please authenticate."));
         server.enqueue(new MockResponse().setBody("Successful auth!"));
@@ -1399,15 +1448,15 @@
 
         assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*");
         assertContains(server.takeRequest().getHeaders(),
-                "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS);
-        assertEquals("Scheme2", authenticator.requestingScheme);
+                "Authorization: Basic " + SimpleAuthenticator.BASE_64_CREDENTIALS);
+        assertEquals("Basic", 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: Basic realm=\"b\"")
                 .addHeader("WWW-Authenticate: Scheme3 realm=\"c\"")
                 .setBody("Please authenticate."));
         server.enqueue(new MockResponse().setBody("Successful auth!"));
@@ -1421,8 +1470,8 @@
 
         assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*");
         assertContains(server.takeRequest().getHeaders(),
-                "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS);
-        assertEquals("Scheme2", authenticator.requestingScheme);
+                "Authorization: Basic " + SimpleAuthenticator.BASE_64_CREDENTIALS);
+        assertEquals("Basic", authenticator.requestingScheme);
     }
 
     public void testRedirectedWithChunkedEncoding() throws Exception {
@@ -1641,9 +1690,12 @@
      * addresses. This is typically one IPv4 address and one IPv6 address.
      */
     public void testConnectTimeouts() throws IOException {
-        StuckServer ss = new StuckServer(false);
+        StuckServer ss = new StuckServer(true);
         int serverPort = ss.getLocalPort();
-        URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection();
+        String hostName = ss.getLocalSocketAddress().getAddress().getHostAddress();
+        URLConnection urlConnection = new URL("http://" + hostName + ":" + serverPort + "/")
+                .openConnection();
+
         int timeout = 1000;
         urlConnection.setConnectTimeout(timeout);
         long start = System.currentTimeMillis();
@@ -1835,19 +1887,25 @@
         }
     }
 
-    public void testGetKeepAlive() throws Exception {
+    public void testReadTimeoutsOnRecycledConnections() throws Exception {
         server.enqueue(new MockResponse().setBody("ABC"));
         server.play();
 
         // The request should work once and then fail
         URLConnection connection = server.getUrl("").openConnection();
+        // Read timeout of a day, sure to cause the test to timeout and fail.
+        connection.setReadTimeout(24 * 3600 * 1000);
         InputStream input = connection.getInputStream();
         assertEquals("ABC", readAscii(input, Integer.MAX_VALUE));
         input.close();
         try {
-            server.getUrl("").openConnection().getInputStream();
+            connection = server.getUrl("").openConnection();
+            // Set the read timeout back to 100ms, this request will time out
+            // because we've only enqueued one response.
+            connection.setReadTimeout(100);
+            connection.getInputStream();
             fail();
-        } catch (ConnectException expected) {
+        } catch (IOException expected) {
         }
     }
 
@@ -1910,7 +1968,22 @@
     }
 
     public void testLenientUrlToUriNul() throws Exception {
-        testUrlToUriMapping("\u0000", "%00", "%00", "%00", "%00"); // RI fails this
+        // On JB-MR2 and below, we would allow a host containing \u0000
+        // and then generate a request with a Host header that violated RFC2616.
+        // We now reject such hosts.
+        //
+        // The ideal behaviour here is to be "lenient" about the host and rewrite
+        // it, but attempting to do so introduces a new range of incompatible
+        // behaviours.
+        testUrlToUriMapping("\u0000", null, "%00", "%00", "%00"); // RI fails this
+    }
+
+    public void testHostWithNul() throws Exception {
+        URL url = new URL("http://host\u0000/");
+        try {
+            url.openStream();
+            fail();
+        } catch (IllegalArgumentException expected) {}
     }
 
     private void testUrlToUriMapping(String string, String asAuthority, String asFile,
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 ec20c2a..b45f8e1 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -124,7 +124,7 @@
             thread.start();
 
             // select doesn't ever return, so await() times out and returns false
-            assertFalse(selectReturned.await(500, TimeUnit.MILLISECONDS));
+            assertFalse(selectReturned.await(2, TimeUnit.SECONDS));
         } finally {
             selector.close();
         }
diff --git a/luni/src/test/java/libcore/java/nio/charset/CharsetTest.java b/luni/src/test/java/libcore/java/nio/charset/CharsetTest.java
deleted file mode 100644
index ddc2b29..0000000
--- a/luni/src/test/java/libcore/java/nio/charset/CharsetTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * 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.java.nio.charset;
-
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.util.Arrays;
-import java.util.ArrayList;
-
-public class CharsetTest extends junit.framework.TestCase {
-    public void test_guaranteedCharsetsAvailable() throws Exception {
-        // All Java implementations must support these charsets.
-        assertNotNull(Charset.forName("ISO-8859-1"));
-        assertNotNull(Charset.forName("US-ASCII"));
-        assertNotNull(Charset.forName("UTF-16"));
-        assertNotNull(Charset.forName("UTF-16BE"));
-        assertNotNull(Charset.forName("UTF-16LE"));
-        assertNotNull(Charset.forName("UTF-8"));
-    }
-
-    // http://code.google.com/p/android/issues/detail?id=42769
-    public void test_42769() throws Exception {
-        ArrayList<Thread> threads = new ArrayList<Thread>();
-        for (int i = 0; i < 10; ++i) {
-            Thread t = new Thread(new Runnable() {
-                public void run() {
-                    for (int i = 0; i < 50; ++i) {
-                        Charset.availableCharsets();
-                    }
-                }
-            });
-            threads.add(t);
-        }
-
-        for (Thread t : threads) {
-            t.start();
-        }
-        for (Thread t : threads) {
-            t.join();
-        }
-    }
-
-    public void test_allAvailableCharsets() throws Exception {
-        // Check that we can instantiate every Charset, CharsetDecoder, and CharsetEncoder.
-        for (String charsetName : Charset.availableCharsets().keySet()) {
-            if (charsetName.equals("UTF-32")) {
-                // Our UTF-32 is broken. http://b/2702411
-                // TODO: remove this hack when UTF-32 is fixed.
-                continue;
-            }
-
-            Charset cs = Charset.forName(charsetName);
-            assertNotNull(cs.newDecoder());
-            if (cs.canEncode()) {
-                CharsetEncoder enc = cs.newEncoder();
-                assertNotNull(enc);
-                assertNotNull(enc.replacement());
-            }
-        }
-    }
-
-    public void test_EUC_JP() throws Exception {
-        assertEncodes(Charset.forName("EUC-JP"), "\ufffd", 0xf4, 0xfe);
-    }
-
-    public void test_SCSU() throws Exception {
-        assertEncodes(Charset.forName("SCSU"), "\ufffd", 14, 0xff, 0xfd);
-    }
-
-    public void test_Shift_JIS() throws Exception {
-        assertEncodes(Charset.forName("Shift_JIS"), "\ufffd", 0xfc, 0xfc);
-    }
-
-    public void test_UTF_16() throws Exception {
-        Charset cs = Charset.forName("UTF-16");
-        // Writes big-endian, with a big-endian BOM.
-        assertEncodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
-        // Reads whatever the BOM tells it to read...
-        assertDecodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
-        assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
-        // ...and defaults to reading big-endian if there's no BOM.
-        assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
-    }
-
-    public void test_UTF_16BE() throws Exception {
-        Charset cs = Charset.forName("UTF-16BE");
-        // Writes big-endian, with no BOM.
-        assertEncodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
-        // Treats a little-endian BOM as an error and continues to read big-endian.
-        // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
-        assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 'a', 0x06, 0x66);
-        // Accepts a big-endian BOM and includes U+FEFF in the decoded output.
-        assertDecodes(cs, "\ufeffa\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
-        // Defaults to reading big-endian.
-        assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
-    }
-
-    public void test_UTF_16LE() throws Exception {
-        Charset cs = Charset.forName("UTF-16LE");
-        // Writes little-endian, with no BOM.
-        assertEncodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
-        // Accepts a little-endian BOM and includes U+FEFF in the decoded output.
-        assertDecodes(cs, "\ufeffa\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
-        // Treats a big-endian BOM as an error and continues to read little-endian.
-        // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
-        assertDecodes(cs, "\ufffda\u0666", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
-        // Defaults to reading little-endian.
-        assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
-    }
-
-    public void test_x_UTF_16LE_BOM() throws Exception {
-        Charset cs = Charset.forName("x-UTF-16LE-BOM");
-        // Writes little-endian, with a BOM.
-        assertEncodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
-        // Accepts a little-endian BOM and swallows the BOM.
-        assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
-        // Swallows a big-endian BOM, but continues to read little-endian!
-        assertDecodes(cs, "\u6100\u6606", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
-        // Defaults to reading little-endian.
-        assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
-    }
-
-    public void test_UTF_32() throws Exception {
-        Charset cs = Charset.forName("UTF-32");
-        // Writes big-endian, with no BOM.
-        assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Reads whatever the BOM tells it to read...
-        assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // ...and defaults to reading big-endian if there's no BOM.
-        assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-    }
-
-    public void test_UTF_32BE() throws Exception {
-        Charset cs = Charset.forName("UTF-32BE");
-        // Writes big-endian, with no BOM.
-        assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Treats a little-endian BOM as an error and continues to read big-endian.
-        // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
-        assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Accepts a big-endian BOM and swallows the BOM.
-        assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Defaults to reading big-endian.
-        assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-    }
-
-    public void test_UTF_32LE() throws Exception {
-        Charset cs = Charset.forName("UTF-32LE");
-        // Writes little-endian, with no BOM.
-        assertEncodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // Accepts a little-endian BOM and swallows the BOM.
-        assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // Treats a big-endian BOM as an error and continues to read little-endian.
-        // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
-        assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // Defaults to reading little-endian.
-        assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-    }
-
-    public void test_X_UTF_32BE_BOM() throws Exception {
-        Charset cs = Charset.forName("X-UTF-32BE-BOM");
-        // Writes big-endian, with a big-endian BOM.
-        assertEncodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Treats a little-endian BOM as an error and continues to read big-endian.
-        // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
-        assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Swallows a big-endian BOM, and continues to read big-endian.
-        assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-        // Defaults to reading big-endian.
-        assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
-    }
-
-    public void test_X_UTF_32LE_BOM() throws Exception {
-        Charset cs = Charset.forName("X-UTF-32LE-BOM");
-        // Writes little-endian, with a little-endian BOM.
-        assertEncodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // Accepts a little-endian BOM and swallows the BOM.
-        assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // Treats a big-endian BOM as an error and continues to read little-endian.
-        // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
-        assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-        // Defaults to reading little-endian.
-        assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
-    }
-
-    private byte[] toByteArray(int[] ints) {
-        byte[] result = new byte[ints.length];
-        for (int i = 0; i < ints.length; ++i) {
-            result[i] = (byte) ints[i];
-        }
-        return result;
-    }
-
-    private void assertEncodes(Charset cs, String s, int... expectedByteInts) throws Exception {
-        ByteBuffer out = cs.encode(s);
-        byte[] bytes = new byte[out.remaining()];
-        out.get(bytes);
-        assertEquals(Arrays.toString(toByteArray(expectedByteInts)), Arrays.toString(bytes));
-    }
-
-    private void assertDecodes(Charset cs, String s, int... byteInts) throws Exception {
-        ByteBuffer in = ByteBuffer.wrap(toByteArray(byteInts));
-        CharBuffer out = cs.decode(in);
-        assertEquals(s, out.toString());
-    }
-}
diff --git a/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java b/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java
index 411727e..d4cf83e 100644
--- a/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java
+++ b/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java
@@ -177,7 +177,12 @@
         encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
         decoder.onMalformedInput(CodingErrorAction.REPORT);
         CharBuffer inputCB = CharBuffer.allocate(65536);
-        for (int code = 32; code <= 65533; code ++) {
+        for (int code = 32; code <= 65533; ++code) {
+            // icu4c seems to accept any surrogate as a sign that "more is coming",
+            // even for charsets like US-ASCII. http://b/10310751
+            if (code >= 0xd800 && code <= 0xdfff) {
+                continue;
+            }
             if (encoder.canEncode((char) code)) {
                 inputCB.put((char) code);
             }
diff --git a/luni/src/test/java/libcore/java/nio/charset/OldCharset_GSM0338.java b/luni/src/test/java/libcore/java/nio/charset/OldCharset_GSM0338.java
deleted file mode 100644
index a8d2669..0000000
--- a/luni/src/test/java/libcore/java/nio/charset/OldCharset_GSM0338.java
+++ /dev/null
@@ -1,57 +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 libcore.java.nio.charset;
-
-import java.nio.charset.CharacterCodingException;
-
-/** Note: this test is GSM specific */
-public class OldCharset_GSM0338 extends OldCharset_AbstractTest {
-
-    @Override
-    protected void setUp() throws Exception {
-//        charsetName = "GSM0338";
-        charsetName = "x-gsm-03.38-2000";
-
-        testChars = theseChars(new int[]{
-10, 13, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
-46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
-78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95, 97, 98,
-99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
-115, 116, 117, 118, 119, 120, 121, 122, 161, 163, 164, 165, 167, 191, 196, 197,
-198, 201, 209, 214, 216, 220, 223, 224, 228, 229, 230, 231, 232, 233, 236, 241,
-242, 246, 248, 249, 252, 915, 916
-            });
-
-        testBytes = theseBytes(new int[]{
-10, 13, 32, 33, 34, 35, 2, 37, 38, 39, 40, 41, 42, 43, 44, 45,
-46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-62, 63, 0, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
-78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 17, 97, 98,
-99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
-115, 116, 117, 118, 119, 120, 121, 122, 64, 1, 36, 3, 95, 96, 91, 14,
-28, 31, 93, 92, 11, 94, 30, 127, 123, 15, 29, 9, 4, 5, 7, 125,
-8, 124, 12, 6, 126, 19, 16
-            });
-
-        super.setUp();
-    }
-
-    @Override
-    public void test_CodecDynamic () throws CharacterCodingException {
-    }
-
-}
diff --git a/luni/src/test/java/libcore/java/nio/charset/OldCharset_ISO_8859_16.java b/luni/src/test/java/libcore/java/nio/charset/OldCharset_ISO_8859_16.java
deleted file mode 100644
index edb219a..0000000
--- a/luni/src/test/java/libcore/java/nio/charset/OldCharset_ISO_8859_16.java
+++ /dev/null
@@ -1,62 +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 libcore.java.nio.charset;
-
-/** Note: ICU only */
-public class OldCharset_ISO_8859_16 extends OldCharset_AbstractTest {
-
-    @Override
-    protected void setUp() throws Exception {
-        charsetName = "ISO-8859-16";
-
-        testChars = theseChars(new int[]{
-0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
-112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
-128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
-144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
-160, 167, 169, 171, 173, 176, 177, 182, 183, 187, 192, 193, 194, 196, 198, 199,
-200, 201, 202, 203, 204, 205, 206, 207, 210, 211, 212, 214, 217, 218, 219, 220,
-223, 224, 225, 226, 228, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 242,
-243, 244, 246, 249, 250, 251, 252, 255, 258, 321, 352, 382, 536, 8221, 8364
-            });
-
-        testBytes = theseBytes(new int[]{
-0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
-112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
-128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
-144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
-160, 167, 169, 171, 173, 176, 177, 182, 183, 187, 192, 193, 194, 196, 198, 199,
-200, 201, 202, 203, 204, 205, 206, 207, 210, 211, 212, 214, 217, 218, 219, 220,
-223, 224, 225, 226, 228, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 242,
-243, 244, 246, 249, 250, 251, 252, 255, 195, 163, 166, 184, 170, 181, 164
-            });
-
-        super.setUp();
-    }
-
-}
diff --git a/luni/src/test/java/libcore/java/security/MessageDigestTest.java b/luni/src/test/java/libcore/java/security/MessageDigestTest.java
index cd7b8db..3646a7a 100644
--- a/luni/src/test/java/libcore/java/security/MessageDigestTest.java
+++ b/luni/src/test/java/libcore/java/security/MessageDigestTest.java
@@ -141,12 +141,9 @@
                                     99, -71, 49, -67, 71, 65, 122, -127,
                                     -91, 56, 50, 122, -7, 39, -38, 62 });
 
-        // Regression test input for problem SHA-1 with input of
-        // 256MBs.  However, its currently too slow to be practical on
-        // devices, so its disabled in that case. http://b/4501620
-        boolean enabled256mb = (System.getProperty("os.arch").contains("x86") // host
-                                || System.getProperty("os.arch").contains("amd64")); // RI
-        if (enabled256mb) {
+        // Regression test for a SHA-1 problem with inputs larger than 256 MiB. http://b/4501620
+        // In mid-2013 this takes 3 minutes even on the host, so let's not run it on devices.
+        if (System.getenv("ANDROID_BUILD_TOP") != null) {
             // INPUT_256MB
             putExpectation("MD2",
                            INPUT_256MB,
diff --git a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
index 21d7302..2813cee 100644
--- a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
@@ -346,4 +346,11 @@
         Date d2 = sdf.parse(formatted);
         assertEquals(d, d2);
     }
+
+    public void test_59383() throws Exception {
+        SimpleDateFormat sdf = new SimpleDateFormat("d. MMM yyyy H:mm", Locale.GERMAN);
+        sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
+        assertEquals(1376927400000L, sdf.parse("19. Aug 2013 8:50").getTime());
+        assertEquals(1376927400000L, sdf.parse("19. Aug. 2013 8:50").getTime());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java b/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java
index 2e6905b..2a9e5ef 100644
--- a/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java
+++ b/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java
@@ -26,8 +26,6 @@
   // http://code.google.com/p/android/issues/detail?id=36519
   public void test_toArray() throws Exception {
     final ConcurrentHashMap<Integer, Integer> m = new ConcurrentHashMap<Integer, Integer>();
-    assertTrue(m.values() instanceof AbstractCollection);
-
     final AtomicBoolean finished = new AtomicBoolean(false);
 
     Thread reader = new Thread(new Runnable() {
diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index 8504eaf..61a22fd 100644
--- a/luni/src/test/java/libcore/java/util/CurrencyTest.java
+++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java
@@ -59,8 +59,6 @@
         assertEquals("Schweizer Franken", Currency.getInstance("CHF").getDisplayName(new Locale("de", "CH")));
         assertEquals("franc suisse", Currency.getInstance("CHF").getDisplayName(new Locale("fr", "CH")));
         assertEquals("Franco Svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH")));
-        // Test behavior with unknown locales; should return the currency code.
-        assertEquals("CHF", Currency.getInstance("CHF").getDisplayName(new Locale("xx", "YY")));
     }
 
     public void test_getDefaultFractionDigits() throws Exception {
diff --git a/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java b/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
index 4bd858c..fa761d3 100644
--- a/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
@@ -75,7 +75,6 @@
     }
 
     // This one makes sure we have all necessary locales installed.
-    // Suppress this flaky test for now.
     public void testICULocales() {
         String[] locales = new String[] {
                 // List of locales currently required for Android.
@@ -131,7 +130,6 @@
                 "ISO-8859-13",
                 "ISO-8859-14",
                 "ISO-8859-15",
-                "ISO-8859-16",
                 "ISO-2022-JP",
                 "Windows-950",
                 "Windows-1250",
@@ -153,32 +151,16 @@
                 "GBK",
                 "GB2312",
                 "EUC-KR",
+                "GSM0338"
+        };
 
-                // Additional encoding not included in standard ICU.
-                "GSM0338" };
+        for (String encoding : encodings) {
+            assertTrue("Charset " + encoding + " must be supported", Charset.isSupported(encoding));
 
-        for (int i = 0; i < encodings.length; i++) {
-            assertTrue("Charset " + encodings[i] + " must be supported",
-                    Charset.isSupported(encodings[i]));
-
-            Charset cs = Charset.forName(encodings[i]);
-
+            Charset cs = Charset.forName(encoding);
             Set<String> aliases = cs.aliases();
-            System.out.println(aliases);
+            System.out.println(cs.name() + ": " + aliases);
         }
-
-        // Test for valid encoding that is not included in Android. IBM-37 is
-        // a perfect candidate for this, as it is being used for mainframes and
-        // thus somewhat out of the scope of Android.
-        assertFalse("Charset IBM-37 must not be supported",
-                Charset.isSupported("IBM-37"));
-
-        // Test for a bogus encoding.
-        assertFalse("Charset KLINGON must not be supported",
-                Charset.isSupported("KLINGON"));
-
-        // Check we have the canonical EUC-JP charset.
-        assertEquals("EUC-JP", Charset.forName("EUC-JP").name());
     }
 
 }
diff --git a/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java b/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java
index de24cf9..26527aa 100644
--- a/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java
+++ b/luni/src/test/java/libcore/java/util/OldPriorityQueueTest.java
@@ -104,4 +104,20 @@
         }
     }
 
+    /**
+     * removeAt(.) must sometimes perform siftUp(.).
+     */
+    public void test_removeAt_siftUp() {
+        PriorityQueue<Integer> q = new PriorityQueue<Integer>();
+        // Adding a valid heap will keep elements in the same order
+        for (int i : new int[] { 0, 3, 1, 4, 5, 6, 2 }) {
+            q.add(i);
+        }
+        q.remove(4);  // 2 replaces 4 but parent is 3, siftUp(.) is needed
+        for (int i : new int[] { 0, 1, 2, 3, 5, 6 }) {
+            assertEquals(i, (int) q.poll());
+        }
+        assertNull(q.poll());
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/util/OldScannerTest.java b/luni/src/test/java/libcore/java/util/OldScannerTest.java
deleted file mode 100644
index 4f63d92..0000000
--- a/luni/src/test/java/libcore/java/util/OldScannerTest.java
+++ /dev/null
@@ -1,442 +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 libcore.java.util;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.StringReader;
-import java.net.InetSocketAddress;
-import java.nio.CharBuffer;
-import java.nio.channels.IllegalBlockingModeException;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.Arrays;
-import java.util.NoSuchElementException;
-import java.util.Scanner;
-import java.util.regex.MatchResult;
-import java.util.regex.Pattern;
-import junit.framework.TestCase;
-
-public final class OldScannerTest extends TestCase {
-
-    public void test_findWithinHorizon_Ljava_lang_StringI() {
-        // This method searches through the input up to the specified search
-        // horizon(exclusive).
-        Scanner s = new Scanner("123test");
-        String result = s.findWithinHorizon("\\p{Lower}", 5);
-        assertEquals("t", result);
-        MatchResult mresult = s.match();
-        assertEquals(3, mresult.start());
-        assertEquals(4, mresult.end());
-
-        s = new Scanner("12345test1234test next");
-        /*
-         * If the pattern is found the scanner advances past the input that
-         * matched and returns the string that matched the pattern.
-         */
-        result = s.findWithinHorizon("\\p{Digit}+", 2);
-        assertEquals("12", result);
-        mresult = s.match();
-        assertEquals(0, mresult.start());
-        assertEquals(2, mresult.end());
-        // Position is now pointing at the bar. "12|345test1234test next"
-
-        result = s.findWithinHorizon("\\p{Digit}+", 6);
-        assertEquals("345", result);
-
-        mresult = s.match();
-        assertEquals(2, mresult.start());
-        assertEquals(5, mresult.end());
-        // Position is now pointing at the bar. "12345|test1234test next"
-
-        // If no such pattern is detected then the null is returned and the
-        // scanner's position remains unchanged.
-        result = s.findWithinHorizon("\\p{Digit}+", 3);
-        assertNull(result);
-
-        try {
-            s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
-        }
-        assertEquals("345", mresult.group());
-        assertEquals(2, mresult.start());
-        assertEquals(5, mresult.end());
-        // Position is now still pointing at the bar. "12345|test1234test next"
-
-        // If horizon is 0, then the horizon is ignored and this method
-        // continues to search through the input looking for the specified
-        // pattern without bound.
-        result = s.findWithinHorizon("\\p{Digit}+", 0);
-        mresult = s.match();
-        assertEquals(9, mresult.start());
-        assertEquals(13, mresult.end());
-        // Position is now pointing at the bar. "12345test1234|test next"
-
-        assertEquals("test", s.next());
-        mresult = s.match();
-        assertEquals(13, mresult.start());
-        assertEquals(17, mresult.end());
-
-        assertEquals("next", s.next());
-        mresult = s.match();
-        assertEquals(18, mresult.start());
-        assertEquals(22, mresult.end());
-
-        try {
-            s.findWithinHorizon((String)null, 1);
-            fail("Should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
-        }
-
-        try {
-            s.findWithinHorizon("\\p{Digit}+", -1);
-            fail("Should throw IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-
-        s.close();
-        // on RI throws NullPointerException
-        /*
-         * try { System.out.println("fsdfs"); s.findWithinHorizon((String) null,
-         * -1); System.out.println("fsdfs"); fail("Should throw
-         * IllegalStateException"); } catch (IllegalStateException e) { //
-         * expected }
-         */
-        s = new Scanner("test");
-        result = s.findWithinHorizon("\\w+", 10);
-        assertEquals("test", result);
-
-        s = new Scanner("aa\n\rb");
-        String patternStr = "^(a)$";
-        result = s.findWithinHorizon("a", 5);
-        assertEquals("a", result);
-        mresult = s.match();
-        assertEquals(0, mresult.start());
-        assertEquals(1, mresult.end());
-
-        result = s.findWithinHorizon(patternStr, 5);
-        assertNull(result);
-
-        try {
-            mresult = s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
-        }
-
-        s = new Scanner("");
-        result = s.findWithinHorizon("^", 0);
-        assertEquals("", result);
-        MatchResult matchResult = s.match();
-        assertEquals(0, matchResult.start());
-        assertEquals(0, matchResult.end());
-
-        result = s.findWithinHorizon("$", 0);
-        assertEquals("", result);
-        matchResult = s.match();
-        assertEquals(0, matchResult.start());
-        assertEquals(0, matchResult.end());
-
-        s = new Scanner("1 fish 2 fish red fish blue fish");
-        result = s.findWithinHorizon("(\\d+) fish (\\d+) fish (\\w+) fish (\\w+)", 10);
-        assertNull(result);
-
-        try {
-            mresult = s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
-        }
-        assertEquals(0, mresult.groupCount());
-
-        result = s.findWithinHorizon("(\\d+) fish (\\d+) fish (\\w+) fish (\\w+)", 100);
-        assertEquals("1 fish 2 fish red fish blue", result);
-        mresult = s.match();
-        assertEquals(4, mresult.groupCount());
-        assertEquals("1", mresult.group(1));
-        assertEquals("2", mresult.group(2));
-        assertEquals("red", mresult.group(3));
-        assertEquals("blue", mresult.group(4));
-
-        s = new Scanner("test");
-        s.close();
-        try {
-            s.findWithinHorizon("test", 1);
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
-        }
-
-        s = new Scanner("word1 WorD2  ");
-        s.close();
-        try {
-            s.findWithinHorizon("\\d+", 10);
-            fail("should throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            // expected
-        }
-
-        s = new Scanner("word1 WorD2 wOrd3 ");
-        patternStr = "\\d+";
-        assertEquals("1", s.findWithinHorizon(patternStr, 10));
-        assertEquals("WorD2", s.next());
-        assertEquals("3", s.findWithinHorizon(patternStr, 15));
-
-        // Regression test
-        s = new Scanner(new MockStringReader("MockStringReader"));
-        patternStr = "test";
-        result = s.findWithinHorizon(patternStr, 10);
-        assertEquals("test", result);
-
-        // Test the situation when input length is longer than buffer size.
-        StringBuilder stringBuilder = new StringBuilder();
-        for (int i = 0; i < 1026; i++) {
-            stringBuilder.append('a');
-        }
-        s = new Scanner(stringBuilder.toString());
-        patternStr = "\\p{Lower}+";
-        result = s.findWithinHorizon(patternStr, 1026);
-        assertEquals(stringBuilder.toString(), result);
-
-        // Test the situation when input length is longer than buffer size and
-        // set horizon to buffer size.
-        stringBuilder = new StringBuilder();
-        for (int i = 0; i < 1026; i++) {
-            stringBuilder.append('a');
-        }
-        s = new Scanner(stringBuilder.toString());
-        patternStr = "\\p{Lower}+";
-        result = s.findWithinHorizon(patternStr, 1022);
-        assertEquals(1022, result.length());
-        assertEquals(stringBuilder.subSequence(0, 1022), result);
-
-        // Test the situation, under which pattern is clipped by buffer.
-        stringBuilder = new StringBuilder();
-        for (int i = 0; i < 1022; i++) {
-            stringBuilder.append(' ');
-        }
-        stringBuilder.append("bbc");
-        assertEquals(1025, stringBuilder.length());
-        s = new Scanner(stringBuilder.toString());
-        patternStr = "bbc";
-        result = s.findWithinHorizon(patternStr, 1025);
-        assertEquals(3, result.length());
-        assertEquals(stringBuilder.subSequence(1022, 1025), result);
-
-        stringBuilder = new StringBuilder();
-        for (int i = 0; i < 1026; i++) {
-            stringBuilder.append('a');
-        }
-        s = new Scanner(stringBuilder.toString());
-        patternStr = "\\p{Lower}+";
-        result = s.findWithinHorizon(patternStr, 0);
-        assertEquals(stringBuilder.toString(), result);
-
-        stringBuilder = new StringBuilder();
-        for (int i = 0; i < 10240; i++) {
-            stringBuilder.append('-');
-        }
-        stringBuilder.replace(0, 2, "aa");
-        s = new Scanner(stringBuilder.toString());
-        result = s.findWithinHorizon("aa", 0);
-        assertEquals("aa", result);
-
-        s = new Scanner("aaaa");
-        result = s.findWithinHorizon("a*", 0);
-        assertEquals("aaaa", result);
-    }
-
-    public void test_findInLine_LString() {
-        Scanner s = new Scanner("");
-        String result = s.findInLine("^");
-        assertEquals("", result);
-        MatchResult matchResult = s.match();
-        assertEquals(0, matchResult.start());
-        assertEquals(0, matchResult.end());
-
-        result = s.findInLine("$");
-        assertEquals("", result);
-        matchResult = s.match();
-        assertEquals(0, matchResult.start());
-        assertEquals(0, matchResult.end());
-
-        /*
-         * When we use the operation of findInLine(Pattern), the match region
-         * should not span the line separator.
-         */
-        s = new Scanner("aa\nb.b");
-        result = s.findInLine("a\nb*");
-        assertNull(result);
-
-        s = new Scanner("aa\nbb.b");
-        result = s.findInLine("\\.");
-        assertNull(result);
-
-        s = new Scanner("abcd1234test\n");
-        result = s.findInLine("\\p{Lower}+");
-        assertEquals("abcd", result);
-        matchResult = s.match();
-        assertEquals(0, matchResult.start());
-        assertEquals(4, matchResult.end());
-
-        result = s.findInLine("\\p{Digit}{5}");
-        assertNull(result);
-        try {
-            matchResult = s.match();
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException expected) {
-        }
-        assertEquals(0, matchResult.start());
-        assertEquals(4, matchResult.end());
-
-        result = s.findInLine("\\p{Lower}+");
-        assertEquals("test", result);
-        matchResult = s.match();
-        assertEquals(8, matchResult.start());
-        assertEquals(12, matchResult.end());
-
-        char[] chars = new char[2048];
-        Arrays.fill(chars, 'a');
-        StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append(chars);
-        stringBuilder.append("1234");
-        s = new Scanner(stringBuilder.toString());
-        result = s.findInLine("\\p{Digit}+");
-        assertEquals("1234", result);
-        matchResult = s.match();
-        assertEquals(2048, matchResult.start());
-        assertEquals(2052, matchResult.end());
-
-        s = new Scanner("test1234\n1234 test");
-        result = s.findInLine("test");
-        assertEquals("test", result);
-        matchResult = s.match();
-        assertEquals(0, matchResult.start());
-        assertEquals(4, matchResult.end());
-
-        int number = s.nextInt();
-        assertEquals(1234, number);
-        matchResult = s.match();
-        assertEquals(4, matchResult.start());
-        assertEquals(8, matchResult.end());
-
-        result = s.next();
-        assertEquals("1234", result);
-        matchResult = s.match();
-        assertEquals(9, matchResult.start());
-        assertEquals(13, matchResult.end());
-
-        result = s.findInLine("test");
-        assertEquals("test", result);
-        matchResult = s.match();
-        assertEquals(14, matchResult.start());
-        assertEquals(18, matchResult.end());
-
-        s = new Scanner("test\u0085\ntest");
-        result = s.findInLine("est");
-        assertEquals("est", result);
-        result = s.findInLine("est");
-        assertEquals("est", result);
-
-        s = new Scanner("test\ntest");
-        result = s.findInLine("est");
-        assertEquals("est", result);
-        result = s.findInLine("est");
-        assertEquals("est", result);
-
-        s = new Scanner("test\n123\ntest");
-        result = s.findInLine("est");
-        assertEquals("est", result);
-        result = s.findInLine("est");
-    }
-
-    public void test_Constructor_LReadableByteChannel() throws IOException {
-        ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
-
-        SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
-        sc.configureBlocking(false);
-        assertFalse(sc.isBlocking());
-
-        ssc.accept().close();
-        ssc.close();
-        assertFalse(sc.isBlocking());
-
-        Scanner s = new Scanner(sc);
-        try {
-            s.hasNextInt();
-            fail();
-        } catch (IllegalBlockingModeException expected) {
-        }
-
-        sc.close();
-    }
-
-    private static class MockStringReader extends StringReader {
-
-        public MockStringReader(String param) {
-            super(param);
-        }
-
-        public int read(CharBuffer target) throws IOException {
-            target.append('t');
-            target.append('e');
-            target.append('s');
-            target.append('t');
-            throw new IOException();
-        }
-    }
-
-  // http://code.google.com/p/android/issues/detail?id=57050
-  public void testPerformance() throws Exception {
-    int count = 100000;
-
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(baos));
-    for (int i = 0; i < count; ++i) {
-      out.write(Integer.toString(123) + " ");
-    }
-    out.close();
-
-    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-    bais.mark(-1);
-
-    Scanner s = new Scanner(new BufferedReader(new InputStreamReader(bais)));
-    for (int i = 0; i < count; ++i) {
-      if (s.nextInt() != 123) {
-        fail();
-      }
-    }
-
-    bais.reset();
-    s = new Scanner(new BufferedReader(new InputStreamReader(bais)));
-    for (int i = 0; i < count; ++i) {
-      if (s.nextFloat() != 123.0) {
-        fail();
-      }
-    }
-  }
-}
diff --git a/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java b/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java
index 56bb651..b69a2dd 100644
--- a/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java
+++ b/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java
@@ -17,23 +17,54 @@
 package libcore.java.util;
 
 import java.util.Iterator;
+import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
 
 public class ServiceLoaderTest extends junit.framework.TestCase {
-    public static interface UnimplementedInterface { }
-    public void test_noImplementations() {
-        assertFalse(ServiceLoader.load(UnimplementedInterface.class).iterator().hasNext());
-    }
+  public static interface UnimplementedInterface { }
+  public void test_noImplementations() {
+    assertFalse(ServiceLoader.load(UnimplementedInterface.class).iterator().hasNext());
+  }
 
-    public static class Impl1 implements ServiceLoaderTestInterface { }
-    public static class Impl2 implements ServiceLoaderTestInterface { }
-    public void test_implementations() {
-        ServiceLoader<ServiceLoaderTestInterface> loader = ServiceLoader.load(ServiceLoaderTestInterface.class);
-        Iterator<ServiceLoaderTestInterface> it = loader.iterator();
-        assertTrue(it.hasNext());
-        assertTrue(it.next() instanceof Impl1);
-        assertTrue(it.hasNext());
-        assertTrue(it.next() instanceof Impl2);
-        assertFalse(it.hasNext());
+  public static class Impl1 implements ServiceLoaderTestInterface { }
+  public static class Impl2 implements ServiceLoaderTestInterface { }
+  public void test_implementations() {
+    ServiceLoader<ServiceLoaderTestInterface> loader = ServiceLoader.load(ServiceLoaderTestInterface.class);
+    Iterator<ServiceLoaderTestInterface> it = loader.iterator();
+    assertTrue(it.hasNext());
+    assertTrue(it.next() instanceof Impl1);
+    assertTrue(it.hasNext());
+    assertTrue(it.next() instanceof Impl2);
+    assertFalse(it.hasNext());
+  }
+
+  // Something like "does.not.Exist", that is a well-formed class name, but just doesn't exist.
+  public void test_missingRegisteredClass() throws Exception {
+    try {
+      ServiceLoader.load(ServiceLoaderTestInterfaceMissingClass.class).iterator().next();
+      fail();
+    } catch (ServiceConfigurationError expected) {
+      assertTrue(expected.getCause() instanceof ClassNotFoundException);
     }
+  }
+
+  // Something like "java.lang.String", that is a class that exists,
+  // but doesn't implement the interface.
+  public void test_wrongTypeRegisteredClass() throws Exception {
+    try {
+      ServiceLoader.load(ServiceLoaderTestInterfaceWrongType.class).iterator().next();
+      fail();
+    } catch (ServiceConfigurationError expected) {
+      assertTrue(expected.getCause() instanceof ClassCastException);
+    }
+  }
+
+  // Something like "This is not a class name!" that's just a parse error.
+  public void test_invalidRegisteredClass() throws Exception {
+    try {
+      ServiceLoader.load(ServiceLoaderTestInterfaceParseError.class).iterator().next();
+      fail();
+    } catch (ServiceConfigurationError expected) {
+    }
+  }
 }
diff --git a/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceMissingClass.java b/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceMissingClass.java
new file mode 100644
index 0000000..e2ceffd
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceMissingClass.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util;
+
+/**
+ * Ideally, this would have been an inner class in ServiceLoaderTest, but we need a corresponding
+ * resource file, and our build system can't cope with $ in filenames.
+ */
+public interface ServiceLoaderTestInterfaceMissingClass {
+}
diff --git a/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceParseError.java b/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceParseError.java
new file mode 100644
index 0000000..2a4dfcc
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceParseError.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util;
+
+/**
+ * Ideally, this would have been an inner class in ServiceLoaderTest, but we need a corresponding
+ * resource file, and our build system can't cope with $ in filenames.
+ */
+public interface ServiceLoaderTestInterfaceParseError {
+}
diff --git a/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceWrongType.java b/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceWrongType.java
new file mode 100644
index 0000000..7d3ddd4
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/ServiceLoaderTestInterfaceWrongType.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util;
+
+/**
+ * Ideally, this would have been an inner class in ServiceLoaderTest, but we need a corresponding
+ * resource file, and our build system can't cope with $ in filenames.
+ */
+public interface ServiceLoaderTestInterfaceWrongType {
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
index 0845b32..550ddfb 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
@@ -169,4 +169,27 @@
     assertEquals(maxLengthComment, zipFile.getEntry("x").getComment());
     zipFile.close();
   }
+
+  public void testCommentAndExtraInSameOrder() throws Exception {
+    String comment = makeString(17, "z");
+    byte[] extra = makeString(11, "a").getBytes();
+
+    File f = createTemporaryZipFile();
+    ZipOutputStream out = createZipOutputStream(f);
+    ZipEntry ze = new ZipEntry("x");
+    ze.setExtra(extra);
+    ze.setComment(comment);
+    out.putNextEntry(ze);
+    out.closeEntry();
+    out.close();
+
+    // Read it back and make sure comments and extra are in the right order
+    ZipFile zipFile = new ZipFile(f);
+    try {
+      assertEquals(comment, zipFile.getEntry("x").getComment());
+      assertTrue(Arrays.equals(extra, zipFile.getEntry("x").getExtra()));
+    } finally {
+      zipFile.close();
+    }
+  }
 }
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 cad881b..60af4d0 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -118,6 +118,51 @@
         }
     }
 
+    /**
+     * Make sure the size used for stored zip entires is the uncompressed size.
+     * b/10227498
+     */
+    public void testStoredEntrySize() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ZipOutputStream out = new ZipOutputStream(baos);
+
+        // Set up a single stored entry.
+        String name = "test_file";
+        int expectedLength = 5;
+        ZipEntry outEntry = new ZipEntry(name);
+        byte[] buffer = new byte[expectedLength];
+        outEntry.setMethod(ZipEntry.STORED);
+        CRC32 crc = new CRC32();
+        crc.update(buffer);
+        outEntry.setCrc(crc.getValue());
+        outEntry.setSize(buffer.length);
+
+        out.putNextEntry(outEntry);
+        out.write(buffer);
+        out.closeEntry();
+        out.close();
+
+        // Write the result to a file.
+        byte[] outBuffer = baos.toByteArray();
+        File zipFile = createTemporaryZipFile();
+        writeBytes(zipFile, outBuffer);
+
+        ZipFile zip = new ZipFile(zipFile);
+        // Set up the zip entry to have different compressed/uncompressed sizes.
+        ZipEntry ze = zip.getEntry(name);
+        ze.setCompressedSize(expectedLength - 1);
+        // Read the contents of the stream and verify uncompressed size was used.
+        InputStream stream = zip.getInputStream(ze);
+        int count = 0;
+        int read;
+        while ((read = stream.read(buffer)) != -1) {
+            count += read;
+        }
+
+        assertEquals(expectedLength, count);
+
+    }
+
     public void testInflatingStreamsRequiringZipRefill() throws IOException {
         int originalSize = 1024 * 1024;
         byte[] readBuffer = new byte[8192];
@@ -378,35 +423,6 @@
         assertEquals(null, zipFile.getComment());
     }
 
-    public void testNameLengthChecks() throws IOException {
-        // Is entry name length checking done on bytes or characters?
-        // Really it should be bytes, but the RI only checks characters at construction time.
-        // Android does the same, because it's cheap...
-        try {
-            new ZipEntry((String) null);
-            fail();
-        } catch (NullPointerException expected) {
-        }
-        new ZipEntry(makeString(0xffff, "a"));
-        try {
-            new ZipEntry(makeString(0xffff + 1, "a"));
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-
-        // ...but Android won't let you create a zip file with a truncated name.
-        ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
-        ZipEntry ze = new ZipEntry(makeString(0xffff, "\u0666"));
-        try {
-            out.putNextEntry(ze);
-            fail(); // The RI fails this test; it just checks the character count at construction time.
-        } catch (IllegalArgumentException expected) {
-        }
-        out.closeEntry();
-        out.putNextEntry(new ZipEntry("okay")); // ZipOutputStream.close throws if you add nothing!
-        out.close();
-    }
-
     // https://code.google.com/p/android/issues/detail?id=58465
     public void test_NUL_in_filename() throws Exception {
         File file = createTemporaryZipFile();
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java b/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java
index 40fd8fd..28d95f2 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java
@@ -24,6 +24,7 @@
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 import junit.framework.TestCase;
 
@@ -71,4 +72,13 @@
         }
         return out.toByteArray();
     }
+
+    public void testCipherInputStream_TruncatedInput_Failure() throws Exception {
+        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(new byte[16], "AES"),
+                new IvParameterSpec(new byte[16]));
+        InputStream is = new CipherInputStream(new ByteArrayInputStream(new byte[31]), cipher);
+        is.read(new byte[4]);
+        is.close();
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index dac43a2..7922a04 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -21,6 +21,7 @@
 import java.io.PrintStream;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -778,8 +779,10 @@
 
         // Cipher.getInstance(String)
         Cipher c1 = Cipher.getInstance(algorithm);
-        assertEquals(algorithm, c1.getAlgorithm());
-        test_Cipher(c1);
+        if (provider.equals(c1.getProvider())) {
+            assertEquals(algorithm, c1.getAlgorithm());
+            test_Cipher(c1);
+        }
 
         // Cipher.getInstance(String, Provider)
         Cipher c2 = Cipher.getInstance(algorithm, provider);
@@ -812,6 +815,23 @@
 
         final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
         int encryptMode = getEncryptMode(algorithm);
+
+        // Bouncycastle doesn't return a default PBEParameterSpec
+        if (isPBE(algorithm) && !"BC".equals(providerName)) {
+            assertNotNull(cipherID + " getParameters()", c.getParameters());
+            assertNotNull(c.getParameters().getParameterSpec(PBEParameterSpec.class));
+        } else {
+            assertNull(cipherID + " getParameters()", c.getParameters());
+        }
+        try {
+            assertNull(cipherID + " getIV()", c.getIV());
+        } catch (NullPointerException e) {
+            // Bouncycastle apparently has a bug here with AESWRAP, et al.
+            if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                throw e;
+            }
+        }
+
         c.init(encryptMode, encryptKey, encryptSpec);
         assertEquals(cipherID + " getBlockSize() encryptMode",
                      getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
@@ -826,9 +846,41 @@
         assertEquals(cipherID + " getOutputSize(0) decryptMode",
                      getExpectedOutputSize(algorithm, decryptMode, providerName), c.getOutputSize(0));
 
-        // TODO: test Cipher.getIV()
+        if (isPBE(algorithm)) {
+            if (algorithm.endsWith("RC4")) {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } else {
+                assertNotNull(cipherID + " getIV()", c.getIV());
+            }
+        } else if (decryptSpec instanceof IvParameterSpec) {
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((IvParameterSpec) decryptSpec).getIV()),
+                    Arrays.toString(c.getIV()));
+        } else {
+            try {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } catch (NullPointerException e) {
+                // Bouncycastle apparently has a bug here with AESWRAP, et al.
+                if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                    throw e;
+                }
+            }
+        }
 
-        // TODO: test Cipher.getParameters()
+        AlgorithmParameters params = c.getParameters();
+        if (decryptSpec == null) {
+            assertNull(cipherID + " getParameters()", params);
+        } else if (decryptSpec instanceof IvParameterSpec) {
+            IvParameterSpec ivDecryptSpec = (IvParameterSpec) params.getParameterSpec(IvParameterSpec.class);
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((IvParameterSpec) decryptSpec).getIV()),
+                    Arrays.toString(ivDecryptSpec.getIV()));
+        } else if (decryptSpec instanceof PBEParameterSpec) {
+            // Bouncycastle seems to be schizophrenic about whther it returns this or not
+            if (!"BC".equals(providerName)) {
+                assertNotNull(cipherID + " getParameters()", params);
+            }
+        }
 
         assertNull(cipherID, c.getExemptionMechanism());
 
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java
index 67c83bf..ba048a8 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java
@@ -96,7 +96,9 @@
         assertTrue(s.invalid.getLastAccessedTime() > 0);
         assertTrue(s.server.getLastAccessedTime() > 0);
         assertTrue(s.client.getLastAccessedTime() > 0);
-        assertTrue(Math.abs(s.server.getLastAccessedTime()
+        assertTrue("s.server.getLastAccessedTime()=" + s.server.getLastAccessedTime() + " " +
+                   "s.client.getLastAccessedTime()=" + s.client.getLastAccessedTime(),
+                   Math.abs(s.server.getLastAccessedTime()
                             - s.client.getLastAccessedTime()) < 1 * 1000);
         assertTrue(s.server.getLastAccessedTime() >=
                    s.server.getCreationTime());
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index de12967..dd56529 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -1239,40 +1239,29 @@
     }
 
     public void test_SSLSocket_interrupt() throws Exception {
-        ServerSocket listening = new ServerSocket(0);
-
-        for (int i = 0; i < 3; i++) {
-            Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
-            Socket server = listening.accept();
-
-            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
-            Socket clientWrapping = sf.createSocket(underlying, null, -1, true);
-
-            switch (i) {
-                case 0:
-                    test_SSLSocket_interrupt_case(underlying, underlying);
-                    break;
-                case 1:
-                    test_SSLSocket_interrupt_case(underlying, clientWrapping);
-                    break;
-                case 2:
-                    test_SSLSocket_interrupt_case(clientWrapping, underlying);
-                    break;
-                case 3:
-                    test_SSLSocket_interrupt_case(clientWrapping, clientWrapping);
-                    break;
-                default:
-                    fail();
-            }
-
-            server.close();
-            underlying.close();
+        test_SSLSocket_interrupt_case(true, true);
+        test_SSLSocket_interrupt_case(true, false);
+        test_SSLSocket_interrupt_case(false, true);
+        // Currently failing due to reader blocking closing thread http://b/10681815
+        if (StandardNames.IS_RI) {
+            test_SSLSocket_interrupt_case(false, false);
         }
-        listening.close();
     }
 
-    private void test_SSLSocket_interrupt_case(Socket toRead, final Socket toClose)
+    private void test_SSLSocket_interrupt_case(boolean readUnderlying, boolean closeUnderlying)
             throws Exception {
+
+        ServerSocket listening = new ServerSocket(0);
+
+        Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
+        Socket server = listening.accept();
+
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        Socket clientWrapping = sf.createSocket(underlying, null, -1, true);
+
+        final Socket toRead = (readUnderlying) ? underlying : clientWrapping;
+        final Socket toClose = (closeUnderlying) ? underlying : clientWrapping;
+
         ExecutorService executor = Executors.newSingleThreadExecutor();
         Future<Void> future = executor.submit(new Callable<Void>() {
             @Override public Void call() throws Exception {
@@ -1291,6 +1280,10 @@
         } catch (SocketException expected) {
         }
         future.get();
+
+        server.close();
+        underlying.close();
+        listening.close();
     }
 
     /**
diff --git a/luni/src/test/java/libcore/javax/net/ssl/X509KeyManagerTest.java b/luni/src/test/java/libcore/javax/net/ssl/X509KeyManagerTest.java
new file mode 100644
index 0000000..a44edd0
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/net/ssl/X509KeyManagerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.net.ssl;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509KeyManager;
+
+import junit.framework.TestCase;
+import libcore.java.security.TestKeyStore;
+
+public class X509KeyManagerTest extends TestCase {
+    /**
+     * Tests whether the key manager will select the right key when the CA is of
+     * one key type and the client is of a possibly different key type.
+     *
+     * <p>There was a bug where EC was being interpreted as EC_EC and only
+     * accepting EC signatures when it should accept any signature type.
+     */
+    public void testChooseClientAlias_Combinations() throws Exception {
+        test_ChooseClientAlias_KeyType("RSA", "RSA", "RSA", true);
+        test_ChooseClientAlias_KeyType("RSA", "EC", "RSA", true);
+        test_ChooseClientAlias_KeyType("RSA", "EC", "EC", false);
+
+        test_ChooseClientAlias_KeyType("EC", "RSA", "EC_RSA", true);
+        test_ChooseClientAlias_KeyType("EC", "EC", "EC_RSA", false);
+
+        test_ChooseClientAlias_KeyType("EC", "EC", "EC_EC", true);
+        test_ChooseClientAlias_KeyType("EC", "RSA", "EC_EC", false);
+
+        test_ChooseClientAlias_KeyType("EC", "RSA", "RSA", false);
+    }
+
+    private void test_ChooseClientAlias_KeyType(String clientKeyType, String caKeyType,
+            String selectedKeyType, boolean succeeds) throws Exception {
+        TestKeyStore ca = new TestKeyStore.Builder()
+                .keyAlgorithms(caKeyType)
+                .build();
+        TestKeyStore client = new TestKeyStore.Builder().keyAlgorithms(clientKeyType)
+                .signer(ca.getPrivateKey(caKeyType, caKeyType))
+                .build();
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
+                .getDefaultAlgorithm());
+        kmf.init(client.keyStore, client.keyPassword);
+
+        String[] keyTypes = new String[] { selectedKeyType };
+        KeyManager[] managers = kmf.getKeyManagers();
+        for (KeyManager manager : managers) {
+            if (manager instanceof X509KeyManager) {
+                String alias = ((X509KeyManager) manager).chooseClientAlias(keyTypes, null, null);
+                if (succeeds) {
+                    assertNotNull(alias);
+                } else {
+                    assertNull(alias);
+                }
+            }
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
index d5f2b6e..28ae416 100644
--- a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
+++ b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
@@ -23,18 +23,22 @@
 
 public class ZoneInfoDBTest extends junit.framework.TestCase {
   private static final String CURRENT_VERSION = ZoneInfoDB.getInstance().getVersion();
-  private static final String DEFAULT_FILE = System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata";
+
+  // Any new file in /data...
+  private static final String TZDATA_IN_DATA = System.getenv("ANDROID_DATA") + "/misc/zoneinfo/tzdata";
+  // ...overrides any existing file in /system.
+  private static final String TZDATA_IN_ROOT = System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata";
 
   // An empty override file should fall back to the default file.
   public void testEmptyOverrideFile() throws Exception {
-    ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(makeEmptyFile(), DEFAULT_FILE);
+    ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(makeEmptyFile(), TZDATA_IN_DATA, TZDATA_IN_ROOT);
     assertEquals(CURRENT_VERSION, data.getVersion());
     assertEquals(TimeZone.getAvailableIDs().length, data.getAvailableIDs().length);
   }
 
   // A corrupt override file should fall back to the default file.
   public void testCorruptOverrideFile() throws Exception {
-    ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(makeCorruptFile(), DEFAULT_FILE);
+    ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(makeCorruptFile(), TZDATA_IN_DATA, TZDATA_IN_ROOT);
     assertEquals(CURRENT_VERSION, data.getVersion());
     assertEquals(TimeZone.getAvailableIDs().length, data.getAvailableIDs().length);
   }
@@ -49,9 +53,11 @@
 
   // Given a valid override file, we should find ourselves using that.
   public void testGoodOverrideFile() throws Exception {
-    RandomAccessFile in = new RandomAccessFile(DEFAULT_FILE, "r");
+    // We copy /system/usr/share/zoneinfo/tzdata because we know that always exists.
+    RandomAccessFile in = new RandomAccessFile(TZDATA_IN_ROOT, "r");
     byte[] content = new byte[(int) in.length()];
     in.readFully(content);
+    // Bump the version number to one long past where humans will be extinct.
     content[6] = '9';
     content[7] = '9';
     content[8] = '9';
@@ -61,7 +67,7 @@
 
     String goodFile = makeTemporaryFile(content);
     try {
-      ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(goodFile, DEFAULT_FILE);
+      ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(goodFile, TZDATA_IN_DATA, TZDATA_IN_ROOT);
       assertEquals("9999z", data.getVersion());
       assertEquals(TimeZone.getAvailableIDs().length, data.getAvailableIDs().length);
     } finally {
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java
index dcd50bb..5d8c621 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java
@@ -261,6 +261,10 @@
             assertTrue("Connection does not use proxy",
                                             connection.usingProxy());
             assertTrue("Proxy server was not used", proxy.accepted);
+
+            connection.disconnect();
+            assertTrue("usingProxy broken after disconnect",
+                   connection.usingProxy());
         } finally {
             // restore default proxy selector
             ProxySelector.setDefault(defPS);
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLEncoderTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLEncoderTest.java
index 1c86303..aa2d9ca 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLEncoderTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLEncoderTest.java
@@ -65,4 +65,19 @@
         } catch (UnsupportedCharsetException expected) {
         }
     }
+
+    // http://b/11571917
+    public void test11571917() throws Exception {
+        assertEquals("%82%A0", URLEncoder.encode("あ", "Shift_JIS"));
+        assertEquals("%82%A9", URLEncoder.encode("か", "Shift_JIS"));
+        assertEquals("%97%43", URLEncoder.encode("佑", "Shift_JIS"));
+        assertEquals("%24", URLEncoder.encode("$", "Shift_JIS"));
+        assertEquals("%E3%81%8B", URLEncoder.encode("か", "UTF-8"));
+
+        assertEquals("%82%A0%82%A9%97%43%24%E3%81%8B", URLEncoder.encode("あ", "Shift_JIS") +
+            URLEncoder.encode("か", "Shift_JIS") +
+            URLEncoder.encode("佑", "Shift_JIS") +
+            URLEncoder.encode("$", "Shift_JIS") +
+            URLEncoder.encode("か", "UTF-8"));
+    }
 }
diff --git a/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
new file mode 100644
index 0000000..d29245f
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
@@ -0,0 +1,2 @@
+tests.api.java.nio.charset.CharsetTest$MockCharsetProvider
+tests.api.java.nio.charset.CharsetTest$MockCharsetProviderASCII
diff --git a/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterface b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterface
index 8f8a9eb..2961ffb 100644
--- a/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterface
+++ b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterface
@@ -5,3 +5,4 @@
 libcore.java.util.ServiceLoaderTest$Impl2 # this comment is valid
 libcore.java.util.ServiceLoaderTest$Impl2#as is this
 libcore.java.util.ServiceLoaderTest$Impl2 # and duplicates are allowed too
+  libcore.java.util.ServiceLoaderTest$Impl2 # as is leading whitespace
diff --git a/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceMissingClass b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceMissingClass
new file mode 100644
index 0000000..253a7b4
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceMissingClass
@@ -0,0 +1 @@
+this.class.does.not.Exist
diff --git a/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceParseError b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceParseError
new file mode 100644
index 0000000..8573996
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceParseError
@@ -0,0 +1 @@
+this is not a valid class name!
diff --git a/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceWrongType b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceWrongType
new file mode 100644
index 0000000..e8caaf3
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/libcore.java.util.ServiceLoaderTestInterfaceWrongType
@@ -0,0 +1 @@
+java.lang.String
diff --git a/luni/src/test/java/libcore/java/lang/ref/FinalizationTester.java b/support/src/test/java/libcore/java/lang/ref/FinalizationTester.java
similarity index 100%
rename from luni/src/test/java/libcore/java/lang/ref/FinalizationTester.java
rename to support/src/test/java/libcore/java/lang/ref/FinalizationTester.java